digitalmars.D - Order of evaluation of a += a++;
- deadalnix (13/13) Mar 30 2015 My understanding is that, as per spec, a += k is rewriten as :
- Temtaime (7/7) Mar 30 2015 So in SDC there's no difference between a += a and a += a++ ?
- deadalnix (4/5) Mar 30 2015 This is certainly not. SDC act as per spec. DMD act as per DMD.
- Andrei Alexandrescu (28/33) Mar 30 2015 As an aside, we can change the reference to do what we think is right.
- deadalnix (3/39) Mar 30 2015 Why are you doing the replacement top/down rather than bottom up ?
- Andrei Alexandrescu (2/3) Mar 30 2015 What would be the alternative? -- Andrei
- deadalnix (13/17) Mar 30 2015 Doing the replacement bottom up :
- Andrei Alexandrescu (13/28) Mar 30 2015 You need another lowering here because you evaluate a twice. Consider:
- deadalnix (11/48) Mar 30 2015 Yeah, I had an int in mind. Make it
- Andrei Alexandrescu (18/62) Mar 30 2015 So you propose the following lowering for e1 += e2:
- Orvid King (7/81) Mar 31 2015 I might be a bit confused here, but from what you're saying:
- Andrei Alexandrescu (16/21) Mar 31 2015 No. Lowering of e1 += e2++ is:
- deadalnix (3/26) Apr 04 2015 Fixed in SDC.
- Andrei Alexandrescu (2/37) Apr 04 2015 Awesome, thanks! -- Andrei
- w0rp (1/1) Mar 30 2015 I'll say +1 for making everything LTR, as you say.
My understanding is that, as per spec, a += k is rewriten as : a = cast(typeof(a)) (a + k); Which make our example looks like : a = cast(typeof(a)) (a + a++); a = cast(typeof(a)) (a + { auto olda = a; a++; return olda; }()); The whole damn thing should end up with newa == 2 * olda . This is what SDC does. DMD tells me newa == 2 * olda + 1, as it evaluate a++ before that the value of a for the sum. I though we decided to do everything LTR, but discussing with Andrei, it seems that Walter oppose this in some special cases. I think we should keep it consistent and do LTR all the way down. Relevant bug report: https://issues.dlang.org/show_bug.cgi?id=14364
Mar 30 2015
So in SDC there's no difference between a += a and a += a++ ? Damn SDC! I think DMD does it right. This is commented behavior. And nobody will change that: as there are many cries in bugzilla for example for phobos fixes « no, we cannot change it, it will break existing code ! », so like this nobody will change evaluation order.
Mar 30 2015
On Monday, 30 March 2015 at 20:31:10 UTC, Temtaime wrote:I think DMD does it right. This is commented behavior.This is certainly not. SDC act as per spec. DMD act as per DMD. But maybe random evaluation order with incorrect documentation is what we want.
Mar 30 2015
On 3/30/15 4:12 PM, deadalnix wrote:On Monday, 30 March 2015 at 20:31:10 UTC, Temtaime wrote:As an aside, we can change the reference to do what we think is right. The principled way is to go by two rules: 1. Use left-to-right wherever there's a choice 2. Express complex operations in terms of lowerings. So now we have to figure e1 += e2. By rule (2) we should go with this lowering: e1 += e2 -\> (ref a, b) { a = cast(typeof(a)) (a + b); }(e1, e2) By rule (1) we should evaluate e1 before e2 when passing into the lambda. To apply that to i += i++, recall this lowering: i++ -\> (ref x) { auto y = x; ++x; return y; }(i) which takes as to this lowering: i = i++ -\> (ref a, b) { a = cast(typeof(a)) (a + b); }( // lambda1 i, (ref x) { auto y = x; ++x; return y; }(i) // lambda2 ) So first we evaluate the address of i (trivial). Then we evaluate lambda2, which increments i before the += lambda1. So assuming i starts at 5, by the time lambda1 is called i is 6 and b has value 5. Then lambda1 executes, bringing i to 11. It seems that dmd is correct and we should fix the spec and sdc. AndreiI think DMD does it right. This is commented behavior.This is certainly not. SDC act as per spec. DMD act as per DMD. But maybe random evaluation order with incorrect documentation is what we want.
Mar 30 2015
On Tuesday, 31 March 2015 at 00:38:25 UTC, Andrei Alexandrescu wrote:On 3/30/15 4:12 PM, deadalnix wrote:Why are you doing the replacement top/down rather than bottom up ?On Monday, 30 March 2015 at 20:31:10 UTC, Temtaime wrote:As an aside, we can change the reference to do what we think is right. The principled way is to go by two rules: 1. Use left-to-right wherever there's a choice 2. Express complex operations in terms of lowerings. So now we have to figure e1 += e2. By rule (2) we should go with this lowering: e1 += e2 -\> (ref a, b) { a = cast(typeof(a)) (a + b); }(e1, e2) By rule (1) we should evaluate e1 before e2 when passing into the lambda. To apply that to i += i++, recall this lowering: i++ -\> (ref x) { auto y = x; ++x; return y; }(i) which takes as to this lowering: i = i++ -\> (ref a, b) { a = cast(typeof(a)) (a + b); }( // lambda1 i, (ref x) { auto y = x; ++x; return y; }(i) // lambda2 ) So first we evaluate the address of i (trivial). Then we evaluate lambda2, which increments i before the += lambda1. So assuming i starts at 5, by the time lambda1 is called i is 6 and b has value 5. Then lambda1 executes, bringing i to 11. It seems that dmd is correct and we should fix the spec and sdc. AndreiI think DMD does it right. This is commented behavior.This is certainly not. SDC act as per spec. DMD act as per DMD. But maybe random evaluation order with incorrect documentation is what we want.
Mar 30 2015
On 3/30/15 5:49 PM, deadalnix wrote:Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- Andrei
Mar 30 2015
On Tuesday, 31 March 2015 at 01:01:24 UTC, Andrei Alexandrescu wrote:On 3/30/15 5:49 PM, deadalnix wrote:Doing the replacement bottom up : a = a++; a += { auto olda = a; a = a + 1; return olda; }(); a = cast(typeof(a)) (a + { auto olda = a; a = a + 1; return olda; }()); Other code transformation do not require a function call to take place, so why would this require one (which force eager evaluation of parameters) ? That is also inconsistent with LTR evaluation. On the other hand, that would make behavior consistent with what opOpAssign would give, so why not.Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- Andrei
Mar 30 2015
On 3/30/15 8:49 PM, deadalnix wrote:On Tuesday, 31 March 2015 at 01:01:24 UTC, Andrei Alexandrescu wrote:You need another lowering here because you evaluate a twice. Consider: import std.stdio; int a; ref int fun() { writeln("meh"); return a; } void main() { fun() = i++; }On 3/30/15 5:49 PM, deadalnix wrote:Doing the replacement bottom up : a = a++; a += { auto olda = a; a = a + 1; return olda; }(); a = cast(typeof(a)) (a + { auto olda = a; a = a + 1; return olda; }());Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- AndreiOther code transformation do not require a function call to take place, so why would this require one (which force eager evaluation of parameters) ?Everything needs to be evaluated (and only once).That is also inconsistent with LTR evaluation.I don't see how.On the other hand, that would make behavior consistent with what opOpAssign would give, so why not.Whatever makes all sleep at night. Could you please submit a PR for the spec with the lowerings discussed? Andrei
Mar 30 2015
On Tuesday, 31 March 2015 at 06:20:22 UTC, Andrei Alexandrescu wrote:On 3/30/15 8:49 PM, deadalnix wrote:Yeah, I had an int in mind. Make it { aPtr = &a; *aPtr = cast(typeof(a)) (*aPtr + { auto olda = *aPtr; *aPtr = a + 1; return olda; }()); return *aPtr; }()On Tuesday, 31 March 2015 at 01:01:24 UTC, Andrei Alexandrescu wrote:You need another lowering here because you evaluate a twice. Consider: import std.stdio; int a; ref int fun() { writeln("meh"); return a; } void main() { fun() = i++; }On 3/30/15 5:49 PM, deadalnix wrote:Doing the replacement bottom up : a = a++; a += { auto olda = a; a = a + 1; return olda; }(); a = cast(typeof(a)) (a + { auto olda = a; a = a + 1; return olda; }());Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- Andreia appear on the left of a++. a should be evaluated before a++. You propose that it isn't.Other code transformation do not require a function call to take place, so why would this require one (which force eager evaluation of parameters) ?Everything needs to be evaluated (and only once).That is also inconsistent with LTR evaluation.I don't see how.On the other hand, that would make behavior consistent with what opOpAssign would give, so why not.Whatever makes all sleep at night. Could you please submit a PR for the spec with the lowerings discussed?
Mar 30 2015
On 3/30/15 11:34 PM, deadalnix wrote:On Tuesday, 31 March 2015 at 06:20:22 UTC, Andrei Alexandrescu wrote:So you propose the following lowering for e1 += e2: e1 += e2 -\> { auto __p = &(e1); *__p = cast(typeof(e1)) (*__p + e2); }() This looks contrived and it's no surprise is inconsistent with opOpAssign as you yourself noted. I prefer the lowering that identifies += to a function calls with two parameters: e1 += e2 -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }(e1, e2)On 3/30/15 8:49 PM, deadalnix wrote:Yeah, I had an int in mind. Make it { aPtr = &a; *aPtr = cast(typeof(a)) (*aPtr + { auto olda = *aPtr; *aPtr = a + 1; return olda; }()); return *aPtr; }()On Tuesday, 31 March 2015 at 01:01:24 UTC, Andrei Alexandrescu wrote:You need another lowering here because you evaluate a twice. Consider: import std.stdio; int a; ref int fun() { writeln("meh"); return a; } void main() { fun() = i++; }On 3/30/15 5:49 PM, deadalnix wrote:Doing the replacement bottom up : a = a++; a += { auto olda = a; a = a + 1; return olda; }(); a = cast(typeof(a)) (a + { auto olda = a; a = a + 1; return olda; }());Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- AndreiI see, thanks. Guess we should change the spec no matter what. My money is on the lowering I mentioned. Andreia appear on the left of a++. a should be evaluated before a++. You propose that it isn't.Other code transformation do not require a function call to take place, so why would this require one (which force eager evaluation of parameters) ?Everything needs to be evaluated (and only once).That is also inconsistent with LTR evaluation.I don't see how.
Mar 30 2015
On Tuesday, 31 March 2015 at 06:50:16 UTC, Andrei Alexandrescu wrote:On 3/30/15 11:34 PM, deadalnix wrote:I might be a bit confused here, but from what you're saying: b += a++; and b += ++a; Are doing the same thing in DMD?On Tuesday, 31 March 2015 at 06:20:22 UTC, Andrei Alexandrescu wrote:So you propose the following lowering for e1 += e2: e1 += e2 -\> { auto __p = &(e1); *__p = cast(typeof(e1)) (*__p + e2); }() This looks contrived and it's no surprise is inconsistent with opOpAssign as you yourself noted. I prefer the lowering that identifies += to a function calls with two parameters: e1 += e2 -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }(e1, e2)On 3/30/15 8:49 PM, deadalnix wrote:Yeah, I had an int in mind. Make it { aPtr = &a; *aPtr = cast(typeof(a)) (*aPtr + { auto olda = *aPtr; *aPtr = a + 1; return olda; }()); return *aPtr; }()On Tuesday, 31 March 2015 at 01:01:24 UTC, Andrei Alexandrescu wrote:You need another lowering here because you evaluate a twice. Consider: import std.stdio; int a; ref int fun() { writeln("meh"); return a; } void main() { fun() = i++; }On 3/30/15 5:49 PM, deadalnix wrote:Doing the replacement bottom up : a = a++; a += { auto olda = a; a = a + 1; return olda; }(); a = cast(typeof(a)) (a + { auto olda = a; a = a + 1; return olda; }());Why are you doing the replacement top/down rather than bottom up ?What would be the alternative? -- AndreiI see, thanks. Guess we should change the spec no matter what. My money is on the lowering I mentioned. Andreia appear on the left of a++. a should be evaluated before a++. You propose that it isn't.Other code transformation do not require a function call to take place, so why would this require one (which force eager evaluation of parameters) ?Everything needs to be evaluated (and only once).That is also inconsistent with LTR evaluation.I don't see how.
Mar 31 2015
On 3/31/15 12:18 AM, Orvid King wrote:I might be a bit confused here, but from what you're saying: b += a++; and b += ++a; Are doing the same thing in DMD?No. Lowering of e1 += e2++ is: e1 += e2++ -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { auto x = a; ++a; return x; }(e2) ) whereas lowering of e1 += ++e2 is: e1 += ++e2 -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { ++a; return a; }(e2) ) Andrei
Mar 31 2015
On Tuesday, 31 March 2015 at 08:17:31 UTC, Andrei Alexandrescu wrote:On 3/31/15 12:18 AM, Orvid King wrote:Fixed in SDC.I might be a bit confused here, but from what you're saying: b += a++; and b += ++a; Are doing the same thing in DMD?No. Lowering of e1 += e2++ is: e1 += e2++ -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { auto x = a; ++a; return x; }(e2) ) whereas lowering of e1 += ++e2 is: e1 += ++e2 -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { ++a; return a; }(e2) ) Andrei
Apr 04 2015
On 4/4/15 7:54 PM, deadalnix wrote:On Tuesday, 31 March 2015 at 08:17:31 UTC, Andrei Alexandrescu wrote:Awesome, thanks! -- AndreiOn 3/31/15 12:18 AM, Orvid King wrote:Fixed in SDC.I might be a bit confused here, but from what you're saying: b += a++; and b += ++a; Are doing the same thing in DMD?No. Lowering of e1 += e2++ is: e1 += e2++ -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { auto x = a; ++a; return x; }(e2) ) whereas lowering of e1 += ++e2 is: e1 += ++e2 -\> (ref a, b) { a = (cast(typeof(a)) (a + b); }( e1, (ref a) { ++a; return a; }(e2) ) Andrei
Apr 04 2015