My collegues and I were fighting a rather weird bug in an app we're developing. Eventually we got it fixed, but we are still unsure if what the compiler was doing is legit or not.
Assuming we have code like this:
class B {
public:
virtual int foo(int d) { return d - 10; }
};
class C : public B {
public:
virtual int foo(int d) { return d - 11; }
};
class A {
public:
A() : count(0) { member = new B;}
int bar() {
return member->foo(renew());
}
int renew() {
count++;
delete member;
member = new C;
return count;
}
private:
B *member;
int count;
};
int square() {
A a;
cout << a.bar() << endl;
return 0;
}
The Visual Studio x86 compiler, for function A::bar, generates something like this when compiled with /O1 (You can check the full code on godbolt):
push esi
push edi
mov edi, ecx
mov eax, DWORD PTR [edi] ; eax = member
mov esi, DWORD PTR [eax] ; esi = B::vtbl
call int A::renew(void) ; Changes the member, vtable and esi are no longer valid
mov ecx, DWORD PTR [edi]
push eax
call DWORD PTR [esi] ; Calls wrong stuff (B::vtbl[0])
pop edi
pop esi
ret 0
Is this optimization allowed by the standard or is it an undefined behaviour? I was unable to get similar assembly with GCC or clang.