November 11 [Issue 24854] New: An @disabled opAssign is generated when it doesn't need to be @disabled | ||||
---|---|---|---|---|
| ||||
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@puremagic.com Reporter: issues.dlang@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. -- |
Copyright © 1999-2021 by the D Language Foundation