Skip to content

improve definition of assign expression behaviour #2881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
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
12 changes: 12 additions & 0 deletions spec/expression.dd
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ $(GNAME AssignExpression):
occurs. The resulting expression is a modifiable lvalue.
)

$(P Operands of an assign expression are evaluated before the assign expression.
The expression)
--------------
a op= b
--------------

is exactly the same as:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thewilsonator I suppose this would be a more accurate wording:

Suggested change
is exactly the same as:
is rewritten to:

Which would address the question of side effects.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it though? I thought it would be rewritten to auto [ref] __tmp = a; __tmp = __tmp op b;, the number of evaluation is important.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I thought that was clear. Yeah, I guess it makes sense to specify this more accurately.

Copy link
Member

@ibuclaw ibuclaw Nov 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One exception to that rewrite though is array concatenation.

a ~= b
// a = a ~ b;

Because evaluating a changes its underlying size, so it would instead be conceptually

auto __tmp = b
extend(&a)[oldlen .. newlen] = __tmp;

Unless I'm failing to see a disconnect between the evaluation of a and extending its size for the concatenation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does evaluating a change its size ? Isn't the size change part of the execution of ~=, not the evaluation of operands ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because in a op= b, operand a can only be evaluated once. Separating the size change from the evaluation means you'll have to compute it twice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I translated that test for eval order to the ~= operator:


auto mul11ret3(T)(ref T s)
{
    s ~= 11;
    return [3];
}

void main()
{
	static auto test3(int[] val) { (val ~= 7) ~= mul11ret3(val); return val; }
	assert(test3([2]) == [2,7,11,3]);
}

DMD fails with [2,11,7]
gdc 10.2 fails with core.exception.OutOfMemoryError.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I translated that test for eval order to the ~= operator:
gdc 10.2 fails with core.exception.OutOfMemoryError.

Excellent, that means garbage is passed as the destination slot in append(&dst, mul11ret3(val)) instead of the address to val.


--------------
a = a op b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a has side-effects?

Copy link
Member

@PetarKirov PetarKirov Nov 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that the point? Whether or not a has side effects, it would be evaluated once:

exp1 op= exp2;

=>

{
    auto tmp1 = exp1;
    auto tmp2 = exp2;
    tmp1 = tmp1 op tmp2;
}

That is, unless exp1 resolves to a type with custom opAssign, or opOpAssign.

Copy link
Member

@ibuclaw ibuclaw Nov 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a has side-effects?

Then in most cases, a can't be an lvalue in an op= expression, unless the result of a is a stable reference (iirc, *a op= b)

--------------

$(UNDEFINED_BEHAVIOR
If either operand is a reference type and one of the following:
$(OL
Expand Down