Jump to page: 1 26  
Page
Thread overview
Evaluation order of index expressions
May 24, 2015
kinke
May 24, 2015
Jonathan M Davis
May 24, 2015
kinke
May 24, 2015
Timon Gehr
May 24, 2015
Jonathan M Davis
May 24, 2015
Timon Gehr
May 24, 2015
Jonathan M Davis
May 24, 2015
Timon Gehr
May 24, 2015
Iain Buclaw
May 24, 2015
Iain Buclaw
May 24, 2015
Timon Gehr
May 25, 2015
Iain Buclaw
May 25, 2015
Johannes Pfau
May 25, 2015
Daniel Murphy
May 25, 2015
Iain Buclaw
May 26, 2015
Timon Gehr
May 26, 2015
deadalnix
May 26, 2015
Timon Gehr
May 26, 2015
Timon Gehr
May 26, 2015
deadalnix
May 26, 2015
Timon Gehr
May 27, 2015
deadalnix
May 26, 2015
John Colvin
May 26, 2015
deadalnix
May 25, 2015
Timon Gehr
May 25, 2015
kinke
May 25, 2015
Iain Buclaw
May 25, 2015
Timon Gehr
May 25, 2015
kinke
May 25, 2015
Timon Gehr
May 25, 2015
Iain Buclaw
May 25, 2015
Timon Gehr
May 24, 2015
Timon Gehr
May 25, 2015
ketmar
May 25, 2015
Jonathan M Davis
May 25, 2015
kinke
May 25, 2015
Jonathan M Davis
May 26, 2015
Timon Gehr
May 26, 2015
Artur Skawina
May 26, 2015
Timon Gehr
May 26, 2015
Artur Skawina
May 26, 2015
ketmar
May 25, 2015
Timon Gehr
May 25, 2015
Daniel Murphy
May 25, 2015
Timon Gehr
May 25, 2015
Daniel Murphy
May 25, 2015
Timon Gehr
May 24, 2015
<code>
import core.stdc.stdio;

static int[] _array = [ 0, 1, 2, 3 ];

int[] array() @property { printf("array()\n"); return _array; }
int   start() @property { printf("start()\n"); return 0; }
int   end()   @property { printf("end()\n");   return 1; }

void main()
{
    array[start..end] = 666;
    printf("---\n");
    array[start] = end;
}
</code>

<stdout>
array()
start()
end()
---
start()
array()
end()
</stdout>

So for the 2nd assignment's left-hand-side, the index is evaluated before evaluating the container! Please don't tell me that's by design. :>

[origin: https://github.com/D-Programming-Language/phobos/pull/3311]
May 24, 2015
On Sunday, 24 May 2015 at 19:30:54 UTC, kinke wrote:
> <code>
> import core.stdc.stdio;
>
> static int[] _array = [ 0, 1, 2, 3 ];
>
> int[] array() @property { printf("array()\n"); return _array; }
> int   start() @property { printf("start()\n"); return 0; }
> int   end()   @property { printf("end()\n");   return 1; }
>
> void main()
> {
>     array[start..end] = 666;
>     printf("---\n");
>     array[start] = end;
> }
> </code>
>
> <stdout>
> array()
> start()
> end()
> ---
> start()
> array()
> end()
> </stdout>
>
> So for the 2nd assignment's left-hand-side, the index is evaluated before evaluating the container! Please don't tell me that's by design. :>
>
> [origin: https://github.com/D-Programming-Language/phobos/pull/3311]

Why would you expect the order to even be defined? There's no operator precedence involved, so the compiler is free to order the evaluations however it likes.

And code like was originally in the PR is just plain error-prone. It's like doing

foo(++i, ++i);

only worse, because the fact that array is a property and count is used inside it is not immediately obvious when looking at

array[count++] = 666;

The original code is clearly wrong. And forcing the order of evaluation so that it's one way or the other just changes under which cases you end up with bugs. Mutating in an expression while using it multiple times in that expression or mutating a variable in an expression while using a variable that depends on it is just plain error-prone and is a serious code smell.

I really don't see anything wrong with what the compiler is doing in this case. The problem is that the code was making bad assumptions.

- Jonathan M Davis
May 24, 2015
On Sunday, 24 May 2015 at 19:48:05 UTC, Jonathan M Davis wrote:
> The original code is clearly wrong. And forcing the order of evaluation so that it's one way or the other just changes under which cases you end up with bugs. Mutating in an expression while using it multiple times in that expression or mutating a variable in an expression while using a variable that depends on it is just plain error-prone and is a serious code smell.
>
> I really don't see anything wrong with what the compiler is doing in this case. The problem is that the code was making bad assumptions.

We agree on the original code smell.

I think the evaluation order should be well-defined by the language though, following the intuitive left-to-right order for cases like this. Left-hand-side before right-hand-side in assign statements, container before its index/index range, for the latter start before end etc.
Then at least all compilers of that language exhibit the same behavior and we don't end up with cases like this, where LDC complains and DMD compiles. Even worse would be not-so-obvious side effects caused by differing evaluation orders of different compilers, with a fair potential for nasty bugs.
May 24, 2015
On 05/24/2015 09:48 PM, Jonathan M Davis wrote:
> On Sunday, 24 May 2015 at 19:30:54 UTC, kinke wrote:
>> <code>
>> import core.stdc.stdio;
>>
>> static int[] _array = [ 0, 1, 2, 3 ];
>>
>> int[] array() @property { printf("array()\n"); return _array; }
>> int   start() @property { printf("start()\n"); return 0; }
>> int   end()   @property { printf("end()\n");   return 1; }
>>
>> void main()
>> {
>>     array[start..end] = 666;
>>     printf("---\n");
>>     array[start] = end;
>> }
>> </code>
>>
>> <stdout>
>> array()
>> start()
>> end()
>> ---
>> start()
>> array()
>> end()
>> </stdout>
>>
>> So for the 2nd assignment's left-hand-side, the index is evaluated
>> before evaluating the container! Please don't tell me that's by
>> design. :>
>>
>> [origin: https://github.com/D-Programming-Language/phobos/pull/3311]
>
> Why would you expect the order to even be defined?

Because this is not C.

BTW, the documentation contradicts itself on evaluation order: http://dlang.org/expression.html
May 24, 2015
On Sunday, 24 May 2015 at 20:30:00 UTC, Timon Gehr wrote:
> On 05/24/2015 09:48 PM, Jonathan M Davis wrote:
>> On Sunday, 24 May 2015 at 19:30:54 UTC, kinke wrote:
>>> <code>
>>> import core.stdc.stdio;
>>>
>>> static int[] _array = [ 0, 1, 2, 3 ];
>>>
>>> int[] array() @property { printf("array()\n"); return _array; }
>>> int   start() @property { printf("start()\n"); return 0; }
>>> int   end()   @property { printf("end()\n");   return 1; }
>>>
>>> void main()
>>> {
>>>    array[start..end] = 666;
>>>    printf("---\n");
>>>    array[start] = end;
>>> }
>>> </code>
>>>
>>> <stdout>
>>> array()
>>> start()
>>> end()
>>> ---
>>> start()
>>> array()
>>> end()
>>> </stdout>
>>>
>>> So for the 2nd assignment's left-hand-side, the index is evaluated
>>> before evaluating the container! Please don't tell me that's by
>>> design. :>
>>>
>>> [origin: https://github.com/D-Programming-Language/phobos/pull/3311]
>>
>> Why would you expect the order to even be defined?
>
> Because this is not C.
>
> BTW, the documentation contradicts itself on evaluation order: http://dlang.org/expression.html

There have been discussions on defining the order of evaluation from left-to-right such that it may happen, but there have been issues raised with it as well (particularly from an optimization standpoint, though IIRC, it causes some havoc for the gdc folks as well given how the gcc backend works).

Regardless, having an expression where you're mutating a variable and using either it or something that depends on it within the same expression is just plain bug-prone, and even if the compiler wants to makes all such cases a compilation error, it's trivial to move some of the changes into a function call and hide it such that the compiler can't catch it. So, the reality of the matter is that even if we get more restrictive about the order of evaluation in expressions, we can't actually prevent the programmer from shooting themselves in the foot due to issues with the order of evaluation. At most, we can reduce the problem, but that just pushes it from common, relatively easy to catch cases, to more complex ones, so on some level, it's simply providing a false sense of security.

So, I don't know if it's better to define the order of evaluation as being left-to-right or not, but it is _not_ a silver bullet.

- Jonathan M Davis
May 24, 2015
On 05/24/2015 10:35 PM, Jonathan M Davis wrote:
> On Sunday, 24 May 2015 at 20:30:00 UTC, Timon Gehr wrote:
>> On 05/24/2015 09:48 PM, Jonathan M Davis wrote:
>>> On Sunday, 24 May 2015 at 19:30:54 UTC, kinke wrote:
>>>> <code>
>>>> import core.stdc.stdio;
>>>>
>>>> static int[] _array = [ 0, 1, 2, 3 ];
>>>>
>>>> int[] array() @property { printf("array()\n"); return _array; }
>>>> int   start() @property { printf("start()\n"); return 0; }
>>>> int   end()   @property { printf("end()\n");   return 1; }
>>>>
>>>> void main()
>>>> {
>>>>    array[start..end] = 666;
>>>>    printf("---\n");
>>>>    array[start] = end;
>>>> }
>>>> </code>
>>>>
>>>> <stdout>
>>>> array()
>>>> start()
>>>> end()
>>>> ---
>>>> start()
>>>> array()
>>>> end()
>>>> </stdout>
>>>>
>>>> So for the 2nd assignment's left-hand-side, the index is evaluated
>>>> before evaluating the container! Please don't tell me that's by
>>>> design. :>
>>>>
>>>> [origin: https://github.com/D-Programming-Language/phobos/pull/3311]
>>>
>>> Why would you expect the order to even be defined?
>>
>> Because this is not C.
>>
>> BTW, the documentation contradicts itself on evaluation order:
>> http://dlang.org/expression.html
>
> There have been discussions on defining the order of evaluation from
> left-to-right such that it may happen, but there have been issues raised
> with it as well (particularly from an optimization standpoint, though
> IIRC, it causes some havoc for the gdc folks as well given how the gcc
> backend works).
> ...

Optimizations can reorder operations when the compiler is able to prove equivalence, and there actually are annotations to help. Yes, occasionally, reorderings that the compiler won't be able to do on its own might lead to non-trivial performance improvements. In such cases, do them manually. The gcc backend obviously supports ordered operations, because some operations are ordered today.

> Regardless, having an expression where you're mutating a variable and
> using either it or something that depends on it within the same
> expression is just plain bug-prone,

So what? If such a bug occurs, you want to be the one who sees it, not the guy who tries to use your code with a different compiler.

> and even if the compiler wants to
> makes all such cases a compilation error, it's trivial to move some of
> the changes into a function call and hide it such that the compiler
> can't catch it.

'pure','const',... Even then, the compiler should not want to make all such cases a compilation error; it is not necessary if evaluation order is defined.

> So, the reality of the matter is that even if we get
> more restrictive about the order of evaluation in expressions, we can't
> actually prevent the programmer from shooting themselves in the foot due
> to issues with the order of evaluation.

We can at least go as far as to not artificially add additional foot-shooting failure modes to the weapon.

> At most, we can reduce the
> problem, but that just pushes it from common, relatively easy to catch
> cases, to more complex ones, so on some level, it's simply providing a
> false sense of security.
>...

No. Seriously. Under the current semantics, running an exhaustive input-output test on a fully @safe program will not ensure that the code is actually correct. Talk about providing a false sense of security.

May 24, 2015
On Sunday, 24 May 2015 at 21:18:54 UTC, Timon Gehr wrote:
> The gcc backend obviously supports ordered operations, because some operations are ordered today.

Iain has talked in the past about how they're forced to work around the backend to force the order of operations for those cases, and it's definitely ugly.

> No. Seriously. Under the current semantics, running an exhaustive input-output test on a fully @safe program will not ensure that the code is actually correct. Talk about providing a false sense of security.

@safe definitely has issues. We went about it the wrong way by effectively implementing it via a blacklist instead of a whitelist. And it needs to be fixed. But as far as the code actually being correct goes, @safe isn't guaranteed to prove that anyway. All it's supposed to guarantee is that you can't corrupt memory. There's really no way to have the compiler guarantee that a program is correct.

- Jonathan M Davis
May 24, 2015
On 05/24/2015 11:26 PM, Jonathan M Davis wrote:
> On Sunday, 24 May 2015 at 21:18:54 UTC, Timon Gehr wrote:
>> The gcc backend obviously supports ordered operations, because some
>> operations are ordered today.
>
> Iain has talked in the past about how they're forced to work around the
> backend to force the order of operations for those cases, and it's
> definitely ugly.
> ...

Given that it is/should be already there for OrExpression, XorExpression, AndExpression, CmpExpression, ShiftExpression, AddExpression, CatExpression, MulExpression, PowExpression, OrOrExpression, AndAndExpression, it would seem that doing the few remaining cases left-to-right shouldn't be that much of an obstacle, no?

>> No. Seriously. Under the current semantics, running an exhaustive
>> input-output test on a fully @safe program will not ensure that the
>> code is actually correct. Talk about providing a false sense of security.
>
> @safe definitely has issues. We went about it the wrong way by
> effectively implementing it via a blacklist instead of a whitelist. And
> it needs to be fixed. But as far as the code actually being correct
> goes, @safe isn't guaranteed to prove that anyway. All it's supposed to
> guarantee is that you can't corrupt memory.

You missed the more relevant exhaustive input-output test part. @safe was there simply to ensure there is no UB.
May 24, 2015
On 5/24/15 1:29 PM, Timon Gehr wrote:
> BTW, the documentation contradicts itself on evaluation order:
> http://dlang.org/expression.html

This comes up once in a while. We should stick with left to right through and through. It's a "simple" matter of getting somebody on the compiler team to find the time for it. -- Andrei

May 24, 2015
On 24 May 2015 23:30, "Jonathan M Davis via Digitalmars-d" < digitalmars-d@puremagic.com> wrote:
>
> On Sunday, 24 May 2015 at 21:18:54 UTC, Timon Gehr wrote:
>>
>> The gcc backend obviously supports ordered operations, because some
operations are ordered today.
>
>
> Iain has talked in the past about how they're forced to work around the
backend to force the order of operations for those cases, and it's definitely ugly.
>

There was a point in time when I started out that I took 'gdc does X, dmd does Y' as serious bugs that need fixing.  I was so naïve back then. ;-)


« First   ‹ Prev
1 2 3 4 5 6