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

Overlapping field initialization in CTFE-generated union literal #20675

Open
ibuclaw opened this issue Jan 9, 2025 · 2 comments
Open

Overlapping field initialization in CTFE-generated union literal #20675

ibuclaw opened this issue Jan 9, 2025 · 2 comments
Labels
Feature:CTFE Compile Time Function Evaluation

Comments

@ibuclaw
Copy link
Member

ibuclaw commented Jan 9, 2025

Reduced from dlang/phobos#9064

union U
{
    struct S
    {
        long l;
    }

    int i;
    S s;
}

enum ctfeInit = {
    U u;
    u.s.l = 1;
    return u;
}();

void main()
{
    static a = ctfeInit;
    auto b = ctfeInit;
    assert(a is b);
}

Assert fails because what CTFE generates is:

enum U ctfeInit = U(0, S(1L));

And dmd backend codegen for static initializers ignores overlapping fields, resulting in the value becoming U(0), while the runtime initializer sets each field individually as b.i = 0; b.s.l = 1;.


Workaround is to declare variable as being void initialized.

enum ctfeInit = {
    U u = void;
    u.s.l = 1;
    return u;
}();

This results in the correct value being generated.

enum U ctfeInit = U(, S(1L));
ibuclaw added a commit to ibuclaw/phobos that referenced this issue Jan 10, 2025
ibuclaw added a commit to ibuclaw/phobos that referenced this issue Jan 10, 2025
thewilsonator pushed a commit to dlang/phobos that referenced this issue Jan 10, 2025
@thewilsonator thewilsonator added the Feature:CTFE Compile Time Function Evaluation label Jan 11, 2025
@kinke
Copy link
Contributor

kinke commented Mar 17, 2025

u.s.l = 1; - I think that's the problem, setting the u.s union field implicitly by setting its member (u.s.l).

In

// If it's a union, set all other members of this union to void
stompOverlappedFields(sle, v);
we make sure the direct overlapping fields are stomped. So if u.s was a union, setting u.s.l would stomp all existing initializers for other u.s members. But this code doesn't take into account that there might be overlaps with parent fields (u.i here).

In v2.111.0-beta.1, we end up with overlapping initializers for std.json.JSONValue.emptyObject: JSONValue(Store(null, , , , Object(false, null, ), ), JSONType.object) (wrong: the first null initializer for JSONValue.Store.str, overlapping with the JSONValue.Store.object field). ldc-developers/ldc#4374 (comment)

@kinke
Copy link
Contributor

kinke commented Mar 17, 2025

Another testcase from ldc-developers/ldc#4374, again by setting a union member indirectly/partially by setting a member of that member:

union U {
    int x;
    int[1] arr;
}

U make() {
    U u;
    u.arr[0] = 123;
    return u;
}

void main()
{
    enum u = make(); // overlapping initializers: `U(0, [123])`
    auto r = u;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:CTFE Compile Time Function Evaluation
Projects
None yet
Development

No branches or pull requests

3 participants