February 13, 2012
On 02/13/12 20:47, Jonathan M Davis wrote:
> On Monday, February 13, 2012 19:47:03 Artur Skawina wrote:
>> On 02/13/12 18:57, Jonathan M Davis wrote:
>>> On Sunday, February 12, 2012 10:07:52 Ali Çehreli wrote:
>>>> Related question: Does D define the order of evaluation in an
>>>> expression
>>>> like
>>>>
>>>> foo() ~ bar()
>>>>
>>>> Or is it unspecified as in C and C++?
>>>
>>> It's currently unspecified. Walter has stated that he wants to make it so that it's always left to right, but I don't believe that he's done it yet, and it may or may not ever happen.
>>
>> Actually, it *is* specified as left-to-right, except for assignments and
>> argument evaluation. http://dlang.org/expression.html
>> No idea if the compiler fully implements that part of the spec.
> 
> Well, foo() ~ bar() _is_ argument evaluation if you're dealing with an overloaded operator. And unfortunately, whether the spec is what the compiler does is frequently suspect anyway. So, who knows.

"argument evaluation" in this context means that given eg "f(a,b,c);"
it's not specified in what order the expressions 'a', 'b', and 'c'
are evaluated, which matters if they are dependent or have side
effects. (depending on arg passing conventions it can make sense to
implement this one way or the other). If an expression is specced
as l-t-r (as CatExpression is here) then the call to foo() must
happen before executing bar(); the operator only comes into play later.

artur
February 13, 2012
On Monday, February 13, 2012 21:49:05 Artur Skawina wrote:
> "argument evaluation" in this context means that given eg "f(a,b,c);"
> it's not specified in what order the expressions 'a', 'b', and 'c'
> are evaluated, which matters if they are dependent or have side
> effects. (depending on arg passing conventions it can make sense to
> implement this one way or the other). If an expression is specced
> as l-t-r (as CatExpression is here) then the call to foo() must
> happen before executing bar(); the operator only comes into play later.

Ideally perhaps, but I expect that that's not true, because operator overloading is done via lowering.

foo() ~ bar()

would become

opBinary!"~"(foo(), bar());

which is a normal function call. It's possible that the compiler always evaluates foo first, but I'd seriously advise against relying on it.

In the long run, it probably will be guaranteed, because Walter wants to make it so that function arguments are always evaluated left-to-right, but until that happens, I wouldn't bet on foo() ~ bar() being guaranteed to have foo called before bar, even it's supposed to be.

- Jonathan M Davis
February 13, 2012
On 02/13/12 21:52, Jonathan M Davis wrote:
> On Monday, February 13, 2012 21:49:05 Artur Skawina wrote:
>> "argument evaluation" in this context means that given eg "f(a,b,c);"
>> it's not specified in what order the expressions 'a', 'b', and 'c'
>> are evaluated, which matters if they are dependent or have side
>> effects. (depending on arg passing conventions it can make sense to
>> implement this one way or the other). If an expression is specced
>> as l-t-r (as CatExpression is here) then the call to foo() must
>> happen before executing bar(); the operator only comes into play later.
> 
> Ideally perhaps, but I expect that that's not true, because operator overloading is done via lowering.
> 
> foo() ~ bar()
> 
> would become
> 
> opBinary!"~"(foo(), bar());
> 
> which is a normal function call. It's possible that the compiler always evaluates foo first, but I'd seriously advise against relying on it.
> 
> In the long run, it probably will be guaranteed, because Walter wants to make it so that function arguments are always evaluated left-to-right, but until that happens, I wouldn't bet on foo() ~ bar() being guaranteed to have foo called before bar, even it's supposed to be.

The important thing here is - the order absolutely *must* be foo(), then bar(),
what happens under the hood is completely irrelevant. The reason is simple -
if this is not what happens then it's a serious compiler bug. And if you can't
rely on the order then it just shouldn't be documented. The order /could/ be
undefined, so specifying it means the programmer has to assume he/she /can/
count that the compiler follows it - otherwise defining it wouldn't make any
sense.
IOW having the order defined, but not implemented, causes bugs that wouldn't be
there without such definition; hence one has to assume that it *is* implemented
(modulo unknown compiler bugs of course).
Undefining the order is a much better solution than suggesting that users
should assume the compiler is broken. It can always be re-defined later,
switching from defined to undefined is not as easy.

artur
February 13, 2012
On Monday, February 13, 2012 22:24:38 Artur Skawina wrote:
> The important thing here is - the order absolutely *must* be foo(), then
> bar(), what happens under the hood is completely irrelevant. The reason is
> simple - if this is not what happens then it's a serious compiler bug. And
> if you can't rely on the order then it just shouldn't be documented. The
> order /could/ be undefined, so specifying it means the programmer has to
> assume he/she /can/ count that the compiler follows it - otherwise defining
> it wouldn't make any sense.
> IOW having the order defined, but not implemented, causes bugs that wouldn't
> be there without such definition; hence one has to assume that it *is*
> implemented (modulo unknown compiler bugs of course).
> Undefining the order is a much better solution than suggesting that users
> should assume the compiler is broken. It can always be re-defined later,
> switching from defined to undefined is not as easy.

I'm not arguing that the order _shouldn't_ be defined and guaranteed. I'm just saying that I wouldn't trust the compiler to make such guarantees. If you find that bar gets evaluated before foo, then by all means, report it. But unless one of the dmd devs verifies in the code that it does indeed guarantee left-to- right evaluation of foo() ~ bar() even with operator overloading, I would consider it a bad idea to rely on it. Given that lowering is involved, I think that there's a high chance that the order is just as defined as directly calling op!"~"(foo(), bar()) would be.

- Jonathan M Davis
February 13, 2012
On 02/13/12 22:58, Jonathan M Davis wrote:
> On Monday, February 13, 2012 22:24:38 Artur Skawina wrote:
>> The important thing here is - the order absolutely *must* be foo(), then
>> bar(), what happens under the hood is completely irrelevant. The reason is
>> simple - if this is not what happens then it's a serious compiler bug. And
>> if you can't rely on the order then it just shouldn't be documented. The
>> order /could/ be undefined, so specifying it means the programmer has to
>> assume he/she /can/ count that the compiler follows it - otherwise defining
>> it wouldn't make any sense.
>> IOW having the order defined, but not implemented, causes bugs that wouldn't
>> be there without such definition; hence one has to assume that it *is*
>> implemented (modulo unknown compiler bugs of course).
>> Undefining the order is a much better solution than suggesting that users
>> should assume the compiler is broken. It can always be re-defined later,
>> switching from defined to undefined is not as easy.
> 
> I'm not arguing that the order _shouldn't_ be defined and guaranteed. I'm just

I'm not arguing that it _should_ be defined - just pointing out that the fact
that it currently _is_ means one should be able to assume it works. After all
there's only one D frontend and it comes from the vendor that also effectively
controls the language spec - so it's reasonable to expect things that are
documented, but could have been omitted, to work. If the compiler does not
currently implement these spec parts it would be much better to say that
the order *will* be L-T-R, but right now is undefined. That way users won't
be mislead and if somebody else decides do to a D compiler it will be clear
what should happen. Switching later from L-T-R to Undefined breaks backwards
compatibility, but doing it the other way is harmless.

artur
February 13, 2012
On 02/13/2012 11:27 PM, Artur Skawina wrote:
> On 02/13/12 22:58, Jonathan M Davis wrote:
>> On Monday, February 13, 2012 22:24:38 Artur Skawina wrote:
>>> The important thing here is - the order absolutely *must* be foo(), then
>>> bar(), what happens under the hood is completely irrelevant. The reason is
>>> simple - if this is not what happens then it's a serious compiler bug. And
>>> if you can't rely on the order then it just shouldn't be documented. The
>>> order /could/ be undefined, so specifying it means the programmer has to
>>> assume he/she /can/ count that the compiler follows it - otherwise defining
>>> it wouldn't make any sense.
>>> IOW having the order defined, but not implemented, causes bugs that wouldn't
>>> be there without such definition; hence one has to assume that it *is*
>>> implemented (modulo unknown compiler bugs of course).
>>> Undefining the order is a much better solution than suggesting that users
>>> should assume the compiler is broken. It can always be re-defined later,
>>> switching from defined to undefined is not as easy.
>>
>> I'm not arguing that the order _shouldn't_ be defined and guaranteed. I'm just
>
> I'm not arguing that it _should_ be defined - just pointing out that the fact
> that it currently _is_ means one should be able to assume it works. After all
> there's only one D frontend and it comes from the vendor that also effectively
> controls the language spec - so it's reasonable to expect things that are
> documented, but could have been omitted, to work. If the compiler does not
> currently implement these spec parts it would be much better to say that
> the order *will* be L-T-R, but right now is undefined. That way users won't
> be mislead and if somebody else decides do to a D compiler it will be clear
> what should happen. Switching later from L-T-R to Undefined breaks backwards
> compatibility, but doing it the other way is harmless.
>
> artur

D the language requires L-T-R for both binary operators and function invocation. If DMD the compiler does not implement this in all cases then that is a bug. Why should a compiler bug have any influence whatsoever on the *language specification* ? I cannot follow.

Order of evaluation for assignment expressions seems to be undefined in DMD 2.057, the following code has differing behavior in presence or absence of the optimization flags:

int foo(int x){writeln(x);return 0;}

ref int bar(int a,int b,int c){return *(new int);}

void main(){
    bar(foo(0),foo(1),foo(2))=bar(foo(3),foo(4),foo(5));
}

FWIW, I didn't find any other counter-examples.

February 13, 2012
On Monday, February 13, 2012 23:27:37 Artur Skawina wrote:
> I'm not arguing that it _should_ be defined - just pointing out that the fact that it currently _is_ means one should be able to assume it works. After all there's only one D frontend and it comes from the vendor that also effectively controls the language spec - so it's reasonable to expect things that are documented, but could have been omitted, to work. If the compiler does not currently implement these spec parts it would be much better to say that the order *will* be L-T-R, but right now is undefined. That way users won't be mislead and if somebody else decides do to a D compiler it will be clear what should happen. Switching later from L-T-R to Undefined breaks backwards compatibility, but doing it the other way is harmless.

The reality of the matter is that the spec is untrustworthy. The compiler may or may not follow it, and just because the spec says something and the compiler doesn't agree doesn't mean that the spec is going to win out. It's better than it used to be, and I think that Walter fixed a number of things in it recently, but the spec is notorious for being out-of-date when it comes to the little details.

In this particular case, I'd fully expect that the spec is correct and that if the compiler doesn't follow suit, then it's buggy. However, it's also the kind of situation where I think that a bug would be very likely. So, I'd advise against relying on it, simply because the odds of your code being buggy (due to what is likely a compiler bug) are too high.

- Jonathan M Davis
February 13, 2012
On 02/13/12 23:39, Timon Gehr wrote:
> On 02/13/2012 11:27 PM, Artur Skawina wrote:
>> On 02/13/12 22:58, Jonathan M Davis wrote:
>>> On Monday, February 13, 2012 22:24:38 Artur Skawina wrote:
>>>> The important thing here is - the order absolutely *must* be foo(), then
>>>> bar(), what happens under the hood is completely irrelevant. The reason is
>>>> simple - if this is not what happens then it's a serious compiler bug. And
>>>> if you can't rely on the order then it just shouldn't be documented. The
>>>> order /could/ be undefined, so specifying it means the programmer has to
>>>> assume he/she /can/ count that the compiler follows it - otherwise defining
>>>> it wouldn't make any sense.
>>>> IOW having the order defined, but not implemented, causes bugs that wouldn't
>>>> be there without such definition; hence one has to assume that it *is*
>>>> implemented (modulo unknown compiler bugs of course).
>>>> Undefining the order is a much better solution than suggesting that users
>>>> should assume the compiler is broken. It can always be re-defined later,
>>>> switching from defined to undefined is not as easy.
>>>
>>> I'm not arguing that the order _shouldn't_ be defined and guaranteed. I'm just
>>
>> I'm not arguing that it _should_ be defined - just pointing out that the fact
>> that it currently _is_ means one should be able to assume it works. After all
>> there's only one D frontend and it comes from the vendor that also effectively
>> controls the language spec - so it's reasonable to expect things that are
>> documented, but could have been omitted, to work. If the compiler does not
>> currently implement these spec parts it would be much better to say that
>> the order *will* be L-T-R, but right now is undefined. That way users won't
>> be mislead and if somebody else decides do to a D compiler it will be clear
>> what should happen. Switching later from L-T-R to Undefined breaks backwards
>> compatibility, but doing it the other way is harmless.
>>
>> artur
> 
> D the language requires L-T-R for both binary operators and function invocation. If DMD the compiler does not implement this in all cases then that is a bug. Why should a compiler bug have any influence whatsoever on the *language specification* ? I cannot follow.

In theory it shouldn't, but it's much better to have a situation where the spec
can be incrementally improved, than to end up having to make backwards incompatible
changes later, when for example it's found that fixing some corner cases would be
too hard.
And I keep saying that *if* the order is defined then it should be followed,
otherwise it would be better to document the actual behavior; that's all.

Is L-T-R for function arg evaluation documented somewhere, btw? The "expression" dlang.org page has it as implementation-defined.

artur
February 14, 2012
mmmhhh.... this is interesting nevertheless i don't understand the solution of my problem, and i cannot even understand it's origin:

void main()
{
    string s1 = "abcd";
    s1 = s1[stride(s1,0)..1] ~ 'r' ~ s1[2..$];
    writeln(s1);
}

why there is not way i cannot achive "arcd"?
February 14, 2012
On 02/14/2012 12:59 PM, RenatoL wrote:
> mmmhhh.... this is interesting nevertheless i don't understand the
> solution of my problem, and i cannot even understand it's origin:
>
> void main()
> {
>      string s1 = "abcd";
>      s1 = s1[stride(s1,0)..1] ~ 'r' ~ s1[2..$];
>      writeln(s1);
> }
>
> why there is not way i cannot achive "arcd"?

import std.stdio;
import std.utf;

void main()
{
    string s1 = "abcd";

    immutable firstCharStride = stride(s1, 0);
    immutable secondCharStride = stride(s1, firstCharStride);

    auto firstPart = s1[0..firstCharStride];
    auto lastPart = s1[firstCharStride + secondCharStride..$];

    s1 = firstPart ~ 'r' ~ lastPart;
    writeln(s1);
}

Ali