Skip to content
Merged
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
1 change: 1 addition & 0 deletions compiler/src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -2363,6 +2363,7 @@ extern (C++) final class CallExp : UnaExp
bool inDebugStatement; /// true if this was in a debug statement
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
bool isUfcsRewrite; /// the first argument was pushed in here by a UFCS rewrite
bool fromOpAssignment; // set when operator overload method call from assignment (2024 edition)
VarDeclaration vthis2; // container for multi-context
Expression loweredFrom; // set if this is the result of a lowering

Expand Down
42 changes: 17 additions & 25 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -5301,6 +5301,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
setError();
}

// `e` calls opAssign, opOpAssign, opUnary!++, opUnary!-- when lowered from an assignment
private void setOpOverloadAssign(Expression e)
{
result = e;
auto ce = e.isCallExp();
// rvalue error in discardValue from 2024 edition
if (!ce || !sc.hasEdition(Edition.v2024))
return;
ce.fromOpAssignment = true;
}

/**************************
* Semantically analyze Expression.
* Determine types, fold constants, etc.
Expand Down Expand Up @@ -9171,10 +9182,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{

if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop))
{
result = e;
return;
}
return setOpOverloadAssign(e);

Expression e;
if (exp.e1.op == EXP.arrayLength)
Expand Down Expand Up @@ -11811,10 +11819,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor

// printf("PreExp::semantic('%s')\n", toChars());
if (Expression e = exp.opOverloadUnary(sc))
{
result = e;
return;
}
return setOpOverloadAssign(e);

// Rewrite as e1+=1 or e1-=1
Expression e;
Expand Down Expand Up @@ -12554,10 +12559,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
else if (exp.op == EXP.assign)
{
if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
{
result = e;
return;
}
return setOpOverloadAssign(e);
}
else
assert(exp.op == EXP.blit);
Expand All @@ -12574,10 +12576,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (exp.op == EXP.assign && !exp.e2.implicitConvTo(exp.e1.type))
{
if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop))
{
result = e;
return;
}
return setOpOverloadAssign(e);
}
if (exp.e2.checkValue())
return setError();
Expand Down Expand Up @@ -13242,10 +13241,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(PowAssignExp exp)
{
if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop))
{
result = e;
return;
}
return setOpOverloadAssign(e);

if (exp.suggestOpOpAssign(sc, parent) ||
exp.e1.checkReadModifyWrite(exp.op, exp.e2))
Expand Down Expand Up @@ -13319,13 +13315,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor

override void visit(CatAssignExp exp)
{

//printf("CatAssignExp::semantic() %s\n", exp.toChars());
if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop))
{
result = e;
return;
}
return setOpOverloadAssign(e);

if (exp.suggestOpOpAssign(sc, parent))
return setError();
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -2594,6 +2594,7 @@ class CallExp final : public UnaExp
bool inDebugStatement;
bool ignoreAttributes;
bool isUfcsRewrite;
bool fromOpAssignment;
VarDeclaration* vthis2;
Expression* loweredFrom;
static CallExp* create(Loc loc, Expression* e, Array<Expression* >* exps);
Expand Down
17 changes: 17 additions & 0 deletions compiler/src/dmd/sideeffect.d
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,23 @@ bool discardValue(Expression e)
error(e.loc, "discarded assignment to indexed array literal");
}
}
// check assignment to struct rvalue
auto ce = e.isCallExp();
if (ce && ce.fromOpAssignment)
{
if (auto dve = ce.e1.isDotVarExp())
{
auto lhs = dve.e1;
auto ts = lhs.type.isTypeStruct();
if (ts && !lhs.isLvalue() && !ts.sym.hasPointerField) // Don't disallow writing to data through a pointer field
{
error(lhs.loc, "assignment to struct rvalue `%s` is discarded",
lhs.toChars());
errorSupplemental(e.loc, "if the assignment is needed to modify a global, call `%s` directly or use an lvalue",
dve.var.toChars());
}
}
}
return false;
}
switch (e.op)
Expand Down
62 changes: 62 additions & 0 deletions compiler/test/fail_compilation/struct_rvalue_assign.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
TEST_OUTPUT:
---
fail_compilation/struct_rvalue_assign.d(16): Error: assignment to struct rvalue `foo()` is discarded
fail_compilation/struct_rvalue_assign.d(16): if the assignment is needed to modify a global, call `opAssign` directly or use an lvalue
fail_compilation/struct_rvalue_assign.d(17): Error: assignment to struct rvalue `foo()` is discarded
fail_compilation/struct_rvalue_assign.d(17): if the assignment is needed to modify a global, call `opOpAssign` directly or use an lvalue
fail_compilation/struct_rvalue_assign.d(18): Error: assignment to struct rvalue `foo()` is discarded
fail_compilation/struct_rvalue_assign.d(18): if the assignment is needed to modify a global, call `opUnary` directly or use an lvalue
---
*/
module sra 2024;

void main()
{
foo() = S.init;
foo() += 5;
++foo();
*foo(); // other unary ops may be OK

// allowed
foo().opAssign(S.init);
foo().opOpAssign!"+"(5);
foo().opUnary!"++"();
}

S foo() => S.init;

struct S
{
int i;

void opAssign(S s);
void opOpAssign(string op : "+")(int);
void opUnary(string op : "++")();
void opUnary(string op : "*")();
}

void test()
{
int i;

static struct Ptr
{
int* p;
void opAssign(int rhs) { *p = rhs; }
}
Ptr(&i) = 1; // allowed

struct Nested
{
void opAssign(int rhs) { i = rhs; }
}
Nested() = 1; // allowed

static struct StaticOp
{
static si = 0;
static void opAssign(int rhs) { si = rhs; }
}
StaticOp() = 1; // allowed
}
Loading