Re: g++ problem with order of evaluation of arguments of delete.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 4 May 2023, Georg-Johann Lay 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?

I think this is a GCC problem. It normally uses a SAVE_EXPR, but seems to consider token->lexer_ as "safe".

        try
          {
            Lexer::~Lexer ((struct Lexer *) token->lexer_);
          }
        finally
          {
            operator delete ((void *) token->lexer_, 8);
          }

whereas if I write delete (token->lexer_ + i); where i is 0, I get

        try
          {
Lexer::~Lexer (SAVE_EXPR <(struct Lexer *) token->lexer_ + (sizetype) (i * 8)>);
          }
        finally
          {
operator delete ((void *) (SAVE_EXPR <(struct Lexer *) token->lexer_ + (sizetype) (i * 8)>), 8);
          }

which works.

--
Marc Glisse



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux