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

An @disabled opAssign is generated when it doesn't need to be @disabled #5575

Open
dlangBugzillaToGithub opened this issue Nov 11, 2024 · 0 comments
Labels
enhancement New feature or request P1

Comments

@dlangBugzillaToGithub
Copy link
Owner

Jonathan M Davis reported this on 2024-11-11T04:19:59Z

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

Description

This is kind of an ugly corner case, so it could be argued that it should be left as-is, but we could be generating an opAssign that works when we aren't. So, I don't know if this is really a bug or just an enhancement request. It could also be argued that once @disable gets involved, you really need to implement stuff manually, since it does get kind of weird otherwise. But I'm creating this issue so that the current situation is at least documented.

In any case, this code

```
void main()
{
    import std.traits;

    static struct Member
    {
        @disable void opAssign(Member) {}
        void opAssign(ref Member) {}
    }

    static struct S { Member member; }

    Member m;
    Member m2;
    m = m2;
    m = Member.init;

    S s;
    S s2;
    s = s2;
    s = S.init;
}
```

results in

```
q.d(16): Error: function `q.main.Member.opAssign` cannot be used because it is annotated with `@disable`
q.d(20): Error: generated function `q.main.S.opAssign` cannot be used because it is annotated with `@disable`
q.d(21): Error: generated function `q.main.S.opAssign` cannot be used because it is annotated with `@disable`
```

This code

```
void main()
{
    import std.traits;

    static struct Member
    {
        @disable void opAssign(Member) {}
        void opAssign(ref Member) {}
    }

    mixin listOpAssign!Member;
    pragma(msg, "");

    static struct S { Member member; }
    mixin listOpAssign!S;
}

template listOpAssign(T)
{
    static if(__traits(hasMember, T, "opAssign"))
    {
        pragma(msg, "Overloads of opAssign for " ~ T.stringof);
        pragma(msg, "---");

        static foreach(sym; __traits(getOverloads, T, "opAssign"))
        {
            pragma(msg, typeof(sym).stringof ~ ": " ~
                        (__traits(isDisabled, sym) ? "disabled" : "NOT disabled"));
        }
    }
    else
        pragma(msg, T.stringof ~ " has no opAssign");
}
```

prints

```
Overloads of opAssign for Member
---
void(Member __param_0): disabled
void(ref Member __param_0): NOT disabled

Overloads of opAssign for S
---
ref S(S p): disabled
```

So, Member is as expected. The rvalue overload is @disabled (and thus results in a compilation error when used), and the lvalue overload works just fine.

On the other hand, with S, only a single overload is created, and it's @disabled in spite of the fact that it doesn't need to be. The obvious implementation would be

```
ref opAssign(S p)
{
    this.member = p.member;
}

ref opAssign(ref S p)
{
    this.member = p.member;
}
```

And because Member has a working opAssign that takes an lvalue, this could work.

Now, the reverse situation can't work, I don't think (at least not without doing some gymnastics with extra copies of S's member field) - that is when Member has an @disabled lvalue overload and a working rvalue overload. That currently results in exactly the same @disabled signature for opAssign on Member. However, it becomes problematic to implement opAssign in S even though there is a working one in Member, because while you might pass an rvalue to S's opAssign, S's opAssign will then naturally need to use an lvalue to assign to its member field, and that overload of Member's opAssign is @disabled. It _could_ be worked around by creating a copy that's an rvalue, but that seems like it's going too far for a compiler-generated function, and anyone who really wants that could do it themselves.

So, I don't know if we want to fix this situation or not, but it did surprise me, since I expected that since Member had a working opAssign, S would get one as well, and it doesn't.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request P1
Projects
None yet
Development

No branches or pull requests

1 participant