digitalmars.D.learn - Struct assignment, possible DMD bug?
- ixid (15/15) Sep 29 2012 This behaviour seems inconsistent and unintuitive:
- Maxim Fomin (7/22) Sep 29 2012 I think this is notorious "i = ++i + ++i".
- Timon Gehr (4/30) Sep 29 2012 No evaluation order of the assignment expression can possibly lead to
- Tommi (4/5) Sep 29 2012 And a pretty serious looking one at that. That bug could make
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (10/31) Sep 29 2012 But that mutation is happening to an object that is also being read
- Timon Gehr (9/41) Sep 29 2012 No. Why?
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (19/69) Sep 29 2012 reads
- ixid (4/93) Sep 29 2012 This would still seem to be a very poor behaviour worth fixing.
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (10/111) Sep 29 2012 I agree. Would you please create a bug report:
- Maxim Fomin (13/21) Sep 29 2012 S(4, s.a, 6) is a struct literal here, not a constructor call
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.
Sep 29 2012
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.
Sep 29 2012
On 09/29/2012 06:26 PM, Maxim Fomin wrote:On Saturday, 29 September 2012 at 16:05:03 UTC, ixid wrote:There is only one mutating sub-expression.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.No evaluation order of the assignment expression can possibly lead to this result. This seems to be a DMD bug.
Sep 29 2012
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.
Sep 29 2012
On 09/29/2012 11:16 AM, Timon Gehr wrote:On 09/29/2012 06:26 PM, Maxim Fomin wrote: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.There is only one mutating sub-expression.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".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. AliStatement 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.
Sep 29 2012
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
Sep 29 2012
On 09/29/2012 04:02 PM, Timon Gehr wrote:On 09/30/2012 12:51 AM, Ali Çehreli wrote:bug.On 09/29/2012 11:16 AM, Timon Gehr wrote:On 09/29/2012 06:26 PM, Maxim Fomin wrote: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 compilerThere is only one mutating sub-expression.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".readsStatement s = S(4, s.a, 6) writes to s object and simultaneouslyI am confused. Of course single mutation and many reads should be fine.Technically there is no language definition to violate.dependit. http://dlang.org/expression.html states that assign expression is evaluated in implementation defined-manner and it is an error toThe compiler seems to be applying an optimization, which it is entitled to as long as the language definition is not violated.on things like this.No evaluation order of the assignment expression can possibly lead to this result. This seems to be a DMD bug.If we are using the same object both to read and write in the same expression, then we should expect the consequences.No. Why?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));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 expressionSilly me! :p Ali
Sep 29 2012
On Sunday, 30 September 2012 at 00:24:34 UTC, Ali Çehreli wrote:On 09/29/2012 04:02 PM, Timon Gehr wrote:This would still seem to be a very poor behaviour worth fixing. What is the compiler generating instead of the constructor example you gave?On 09/30/2012 12:51 AM, Ali Çehreli wrote:the reverseOn 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 whiledoes what I'dis true of the array assignment. Using DMD 2.0.60. GDCbeing readBut that mutation is happening to an object that is alsoThere is only one mutating sub-expression.expect and gives both as 4,1,6.I think this is notorious "i = ++i + ++i".compiler bug.inside the same expression. This is one of the definitions of undefined behavior, not asimultaneously readsStatement s = S(4, s.a, 6) writes to s object andexpression isit. http://dlang.org/expression.html states that assignerror toevaluated in implementation defined-manner and it is anpossibly lead todependon things like this.No evaluation order of the assignment expression canis entitledthis result. This seems to be a DMD bug.The compiler seems to be applying an optimization, which itthe sameto 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 inI am confused. Of course single mutation and many reads should be fine.expression, then we should expect the consequences.No. Why?C++ here.Disclaimer: I assume that D's rules are the same as C andmistaken,C and C++ do not have struct literals and if I am notconstructor 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 relevantpart of thestandard? int c = 0; c = c+1; // c is both read and written to in the sameexpression Silly me! :p Ali
Sep 29 2012
On 09/29/2012 08:13 PM, ixid wrote:On Sunday, 30 September 2012 at 00:24:34 UTC, Ali Çehreli wrote:I agree. Would you please create a bug report: http://d.puremagic.com/issues/On 09/29/2012 04:02 PM, Timon Gehr wrote:This would still seem to be a very poor behaviour worth fixing.On 09/30/2012 12:51 AM, Ali Çehreli wrote:the reverseOn 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 whiledoes what I'dis true of the array assignment. Using DMD 2.0.60. GDCbeing readBut that mutation is happening to an object that is alsoThere is only one mutating sub-expression.expect and gives both as 4,1,6.I think this is notorious "i = ++i + ++i".compiler bug.inside the same expression. This is one of the definitions of undefined behavior, not asimultaneously readsStatement s = S(4, s.a, 6) writes to s object andexpression isit. http://dlang.org/expression.html states that assignerror toevaluated in implementation defined-manner and it is anpossibly lead todependon things like this.No evaluation order of the assignment expression canis entitledthis result. This seems to be a DMD bug.The compiler seems to be applying an optimization, which itthe sameto 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 inI am confused. Of course single mutation and many reads should be fine.expression, then we should expect the consequences.No. Why?C++ here.Disclaimer: I assume that D's rules are the same as C andmistaken,C and C++ do not have struct literals and if I am notconstructor 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 relevantpart of thestandard? int c = 0; c = c+1; // c is both read and written to in the sameexpression Silly me! :p AliWhat 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
Sep 29 2012
On Saturday, 29 September 2012 at 23:02:08 UTC, Timon Gehr wrote:On 09/30/2012 12:51 AM, Ali Çehreli wrote: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.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?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.
Sep 29 2012