@@ -681,8 +681,19 @@ bool isLvalue(Expression _this)
681681 if (tf && tf.isRef)
682682 {
683683 if (auto dve = _this.e1.isDotVarExp())
684+ {
684685 if (dve.var.isCtorDeclaration())
685- return false;
686+ {
687+ // Allow taking the address of explicit constructor calls,
688+ // but not (__stmp = S(), __stmp).__ctor().
689+ auto ve = lastComma(dve.e1).isVarExp();
690+
691+ if (ve && (ve.var.storage_class & STC.temp) != 0)
692+ return false;
693+
694+ return isLvalue(dve.e1);
695+ }
696+ }
686697 return true; // function returns a reference
687698 }
688699 return false;
@@ -761,6 +772,10 @@ bool isLvalue(Expression _this)
761772 * Determine if copy elision is allowed when copying an expression to
762773 * a typed storage. This basically elides a restricted subset of so-called
763774 * "pure" rvalues, i.e. expressions with no reference semantics.
775+ *
776+ * Note: Please try to keep `dmd.glue.e2ir.toElemRVO()` in sync with this.
777+ * It is not destructive to fail to elide a copy, but it is always better
778+ * to stay consistent.
764779 */
765780bool canElideCopy(Expression e, Type to, bool checkMod = true)
766781{
@@ -770,8 +785,19 @@ bool canElideCopy(Expression e, Type to, bool checkMod = true)
770785 static bool visitCallExp(CallExp e)
771786 {
772787 if (auto dve = e.e1.isDotVarExp())
788+ {
773789 if (dve.var.isCtorDeclaration())
774- return true;
790+ {
791+ // Allow (__stmp = S(), __stmp).__ctor() to be elided,
792+ // but force a copy for (s2 = s1.__ctor()).
793+ auto ve = lastComma(dve.e1).isVarExp();
794+
795+ if (ve && (ve.var.storage_class & STC.temp) != 0)
796+ return true;
797+
798+ return canElideCopy(dve.e1, e.type, false);
799+ }
800+ }
775801
776802 auto tb = e.e1.type.toBasetype();
777803 if (tb.ty == Tdelegate || tb.ty == Tpointer)
@@ -12303,7 +12329,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
1230312329 ? cast(DotVarExp)ce.e1 : null;
1230412330 if (sd.ctor && ce && dve && dve.var.isCtorDeclaration() &&
1230512331 // https://issues.dlang.org/show_bug.cgi?id=19389
12306- dve.e1.op != EXP.dotVariable &&
12332+ canElideCopy(ce, t1, false) &&
1230712333 e2y.type.implicitConvTo(t1))
1230812334 {
1230912335 /* Look for form of constructor call which is:
@@ -12414,7 +12440,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
1241412440 return;
1241512441 }
1241612442 }
12417- else if (sd.hasMoveCtor && (! e2x.isCallExp() || e2x.rvalue) && !e2x.isStructLiteralExp( ))
12443+ else if (sd.hasMoveCtor && (e2x.rvalue || !canElideCopy( e2x, t1, false) ))
1241812444 {
1241912445 // #move
1242012446 /* The !e2x.isCallExp() is because it is already an rvalue
0 commit comments