Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -11642,6 +11642,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// printf("PreExp::semantic('%s')\n", toChars());
if (Expression e = exp.opOverloadUnary(sc))
{
if (checkRvalueAssign(sc, exp.e1, Id.opUnary))
{
e = ErrorExp.get();
}
result = e;
return;
}
Expand Down
30 changes: 29 additions & 1 deletion compiler/src/dmd/opover.d
Original file line number Diff line number Diff line change
Expand Up @@ -537,11 +537,35 @@ Expression opOverloadAssign(AssignExp e, Scope* sc, Type[2] aliasThisStop)

bool choseReverse;
if (auto result = pickBestBinaryOverload(sc, null, s, null, e, choseReverse))
{
if (checkRvalueAssign(sc, e.e1, Id.opAssign))
{
return ErrorExp.get();
}
return result;

}
return binAliasThis(e, sc, aliasThisStop);
}

bool checkRvalueAssign(Scope *sc, Expression e, Identifier op)
{
if (!sc.intypeof && sc.hasEdition(Edition.v2024) &&
e.type && e.type.ty == Tstruct && !e.isLvalue())
{
TypeStruct ts = cast(TypeStruct)e.type;
// nested struct may assign data outside of the struct, e.g. ae.utils.array.list(args)
if (!ts.sym.isNested())
{
const char* action = op == Id.opAssign ? "assign to" : "modify";
error(e.loc, "cannot %s struct rvalue `%s`", action, e.toChars());
errorSupplemental(e.loc, "if the assignment is used for side-effects, call `%s` directly",
op.toChars());
return true;
}
}
return false;
}

Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop)
{
if (Expression err = binSemanticProp(e, sc))
Expand Down Expand Up @@ -1004,6 +1028,10 @@ Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisSt
if (e.e1.type.isTypeError() || e.e2.type.isTypeError())
return ErrorExp.get();

if (checkRvalueAssign(sc, e.e1, Id.opOpAssign))
{
return ErrorExp.get();
}
AggregateDeclaration ad1 = isAggregate(e.e1.type);
Dsymbol s = search_function(ad1, Id.opOpAssign);
if (s && !(s.isTemplateDeclaration() || s.isOverloadSet()))
Expand Down
8 changes: 4 additions & 4 deletions compiler/test/fail_compilation/fail9936.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ TEST_OUTPUT:
---
fail_compilation/fail9936.d(25): Error: `S().opBinary` isn't a template
fail_compilation/fail9936.d(26): Error: `S().opBinaryRight` isn't a template
fail_compilation/fail9936.d(27): Error: `S().opOpAssign` isn't a template
fail_compilation/fail9936.d(27): Error: `s.opOpAssign` isn't a template
fail_compilation/fail9936.d(29): Error: `S().opIndexUnary` isn't a template
fail_compilation/fail9936.d(30): Error: `S().opUnary` isn't a template
---
Expand All @@ -17,14 +17,14 @@ struct S
auto opIndexUnary(S s) { return 1; }
auto opUnary(S s) { return 1; }
}
void main()
void f(S s)
{
static assert(!is(typeof( S() + S() )));
static assert(!is(typeof( 100 + S() )));
static assert(!is(typeof( S() += S() )));
static assert(!is(typeof( s += S() )));
S() + S();
100 + S();
S() += S();
s += S();

+S()[0];
+S();
Expand Down
31 changes: 31 additions & 0 deletions compiler/test/fail_compilation/struct_rvalue_assign.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
TEST_OUTPUT:
---
fail_compilation/struct_rvalue_assign.d(16): Error: cannot assign to struct rvalue `foo()`
fail_compilation/struct_rvalue_assign.d(16): if the assignment is used for side-effects, call `opAssign` directly
fail_compilation/struct_rvalue_assign.d(17): Error: cannot modify struct rvalue `foo()`
fail_compilation/struct_rvalue_assign.d(17): if the assignment is used for side-effects, call `opOpAssign` directly
fail_compilation/struct_rvalue_assign.d(18): Error: cannot modify struct rvalue `foo()`
fail_compilation/struct_rvalue_assign.d(18): if the assignment is used for side-effects, call `opUnary` directly
---
*/
module sra 2024;

void main()
{
foo() = S.init;
foo() += 5;
++foo();
cast(void) ~foo(); // other unary ops are OK
}

S foo() => S.init;

struct S
{
int i;

void opAssign(S s) {}
void opOpAssign(string op : "+")(int) {}
void opUnary(string op)() {}
}
Loading