Evaluation order of index expressions
Jonathan M Davis via Digitalmars-d
digitalmars-d at puremagic.com
Mon May 25 08:35:00 PDT 2015
On Monday, 25 May 2015 at 12:38:29 UTC, kinke wrote:
> On Monday, 25 May 2015 at 08:00:15 UTC, Jonathan M Davis wrote:
>> It might be completely well-defined and consistent, but it may
>> not be what you expect, and even if it is, a slight change to
>> the code could change the order.
>
> If the behavior isn't what I expect (and I don't think that's
> often case for left-to-right order), then the language should
> force me to express the intention differently. If it's not
> well-defined, I may not be aware of such issues until I use a
> different compiler.
It seems like you still don't understand. Yes, defining the order
of evaluation within an expression as being left-to-right makes
it easier to deal with what is directly inside the expression,
and the compiler could be make to do that (and from the sounds of
it likely will). It could also be made to not be fixed about the
order of evaluation but give an error when it detects that the
order of evaluation matters, so that expressions like
foo(++i, ++i);
give an error. But even so, the compiler _cannot_ be made to
catch all such problems for you, because all it takes is starting
to bury the problem within other function calls within the
expression, and while the order of evaluation in such cases may
very well be defined, whether it's going to do what you expect is
a completely different matter. For instance, what if you had
int bar()
{
return ++i;
}
foo(bar(), bar());
Now, because ++i is inside a call to another function, the
compiler can no longer see that the arguments that you're using
depend on one another. The results _are_ well-defined, but
whether it's what you expect is another matter. With one extra
layer like this, you'll probably see it, and it'll be doing what
you want, but what if you have an expression like
auto f = foo(bar() + baz(bop(), beep() + boop()));
and 4 levels down into the call stack bar() and beep() both
mutate a static or global variable - or some other shared
resource. Then the result of this expression ends up depending on
an order of evaluation issue that you can't see without really
digging into the code, and the compiler sure isn't going to see
for you. You might see that swapping the arguments around in the
expression results in a different result when you think that it
shouldn't, but just as likely, you wouldn't catch that, and a
small change to the code later could change the results
unexpectedly, which you might or might not notice.
Now, that sort of thing is all the more reason to avoid using
static or global variables, and it's the sort of thing that I
would hope good code would avoid. But defining the order of
evaluation as left-to-right, doesn't make those problems go away.
At best, it makes them consistent, and that may be worth it, but
it's not a silver bullet. And it takes optimization opportunities
away from the compiler, since in many cases, it can reorder how
the expression is evaluated to make better use of the registers
and whatnot. So, forcing the order of evaluation is not without
its cons, even if it did solve all order of evaluation issues,
and it really doesn't - especially when that often depends on
what you expect.
Ketmar had a screwy example with arrays a while back that he was
complaining bitterly about not working due to order of evaluation
issues, but IIRC he had recursive function calls which affected
each other and was having all kinds of problems just because he
insisted on doing multiple things in an expression rather than
splitting them out. And the results he was getting were
completely consistent; they just weren't what he wanted. The
order of evaluation mattered too much in the expressions that he
was writing.
Ultimately, if you want to reduce the risk of bugs, you really
should be writing expressions where the order of evaluation
doesn't matter, or where it only matters based on operator
precedence directly within the expression so that it really
doesn't matter what other code is doing. And forcing
left-to-right evaluation doesn't change that. All it really does
is make what what's happening consistent. It doesn't mean that
relying on it is a good idea or that it's going to fix anything
but the some of the most basic order of evaluation issues.
Personally, I don't think that it's worth the cost in lost
optimizations, but even if it is, my point here is really that at
best it only fixes part of the problem.
- Jonathan M Davis
More information about the Digitalmars-d
mailing list