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);