::delete behaves differently in clang and gcc when class has a virtual destructor

I'm reluctant to allege a bug in clang, since so far my experience has been that when clang's behavior differs from gcc, clang is being more standards-compliant than gcc.

However, in this particular case, I haven't been able to find any documentation that indicates that Standard C++ is supposed to do what clang seems to be doing. So, I'd like to ask whether there's a possibility that this is a clang bug, or whether it's just a very obscure corner of the standard that I haven't been able to find any mention of.

First of all, here are the assumptions I'm starting with:

a) the "delete" expression calls the object's destructor, followed by calling "operator delete" on the object

b) "::delete" is the same as "delete", except that instead of calling "operator delete", it calls "::operator delete"

If these assumptions are correct, then in the program below, I would expect case 1 to call Foo's operator delete, but I would not expect case 2 or case 3 to call Foo's operator delete. (Indeed, based on my assumptions above, I would expect case 2 and case 3 to be identical; my belief is that case 3 is just a manual de-sugaring of case 2.)

When compiled with gcc, the program below behaves as I would expect. However, when compiled with clang, the program as written (i. e. with the virtual destructor) does not behave as I expect, in that Foo's operator delete is called in case 2. (Although still not in case 3.)

The presence of the virtual destructor is important. If I simply remove the word "virtual" from the declaration of ~Foo(), then the program behaves identically in clang and gcc, and both are what I expect. (i. e. case 1 is the only case that calls Foo's operator delete.)

Thanks for any light you can shed on this behavior, and my apologies if this is indeed expected behavior that I am unaware of.

--Patrick

ppelletier@pumpkin:~/misc$ uname -a
Linux pumpkin 2.6.32-21-generic #32-Ubuntu SMP Fri Apr 16 08:09:38 UTC 2010 x86_64 GNU/Linux
ppelletier@pumpkin:~/misc$ cat delete-test.C
#include <iostream>

static const char *msg;

class Foo
{ const int n;
public:
  Foo (int n_in);
  virtual ~Foo ();
  void operator delete (void *);
};

Foo::Foo (int n_in) : n (n_in)
{ }

Foo::~Foo ()
{ std::cerr << "destructor for " << n << std::endl; }

void Foo::operator delete (void *)
{ std::cerr << msg << std::endl; }

int main (int argc, char **argv)
{ msg = "[1] I would expect this to be printed (and it is)";
  Foo *f1 = new Foo (1);
  delete f1;
  msg = "[2] I would not expect this to be printed (gcc doesn't, clang does)";
  Foo *f2 = new Foo (2);
  ::delete f2;
  msg = "[3] This should not be printed either (and it isn't)";
  Foo *f3 = new Foo (3);
  f3 -> ~Foo ();
  ::operator delete (f3);
  return 0;
}
ppelletier@pumpkin:~/misc$ /home/ppelletier/src/asan/asan_clang_Linux/bin/clang++ --version
clang version 3.0 (trunk 133511)
Target: x86_64-unknown-linux-gnu
Thread model: posix
ppelletier@pumpkin:~/misc$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ppelletier@pumpkin:~/misc$ /home/ppelletier/src/asan/asan_clang_Linux/bin/clang++ -O3 -Wall -o delete-test-clang delete-test.C
ppelletier@pumpkin:~/misc$ g++ -O3 -Wall -o delete-test-gcc delete-test.C
ppelletier@pumpkin:~/misc$ ./delete-test-clang
destructor for 1
[1] I would expect this to be printed (and it is)
destructor for 2
[2] I would not expect this to be printed (gcc doesn't, clang does)
destructor for 3
ppelletier@pumpkin:~/misc$ ./delete-test-gcc
destructor for 1
[1] I would expect this to be printed (and it is)
destructor for 2
destructor for 3
ppelletier@pumpkin:~/misc$

First of all, here are the assumptions I'm starting with:

a) the "delete" expression calls the object's destructor, followed by
calling "operator delete" on the object

b) "::delete" is the same as "delete", except that instead of calling
"operator delete", it calls "::operator delete"

If these assumptions are correct, then in the program below, I would
expect case 1 to call Foo's operator delete, but I would not expect case
2 or case 3 to call Foo's operator delete. (Indeed, based on my
assumptions above, I would expect case 2 and case 3 to be identical; my
belief is that case 3 is just a manual de-sugaring of case 2.)

Exception handling is different in case 2 than 3, and null pointers are treated differently, but for the simple case, that's correct.

When compiled with gcc, the program below behaves as I would expect.
However, when compiled with clang, the program as written (i. e. with
the virtual destructor) does not behave as I expect, in that Foo's
operator delete is called in case 2. (Although still not in case 3.)

The presence of the virtual destructor is important. If I simply remove
the word "virtual" from the declaration of ~Foo(), then the program
behaves identically in clang and gcc, and both are what I expect. (i.
e. case 1 is the only case that calls Foo's operator delete.)

Thanks for any light you can shed on this behavior, and my apologies if
this is indeed expected behavior that I am unaware of.

No, I believe you found a genuine bug. Please file it.

Sebastian