Skip to content

Commit f5866ad

Browse files
committed
fix dlang#21478: Copy assignment not called when struct implements rule of five
If a struct implemented a copy constructor, then an rvalue opAssign would be given a better match score than the lvalue opAssign regardless of the ref-ness of the argument. ``` struct S { this(ref return scope S); ref S opAssign(const ref S); ref S opAssign(const S); } s1 = s2; // lowered as: S.opAssign(&s1, *S.this(s2)); ``` This change optimizes the assignment as: ``` s1 = s2; // lowered as: S.opAssign(&s1, &s2); ```
1 parent 17184b4 commit f5866ad

3 files changed

Lines changed: 62 additions & 4 deletions

File tree

compiler/src/dmd/typesem.d

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,11 +1070,10 @@ private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction
10701070
}
10711071

10721072
// check if the copy constructor may be called to copy the argument
1073-
if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor)
1073+
if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor &&
1074+
!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
10741075
{
1075-
if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage))
1076-
return MATCH.nomatch;
1077-
m = MATCH.exact;
1076+
return MATCH.nomatch;
10781077
}
10791078
else
10801079
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// https://github.com/dlang/dmd/issues/21478
2+
3+
// Test struct that implements "rule of five"
4+
5+
struct S21478
6+
{
7+
// 1. destructor
8+
~this() { }
9+
// 2. copy constructor
10+
this(ref return scope S21478) { assert(0); }
11+
// 3. copy assign
12+
void opAssign(const ref S21478) { }
13+
// 4. move constructor
14+
this(return scope S21478) { assert(0); }
15+
// 5. move assign
16+
void opAssign(const S21478) { assert(0); }
17+
}
18+
19+
void main()
20+
{
21+
S21478 sa, sb;
22+
sb = sa; // Should call 3, not 2 + 5.
23+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// https://github.com/dlang/dmd/issues/21478
2+
3+
// Test struct that implements copy constructor follows rvalue expression spec:
4+
// If both ref and non-ref parameter overloads are present,
5+
// an rvalue is preferably matched to the non-ref parameters,
6+
// and an lvalue is preferably matched to the ref parameter.
7+
// An RvalueExpression will preferably match with the non-ref parameter.
8+
9+
struct S21478
10+
{
11+
this(ref return scope S21478) { assert(0); }
12+
}
13+
14+
struct P21478
15+
{
16+
int plain_old_data;
17+
}
18+
19+
int overload(const S21478) { return 1; }
20+
int overload(const ref S21478) { return 2; }
21+
22+
int overload(const P21478) { return 1; }
23+
int overload(const ref P21478) { return 2; }
24+
25+
void main()
26+
{
27+
S21478 s;
28+
assert(overload(s) == 2);
29+
assert(overload(S21478()) == 1);
30+
assert(overload(__rvalue(s)) == 1);
31+
32+
P21478 p;
33+
assert(overload(p) == 2);
34+
assert(overload(P21478()) == 1);
35+
assert(overload(__rvalue(p)) == 1);
36+
}

0 commit comments

Comments
 (0)