[Issue 24854] New: An @disabled opAssign is generated when it doesn't need to be @disabled
d-bugmail at puremagic.com
d-bugmail at puremagic.com
Mon Nov 11 04:19:59 UTC 2024
https://issues.dlang.org/show_bug.cgi?id=24854
Issue ID: 24854
Summary: An @disabled opAssign is generated when it doesn't
need to be @disabled
Product: D
Version: D2
Hardware: All
OS: All
Status: NEW
Severity: enhancement
Priority: P1
Component: dmd
Assignee: nobody at puremagic.com
Reporter: issues.dlang at jmdavisProg.com
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.
--
More information about the Digitalmars-d-bugs
mailing list