View mode: basic / threaded / horizontal-split · Log in · Help
September 29, 2012
Struct assignment, possible DMD bug?
This behaviour seems inconsistent and unintuitive:

void main() {
	int[3] a = [1,2,3];
	a = [4, a[0], 6];

	struct S {
		int a, b, c;
	}

	S s = S(1,2,3);
	s = S(4, s.a, 6);

	assert(a == [4,1,6]);
	assert(s == S(4,4,6));
}

Setting the struct writes s.a before evaluating it while the 
reverse is true of the array assignment. Using DMD 2.0.60. GDC 
does what I'd expect and gives both as 4,1,6.
September 29, 2012
Re: Struct assignment, possible DMD bug?
On Saturday, 29 September 2012 at 16:05:03 UTC, ixid wrote:
> This behaviour seems inconsistent and unintuitive:
>
> void main() {
> 	int[3] a = [1,2,3];
> 	a = [4, a[0], 6];
>
> 	struct S {
> 		int a, b, c;
> 	}
>
> 	S s = S(1,2,3);
> 	s = S(4, s.a, 6);
>
> 	assert(a == [4,1,6]);
> 	assert(s == S(4,4,6));
> }
>
> Setting the struct writes s.a before evaluating it while the 
> reverse is true of the array assignment. Using DMD 2.0.60. GDC 
> does what I'd expect and gives both as 4,1,6.

I think this is notorious "i = ++i + ++i".
Statement s = S(4, s.a, 6) writes to s object and simultaneously 
reads it.
http://dlang.org/expression.html states that assign expression is 
evaluated in implementation defined-manner and it is an error to 
depend on things like this.
September 29, 2012
Re: Struct assignment, possible DMD bug?
On 09/29/2012 06:26 PM, Maxim Fomin wrote:
> On Saturday, 29 September 2012 at 16:05:03 UTC, ixid wrote:
>> This behaviour seems inconsistent and unintuitive:
>>
>> void main() {
>>     int[3] a = [1,2,3];
>>     a = [4, a[0], 6];
>>
>>     struct S {
>>         int a, b, c;
>>     }
>>
>>     S s = S(1,2,3);
>>     s = S(4, s.a, 6);
>>
>>     assert(a == [4,1,6]);
>>     assert(s == S(4,4,6));
>> }
>>
>> Setting the struct writes s.a before evaluating it while the reverse
>> is true of the array assignment. Using DMD 2.0.60. GDC does what I'd
>> expect and gives both as 4,1,6.
>
> I think this is notorious "i = ++i + ++i".

There is only one mutating sub-expression.

> Statement s = S(4, s.a, 6) writes to s object and simultaneously reads it.
> http://dlang.org/expression.html states that assign expression is
> evaluated in implementation defined-manner and it is an error to depend
> on things like this.

No evaluation order of the assignment expression can possibly lead to
this result. This seems to be a DMD bug.
September 29, 2012
Re: Struct assignment, possible DMD bug?
On Saturday, 29 September 2012 at 18:16:24 UTC, Timon Gehr wrote:
> This seems to be a DMD bug.

And a pretty serious looking one at that. That bug could make 
nukes fly to wrong coordinates, and that just ruins everybody's 
day.
September 29, 2012
Re: Struct assignment, possible DMD bug?
On 09/29/2012 11:16 AM, Timon Gehr wrote:
> On 09/29/2012 06:26 PM, Maxim Fomin wrote:

>>> S s = S(1,2,3);
>>> s = S(4, s.a, 6);
>>>
>>> assert(a == [4,1,6]);
>>> assert(s == S(4,4,6));
>>> }
>>>
>>> Setting the struct writes s.a before evaluating it while the reverse
>>> is true of the array assignment. Using DMD 2.0.60. GDC does what I'd
>>> expect and gives both as 4,1,6.
>>
>> I think this is notorious "i = ++i + ++i".
>
> There is only one mutating sub-expression.

But that mutation is happening to an object that is also being read 
inside the same expression.

This is one of the definitions of undefined behavior, not a compiler bug.

>> Statement s = S(4, s.a, 6) writes to s object and simultaneously reads
>> it.
>> http://dlang.org/expression.html states that assign expression is
>> evaluated in implementation defined-manner and it is an error to depend
>> on things like this.
>
> No evaluation order of the assignment expression can possibly lead to
> this result. This seems to be a DMD bug.

The compiler seems to be applying an optimization, which it is entitled 
to as long as the language definition is not violated.

If we are using the same object both to read and write in the same 
expression, then we should expect the consequences.

Disclaimer: I assume that D's rules are the same as C and C++ here.

Ali
September 29, 2012
Re: Struct assignment, possible DMD bug?
On 09/30/2012 12:51 AM, Ali Çehreli wrote:
> On 09/29/2012 11:16 AM, Timon Gehr wrote:
>  > On 09/29/2012 06:26 PM, Maxim Fomin wrote:
>
>  >>> S s = S(1,2,3);
>  >>> s = S(4, s.a, 6);
>  >>>
>  >>> assert(a == [4,1,6]);
>  >>> assert(s == S(4,4,6));
>  >>> }
>  >>>
>  >>> Setting the struct writes s.a before evaluating it while the reverse
>  >>> is true of the array assignment. Using DMD 2.0.60. GDC does what I'd
>  >>> expect and gives both as 4,1,6.
>  >>
>  >> I think this is notorious "i = ++i + ++i".
>  >
>  > There is only one mutating sub-expression.
>
> But that mutation is happening to an object that is also being read
> inside the same expression.
>
> This is one of the definitions of undefined behavior, not a compiler bug.
>
>  >> Statement s = S(4, s.a, 6) writes to s object and simultaneously reads
>  >> it.
>  >> http://dlang.org/expression.html states that assign expression is
>  >> evaluated in implementation defined-manner and it is an error to depend
>  >> on things like this.
>  >
>  > No evaluation order of the assignment expression can possibly lead to
>  > this result. This seems to be a DMD bug.
>
> The compiler seems to be applying an optimization, which it is entitled
> to as long as the language definition is not violated.
>

Technically there is no language definition to violate.

> If we are using the same object both to read and write in the same
> expression, then we should expect the consequences.
>

No. Why?

> Disclaimer: I assume that D's rules are the same as C and C++ here.
>

C and C++ do not have struct literals and if I am not mistaken,
constructor invocation is a sequence point.

Besides, this does not make any sense, what is the relevant part of the 
standard?

int c = 0;
c = c+1; // c is both read and written to in the same expression
September 30, 2012
Re: Struct assignment, possible DMD bug?
On 09/29/2012 04:02 PM, Timon Gehr wrote:
> On 09/30/2012 12:51 AM, Ali Çehreli wrote:
>> On 09/29/2012 11:16 AM, Timon Gehr wrote:
>> > On 09/29/2012 06:26 PM, Maxim Fomin wrote:
>>
>> >>> S s = S(1,2,3);
>> >>> s = S(4, s.a, 6);
>> >>>
>> >>> assert(a == [4,1,6]);
>> >>> assert(s == S(4,4,6));
>> >>> }
>> >>>
>> >>> Setting the struct writes s.a before evaluating it while the reverse
>> >>> is true of the array assignment. Using DMD 2.0.60. GDC does what I'd
>> >>> expect and gives both as 4,1,6.
>> >>
>> >> I think this is notorious "i = ++i + ++i".
>> >
>> > There is only one mutating sub-expression.
>>
>> But that mutation is happening to an object that is also being read
>> inside the same expression.
>>
>> This is one of the definitions of undefined behavior, not a compiler 
bug.
>>
>> >> Statement s = S(4, s.a, 6) writes to s object and simultaneously 
reads
>> >> it.
>> >> http://dlang.org/expression.html states that assign expression is
>> >> evaluated in implementation defined-manner and it is an error to
>> depend
>> >> on things like this.
>> >
>> > No evaluation order of the assignment expression can possibly lead to
>> > this result. This seems to be a DMD bug.
>>
>> The compiler seems to be applying an optimization, which it is entitled
>> to as long as the language definition is not violated.
>>
>
> Technically there is no language definition to violate.
>
>> If we are using the same object both to read and write in the same
>> expression, then we should expect the consequences.
>>
>
> No. Why?

I am confused. Of course single mutation and many reads should be fine.

>
>> Disclaimer: I assume that D's rules are the same as C and C++ here.
>>
>
> C and C++ do not have struct literals and if I am not mistaken,
> constructor invocation is a sequence point.

Yes. And in this case it is the compiler-generated constructor. The OP's 
problem goes away if there is a user-provided constructor:

    struct S {
        int a, b, c;

        this(int a, int b, int c)
        {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }

Now it is as expected:

    assert(s == S(4,1,6));

> Besides, this does not make any sense, what is the relevant part of the
> standard?
>
> int c = 0;
> c = c+1; // c is both read and written to in the same expression

Silly me! :p

Ali
September 30, 2012
Re: Struct assignment, possible DMD bug?
On Sunday, 30 September 2012 at 00:24:34 UTC, Ali Çehreli wrote:
> On 09/29/2012 04:02 PM, Timon Gehr wrote:
> > On 09/30/2012 12:51 AM, Ali Çehreli wrote:
> >> On 09/29/2012 11:16 AM, Timon Gehr wrote:
> >> > On 09/29/2012 06:26 PM, Maxim Fomin wrote:
> >>
> >> >>> S s = S(1,2,3);
> >> >>> s = S(4, s.a, 6);
> >> >>>
> >> >>> assert(a == [4,1,6]);
> >> >>> assert(s == S(4,4,6));
> >> >>> }
> >> >>>
> >> >>> Setting the struct writes s.a before evaluating it while
> the reverse
> >> >>> is true of the array assignment. Using DMD 2.0.60. GDC
> does what I'd
> >> >>> expect and gives both as 4,1,6.
> >> >>
> >> >> I think this is notorious "i = ++i + ++i".
> >> >
> >> > There is only one mutating sub-expression.
> >>
> >> But that mutation is happening to an object that is also
> being read
> >> inside the same expression.
> >>
> >> This is one of the definitions of undefined behavior, not a
> compiler bug.
> >>
> >> >> Statement s = S(4, s.a, 6) writes to s object and
> simultaneously reads
> >> >> it.
> >> >> http://dlang.org/expression.html states that assign
> expression is
> >> >> evaluated in implementation defined-manner and it is an
> error to
> >> depend
> >> >> on things like this.
> >> >
> >> > No evaluation order of the assignment expression can
> possibly lead to
> >> > this result. This seems to be a DMD bug.
> >>
> >> The compiler seems to be applying an optimization, which it
> is entitled
> >> to as long as the language definition is not violated.
> >>
> >
> > Technically there is no language definition to violate.
> >
> >> If we are using the same object both to read and write in
> the same
> >> expression, then we should expect the consequences.
> >>
> >
> > No. Why?
>
> I am confused. Of course single mutation and many reads should 
> be fine.
>
> >
> >> Disclaimer: I assume that D's rules are the same as C and
> C++ here.
> >>
> >
> > C and C++ do not have struct literals and if I am not
> mistaken,
> > constructor invocation is a sequence point.
>
> Yes. And in this case it is the compiler-generated constructor. 
> The OP's problem goes away if there is a user-provided 
> constructor:
>
>     struct S {
>         int a, b, c;
>
>         this(int a, int b, int c)
>         {
>             this.a = a;
>             this.b = b;
>             this.c = c;
>         }
>     }
>
> Now it is as expected:
>
>     assert(s == S(4,1,6));
>
> > Besides, this does not make any sense, what is the relevant
> part of the
> > standard?
> >
> > int c = 0;
> > c = c+1; // c is both read and written to in the same
> expression
>
> Silly me! :p
>
> Ali

This would still seem to be a very poor behaviour worth fixing. 
What is the compiler generating instead of the constructor 
example you gave?
September 30, 2012
Re: Struct assignment, possible DMD bug?
On 09/29/2012 08:13 PM, ixid wrote:
> On Sunday, 30 September 2012 at 00:24:34 UTC, Ali Çehreli wrote:
>> On 09/29/2012 04:02 PM, Timon Gehr wrote:
>> > On 09/30/2012 12:51 AM, Ali Çehreli wrote:
>> >> On 09/29/2012 11:16 AM, Timon Gehr wrote:
>> >> > On 09/29/2012 06:26 PM, Maxim Fomin wrote:
>> >>
>> >> >>> S s = S(1,2,3);
>> >> >>> s = S(4, s.a, 6);
>> >> >>>
>> >> >>> assert(a == [4,1,6]);
>> >> >>> assert(s == S(4,4,6));
>> >> >>> }
>> >> >>>
>> >> >>> Setting the struct writes s.a before evaluating it while
>> the reverse
>> >> >>> is true of the array assignment. Using DMD 2.0.60. GDC
>> does what I'd
>> >> >>> expect and gives both as 4,1,6.
>> >> >>
>> >> >> I think this is notorious "i = ++i + ++i".
>> >> >
>> >> > There is only one mutating sub-expression.
>> >>
>> >> But that mutation is happening to an object that is also
>> being read
>> >> inside the same expression.
>> >>
>> >> This is one of the definitions of undefined behavior, not a
>> compiler bug.
>> >>
>> >> >> Statement s = S(4, s.a, 6) writes to s object and
>> simultaneously reads
>> >> >> it.
>> >> >> http://dlang.org/expression.html states that assign
>> expression is
>> >> >> evaluated in implementation defined-manner and it is an
>> error to
>> >> depend
>> >> >> on things like this.
>> >> >
>> >> > No evaluation order of the assignment expression can
>> possibly lead to
>> >> > this result. This seems to be a DMD bug.
>> >>
>> >> The compiler seems to be applying an optimization, which it
>> is entitled
>> >> to as long as the language definition is not violated.
>> >>
>> >
>> > Technically there is no language definition to violate.
>> >
>> >> If we are using the same object both to read and write in
>> the same
>> >> expression, then we should expect the consequences.
>> >>
>> >
>> > No. Why?
>>
>> I am confused. Of course single mutation and many reads should be fine.
>>
>> >
>> >> Disclaimer: I assume that D's rules are the same as C and
>> C++ here.
>> >>
>> >
>> > C and C++ do not have struct literals and if I am not
>> mistaken,
>> > constructor invocation is a sequence point.
>>
>> Yes. And in this case it is the compiler-generated constructor. The
>> OP's problem goes away if there is a user-provided constructor:
>>
>> struct S {
>> int a, b, c;
>>
>> this(int a, int b, int c)
>> {
>> this.a = a;
>> this.b = b;
>> this.c = c;
>> }
>> }
>>
>> Now it is as expected:
>>
>> assert(s == S(4,1,6));
>>
>> > Besides, this does not make any sense, what is the relevant
>> part of the
>> > standard?
>> >
>> > int c = 0;
>> > c = c+1; // c is both read and written to in the same
>> expression
>>
>> Silly me! :p
>>
>> Ali
>
> This would still seem to be a very poor behaviour worth fixing.

I agree. Would you please create a bug report:

  http://d.puremagic.com/issues/

> What is
> the compiler generating instead of the constructor example you gave?

I haven't read the generated assembly output but I am pretty sure that 
the compiler is generating the three expressions of the user-defined 
constructor "out in the open":

  s.a = 4;
  s.b = s.a;   // oops
  s.c = 6;

Ali
September 30, 2012
Re: Struct assignment, possible DMD bug?
On Saturday, 29 September 2012 at 23:02:08 UTC, Timon Gehr wrote:
> On 09/30/2012 12:51 AM, Ali Çehreli wrote:
>> Disclaimer: I assume that D's rules are the same as C and C++ 
>> here.
>>
>
> C and C++ do not have struct literals and if I am not mistaken,
> constructor invocation is a sequence point.

S(4, s.a, 6) is a struct literal here, not a constructor call 
(because structure S doesn't define any constructors). C has 
compound literals which is close to D struct literals.

> Besides, this does not make any sense, what is the relevant 
> part of the standard?

The standard states for assign expression (in $6.15.6) that "the 
side effect of updating the stored value is sequenced after the 
value computations of the left and the rights operands. The 
evaluations of the operands are unsequenced". This means that a 
compiler can evaluate either first and the second operand, or the 
second and the first. In any case it can store value only after 
evaluations of both operands which means that value 4 cannot be 
assigned to s.a when S(4, s.a, 6) is evaluated. Actually, it is a 
bug.
Top | Discussion index | About this forum | D home