Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRTP + pass alias to virtual member to mixin = runtime crash #17932

Open
dlangBugzillaToGithub opened this issue Dec 19, 2019 · 4 comments
Open

Comments

@dlangBugzillaToGithub
Copy link

Adam D. Ruppe (@adamdruppe) reported this on 2019-12-19T19:18:35Z

Transferred from https://issues.dlang.org/show_bug.cgi?id=20458

CC List

  • basile-z

Description

Encountered this during my android/jni stuff. Easier to describe with code than with words:

----

mixin template Impl(T, string name){
        alias a = __traits(getMember, T, name); // comment this and it works
        static void impl() {
                auto t = new T();
                __traits(getMember, t, name)();
        }
}

class A(CRTP) {
        static foreach(memberName; __traits(derivedMembers, CRTP))
                mixin Impl!(CRTP, memberName);
}

class Foo : A!Foo {
        void test() {
                import core.stdc.stdio;
                printf("Success
");
        }
}

void main() {
        Foo.impl();
}

----


Compiles successfully. Running it leads to crash.

Make `class Foo` into `final class Foo` and it works.

Put `final` on `void test` instead and it works here, but adding more members can result in the wrong method being called. Leads me to believe the virtual function slot gets confused.


Comment that `alias a = ....` line in there and it works. So the fundamental problem is related to the alias being present in the mixin template, even if never used.

So the alias of the virtual derived member appearing in the base class. However, I believe the use of the curiously-recurring template pattern is necessary to trigger the bug though since I've been unable to reproduce the crash without that.


This is the most I have been able to minimize it... the original version is:


---
mixin template Impl(T, alias a) {
        static void impl() {
                auto t = new T();
                __traits(getMember, t, __traits(identifier, a))();
        }
}

class A(CRTP) {
        static foreach(memberName; __traits(derivedMembers, CRTP))
                mixin Impl!(CRTP, __traits(getMember, CRTP, memberName));
}

class Foo : A!Foo {
        void test() {
                import core.stdc.stdio;
                printf("Success
");
        }
}

void main() {
        Foo.impl();
}
---

And I think it is worth noting that one too: just *passing* the alias to the mixin template also results in the same behavior. It doesn't have to explicitly mixin the alias, as long as it is present there.
@dlangBugzillaToGithub
Copy link
Author

b2.temp commented on 2023-03-22T18:30:46Z

With assertions built in DMD your code triggers this : 
https://github.com/dlang/dmd/blob/498822ec8efb8e2b68e257b01fa1e097ec6e3a88/compiler/src/dmd/dsymbolsem.d#L5261, so the vtbl seems to be started already, probably with "test" but then is rebuild again, without

@dlangBugzillaToGithub
Copy link
Author

b2.temp commented on 2023-03-22T23:25:40Z

reduced further

```
mixin template Impl(T){
    alias a =  __traits(getMember, T, "test");
    //alias a = T.test; // no vtbl corruption with this alias
}

class A(T) {
    mixin Impl!T;
}

class Foo : A!Foo {
    void test() {}
}

void main() {
    (new Foo).test();
}    
```

@dlangBugzillaToGithub
Copy link
Author

b2.temp commented on 2023-03-23T00:33:38Z

The same corruption without the getMember trait:

```
mixin template Impl(T){
    alias a = T.test;       
    pragma(msg, typeof(a)); // launch dsymbolsema and corrupt vtbl
}

class A(T) {
    mixin Impl!T;
}

class Foo : A!Foo {
    void test() {}
}

void main() {
    (new Foo).test();
}   
```

@dlangBugzillaToGithub
Copy link
Author

b2.temp commented on 2023-03-23T07:28:49Z

Finally the generic reproduction

```
class Base
{
    alias a = Derived.test;
    pragma(msg, typeof(a)); // triggers the vtbl corruption
}

class Derived : Base
{
    void test(){}
}

void main()
{
    (new Derived).test();
}   
```

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant