Thread overview
Spec: can a class method change the type of the object (vtable) ?
Mar 17
Johan
Mar 18
kinke
Mar 18
Johan
Mar 18
Dukc
March 17

Hi all,
I am looking for a (legal D) counterexample where a class method alters the vtable of the object it is called on. In other words, a counterexample where the following transformation is invalid:

class A {
    void foo();
    ...
}

void g() {
    A a = new A();
    a.foo();
    a.foo();
}
---Transformed into --->
void g() {
    A a = new A();
    a.A.foo(); // devirtualized
    a.A.foo(); // devirtualized because `A.foo(a)` may not change the type of `a`.
}

Although accepted by the compiler currently, I believe this is forbidden by the spec (can't find it, if it is not in the spec it should be added):

    void foo() {
        this = new B(); // illegal D code
    }

Thanks,
Johan

March 18

On Sunday, 17 March 2024 at 12:47:49 UTC, Johan wrote:

>

Although accepted by the compiler currently, I believe this is forbidden by the spec (can't find it, if it is not in the spec it should be added):

    void foo() {
        this = new B(); // illegal D code
    }

With dmd v2.108.0-rc.1 I get:

thislval.d(7): Error: cannot modify expression `this` because it is not an lvalue

this was changed to be an lvalue for a few releases recently, see:
https://issues.dlang.org/show_bug.cgi?id=24157

March 18

On Monday, 18 March 2024 at 12:44:46 UTC, Nick Treleaven wrote:

>

On Sunday, 17 March 2024 at 12:47:49 UTC, Johan wrote:

>

Although accepted by the compiler currently, I believe this is forbidden by the spec (can't find it, if it is not in the spec it should be added):

    void foo() {
        this = new B(); // illegal D code
    }

With dmd v2.108.0-rc.1 I get:

thislval.d(7): Error: cannot modify expression `this` because it is not an lvalue

this was changed to be an lvalue for a few releases recently, see:
https://issues.dlang.org/show_bug.cgi?id=24157

Yeah, IIRC, this was only allowed in v2.105.0 and v2.105.1, then reverted in v2.105.2. But it never changed the vptr, it just made this point to another object after the assignment (not affecting the callER though).

I think the assumption/optimization should be safe. The only exception might be extern(C++) ctors in the future, if we really made those C++-compatible - they set the vptr (base ctors first to their vptr, then derived ctors overriding it later).

March 18

On Sunday, 17 March 2024 at 12:47:49 UTC, Johan wrote:

>

Although accepted by the compiler currently, I believe this is forbidden by the spec (can't find it, if it is not in the spec it should be added):

    void foo() {
        this = new B(); // illegal D code
    }

This wouldn't accomplish anything if it was allowed. Remember, foo is

void foo(A this) { this = new B; }

under the hood. You aren't modifying the existing instance of the class with this, you're only allocating a new instance and then not doing anything neither with the old nor the new instance.

I believe you meant

    void foo() {
        import core.lifetime;
        this.destroy(false);
        emplace(cast(B) cast(void*) this);
    }
March 18

On Monday, 18 March 2024 at 13:50:59 UTC, kinke wrote:

>

The only exception might be extern(C++) ctors in the future, if we really made those C++-compatible - they set the vptr (base ctors first to their vptr, then derived ctors overriding it later).

Any extern(C++) method is not safe, because C++ allows placement new on this (a known issue for devirtualizing in C++). And indeed during construction things are particularly tricky in C++.

-Johan