Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is replacing `this` with a different type allowed?

In the comments and answers to this question: Virtual function compiler optimization c++ it is argued that a virtual function call in a loop cannot be devirtualized, because the virtual function might replace this by another object using placement new, e.g.:

void A::foo() { // virtual 
   static_assert(sizeof(A) == sizeof(Derived)); 
   new(this) Derived; 
}

The example is from a LLVM blog article about devirtualization

Now my question is: is that allowed by the standard?

I could find this on cppreference about storage reuse: (emphasis mine)

A program is not required to call the destructor of an object to end its lifetime if the object is trivially-destructible or if the program does not rely on the side effects of the destructor. However, if a program ends the lifetime of an non-trivial object, it must ensure that a new object of the same type is constructed in-place (e.g. via placement new) before the destructor may be called implicitly

If the new object must have the same type, it must have the same virtual functions. So it is not possible to have a different virtual function, and thus, devirtualization is acceptable.

Or do I misunderstand something?

like image 291
king_nak Avatar asked May 08 '18 12:05

king_nak


People also ask

Is it possible to combine types in TS?

An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need.

What is !: In TypeScript?

Save this answer. Show activity on this post. That is a "definite assignment assertion": varname !: sometype informs typescript not to worry about checking if varname might be unassigned (it tells typescript that varname will definitely be assigned, even if typescript cannot infer where it is assigned).

What is type compatibility?

In C, compatible types are defined as: two types that can be used together without modification (as in an assignment expression) two types that can be substituted one for the other without modification.

Is an intersection type?

Intersection types are composite data types. Similar to product types, they are used to assign several types to an object. However, product types are assigned to tuples, so that each tuple element is assigned a particular product type component.


2 Answers

The quote you provided says:

If a program ends the lifetime of an non-trivial object, it must ensure that a new object of the same type is constructed in-place (e.g. via placement new) before the destructor may be called implicitly

The intent of this statement relates to something a bit different to what you are doing. The statement is meant to say that when you destroy an object without destroying its name, something still refers to that storage with the original type, o you need to construct a new object there so that when the implicit destruction occurs, there is a valid object to destroy. This is relevant for example if you have an automatic ("stack") variable, and you call its destructor--you need to construct a new instance there before the destructor is called when the variable goes out of scope.

The statement as a whole, and its "of the same type" clause in particular, has no bearing on the topic you're discussing, which is whether you are allowed to construct a different polymorphic type having the same storage requirements in place of an old one. I don't know of any reason why you shouldn't be allowed to do that.

Now, that being said, the question you linked to is doing something different: it is calling a function using implicit this in a loop, and the question is whether the compiler could assume that the vptr for this will not change in that loop. I believe the compiler could (and clang -fstrict-vtable-pointers does) assume this, because this is only valid if the type is the same after the placement new.

So while the quotes from the standard you have provided are not relevant to this issue, the end result is that it does seem possible for an optimizer to devirtualize function calls made in a loop under the assumption that the type of *this (or its vptr) cannot change. The type of an object stored at an address (and its vptr) can change, but if it does, the old this is no longer valid.

like image 192
John Zwinck Avatar answered Nov 15 '22 21:11

John Zwinck


It appears that you intend to use the new object using handles (pointers, references, or the original variable name) that existed prior to its recreation. That's allowed only if the instance type is not changed, plus some other conditions excluding const objects and sub-objects:

From [basic.life]:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied,

    and

  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and

  • the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

Your quote from the Standard is merely a consequence of this one.

Your proposed "devirtualization counter-example" does not meet these requirements, therefore all attempts to access the object after it is replaced will cause undefined behavior.

The blog post even pointed this out, in the very next sentence after the example code you looked at.

like image 36
Ben Voigt Avatar answered Nov 15 '22 23:11

Ben Voigt