On Thu, 4 May 2023 at 11:12, Jonathan Wakely <jwakely.gcc@xxxxxxxxx> wrote: > > On Thu, 4 May 2023 at 11:06, Jonathan Wakely <jwakely.gcc@xxxxxxxxx> wrote: > > > > On Thu, 4 May 2023 at 10:46, Georg-Johann Lay <avr@xxxxxxxx> wrote: > > > > > > Given the following C++ code: > > > > > > struct Lexer; > > > > > > struct Token > > > { > > > Lexer* const lexer_; > > > Token (Lexer *l) : lexer_(l) {} > > > ~Token() = default; > > > > > > Token() = delete; > > > Token (const Token&) = delete; > > > Token (Token&&) = delete; > > > void operator= (const Token&) = delete; > > > void operator= (Token&&) = delete; > > > }; > > > > > > struct Lexer > > > { > > > Token *token_; > > > Lexer() = default; > > > ~Lexer() { delete token_; } > > > > > > Lexer (const Lexer&) = delete; > > > Lexer (Lexer&&) = delete; > > > void operator= (const Lexer&) = delete; > > > void operator= (Lexer&&) = delete; > > > }; > > > > > > int main() > > > { > > > Lexer *lexer = new Lexer(); > > > Token *token = new Token (lexer); > > > lexer->token_ = token; > > > delete token->lexer_; > > > // delete lexer; // is OK > > > } > > > > > > When I compile this with g++ v11.3 (same with g++ from master from > > > 2023-04-20) and run > > > > > > $ g++ main-3.cpp -Os -W -Wall -Wextra -dumpbase "" -save-temps -dp && > > > ./a.out > > > > > > Segmentation fault (core dumped) > > > > > > The assembly shows that the generated code does two calls to "delete" > > > but just one call to "new", so it's clear something is going wrong. > > > > > > As far as I understand, the "delete token_" in ~Lexer is a sequence > > > point, so that dereferencing token in "delete->lexer_" must be sequenced > > > before calling ~Token ? > > > > > > Segmentation fault also occurs with -O0, but goes away when removing the > > > "const" in "Lexer* const lexer_;". > > > > > > My question: Is this a GCC problem, or a problem with the code and > > > sequence points? > > > > It's definitely a GCC bug. > > > > The code is compiled to something like: > > > > token->lexer_->~Lexer(); > > operator delete(token->lexer_); > > > > But that means that we evaluate 'token' twice, even though it's been > > invalidated by the destructor. It should be compiled to something more > > like: > > > > auto* p = token->lexer_; > > p->~Lexer(); > > operator delete(p); > > The C++ standard is clear, see [expr.delete] p4: > > "The cast-expression in a delete-expression shall be evaluated exactly once." > > That wording has been present since C++98. > > Please file a bug. The bug was already present in gcc 4.1.0, I didn't check anything older than r0-71179-gc6ff1944941b0c aka r105000