May 26, 2015
On 05/26/2015 01:45 AM, Andrei Alexandrescu wrote:
> On 5/25/15 11:58 AM, Daniel Murphy wrote:
>> "Andrei Alexandrescu"  wrote in message
>> news:mjvlv5$vch$1@digitalmars.com...
>>
>>> > which one is correct?
>>>
>>> GDC. -- Andrei
>>
>> I don't think it matters too much if we pick strict LTR, or keep dmd's
>> existing exception for assign expressions.  IIRC Walter is in favour of
>> keeping the exception[1].
>>
>> Could you and Walter please come to an agreement and confirm here?  It
>> should be fairly straightforward to get this fixed once it's clear which
>> way it should go.
>>
>> [1]
>> https://github.com/D-Programming-Language/dmd/pull/4035#issuecomment-58861231
>>
>
> I'm fine with RTL for assignment expressions, and LTR everywhere else.
> Daniel, if you could work this out at front end level so it goes the
> same way for all backends, that would be fantastic. -- Andrei
>

Why? Strictly left-to-right is the simplest thing.
May 26, 2015
On Monday, 25 May 2015 at 23:44:57 UTC, Andrei Alexandrescu wrote:
> On 5/25/15 11:58 AM, Daniel Murphy wrote:
>> "Andrei Alexandrescu"  wrote in message
>> news:mjvlv5$vch$1@digitalmars.com...
>>
>>> > which one is correct?
>>>
>>> GDC. -- Andrei
>>
>> I don't think it matters too much if we pick strict LTR, or keep dmd's
>> existing exception for assign expressions.  IIRC Walter is in favour of
>> keeping the exception[1].
>>
>> Could you and Walter please come to an agreement and confirm here?  It
>> should be fairly straightforward to get this fixed once it's clear which
>> way it should go.
>>
>> [1]
>> https://github.com/D-Programming-Language/dmd/pull/4035#issuecomment-58861231
>
> I'm fine with RTL for assignment expressions, and LTR everywhere else. Daniel, if you could work this out at front end level so it goes the same way for all backends, that would be fantastic. -- Andrei

It seems to me that

a += b - c;

should always be the same as

a.opOpAssign!"+"(b - c);

because otherwise it's just totally confusing.
May 26, 2015
On Monday, 25 May 2015 at 17:25:57 UTC, Andrei Alexandrescu wrote:
>> It's not += doing the magic, it's bar(). And it's not limited to
>> concurrency, it happens with every side effect:
>>
>> import std.stdio;
>> void main()
>> {
>>     int a = 0;
>>     int bar()
>>     {
>>         a++;
>>         return a;
>>     }
>>     a += bar(); // => a = a + bar()
>>     writeln(a);
>> }
>>
>> DMD: 2
>> GDC: 1
>>
>> which one is correct?
>
> GDC. -- Andrei

You made me change SDC to return 2 recently using the following as an argument (one lwoering per line):

a += foo();
((ref X, Y) => X = X + Y)(a, foo());

http://33.media.tumblr.com/31bb0136f46468417bd3ccac1c52c769/tumblr_inline_nix5v8WXLd1t7oi6g.gif
May 26, 2015
On Tuesday, 26 May 2015 at 00:07:33 UTC, Timon Gehr wrote:
>> I'm fine with RTL for assignment expressions, and LTR everywhere else.
>> Daniel, if you could work this out at front end level so it goes the
>> same way for all backends, that would be fantastic. -- Andrei
>>
>
> Why? Strictly left-to-right is the simplest thing.

In case of opAssign kind of thing, LTR is not doable as operator overloading, at least not in a backward compatible manner.
May 26, 2015
On Monday, 25 May 2015 at 15:35:02 UTC, Jonathan M Davis wrote:
> 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

One of C's design mistakes is to make assignments expressions and not statements. Favouring terseness over correctness, an issue the C follow-up language Go partially recognized by turning "++" and "--" into statements.

I agree with you that if an expression depends on evaluation order it is most likely either buggy or prone to become buggy when the program is modified later on. So it is reasonable to define it as illegal and leave it to a sanitizer. (Of course, the exception is shortcut operators like "&&" and "||", which already are have strict evaluation order).

Being able to change evaluation order can provide optimization opportunities that cannot be fixed by having the compiler infer it due to aliasing issues. Not only due to registers, but also because of barriers, aliasing,  lookup-tables, sub-expressions etc…

The downside to not having a strict evaluation order is contexts where you want to use multiple generator calls in a single expression (like a numeric range or random number generator), so it goes against the whole "range iterators" approach which will lead to lengthy expressions that do contain side effects.

So essentially D does not have any other reasonable option than strict LTR evaluation, since it is making "gigantic" expressions with generators in them a  selling point.

You have to support what you market as a major feature whether that is done by having dedicated "range-operators" that have strict evaluation order or by making all expressions strict..
May 26, 2015
On 05/26/2015 02:55 AM, deadalnix wrote:
> On Tuesday, 26 May 2015 at 00:07:33 UTC, Timon Gehr wrote:
>>> I'm fine with RTL for assignment expressions, and LTR everywhere else.
>>> Daniel, if you could work this out at front end level so it goes the
>>> same way for all backends, that would be fantastic. -- Andrei
>>>
>>
>> Why? Strictly left-to-right is the simplest thing.
>
> In case of opAssign kind of thing, LTR is not doable as operator
> overloading, at least not in a backward compatible manner.

Not caching the value of the left hand side is not the same thing as right-to-left evaluation:

int a=0,b=0;
(b++,a)=b; // ltr gives a==1, rtl gives a==0, caching irrelevant

int a=0,b=0;
((ref a,b)=>a=b)((b++,a),b) // operator overloading lowering gives a==1

However, this is a more general problem with operator overloading: the first argument is always passed by reference, hence it is not cached:

int[] foo(){
    int a=1;
    int[] r;
    a=(a=a*2)+(a=a+2); // with l-t-r and caching: 6
    r~=a;
    alias string=immutable(char)[];
    static struct S{
        int a;
        this(int a){ this.a=a; }
        S opBinary(string op)(S t){ return S(mixin("a "~op~" t.a")); }
        ref S opUnary(string op:"++")(){ ++a; return this; }
    }
    static struct T{
        int a;
        this(int a){ this.a=a; }
        T opBinaryRight(string op)(T s){ return T(mixin("s.a "~op~" a")); }
        ref T opUnary(string op:"++")(){ ++a; return this; }
    }
    auto s=S(1);
    auto t=T(1);
    s=(s=s*S(2))+(s=s+S(2)); // with l-t-r and lowering: 8
    t=(t=t*T(2))+(t=t+T(2)); // either 8 or 12, depending on whether evaluation order is preserved during lowering.
    r~=s.a,r~=t.a;
    return r;
}

I guess overloaded operators could be made to cache the old value. (As they do in CTFE, apparently. :o))

However, this seems like overkill. Any other ideas?

May 26, 2015
On 05/26/2015 06:35 AM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" wrote:
>
> One of C's design mistakes is to make assignments expressions and not
> statements.

I think it is more about returning void vs. returning the lvalue. The expression/statement distinction is unnecessary.
May 26, 2015
On 05/26/2015 02:51 PM, Timon Gehr wrote:
>
> int a=0,b=0;
> (b++,a)=b; // ltr gives a==1, rtl gives a==0, caching irrelevant

This should have said that caching _on the lhs_ is irrelevant.
May 26, 2015
On Tuesday, 26 May 2015 at 12:54:27 UTC, Timon Gehr wrote:
> On 05/26/2015 06:35 AM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" wrote:
>>
>> One of C's design mistakes is to make assignments expressions and not
>> statements.
>
> I think it is more about returning void vs. returning the lvalue. The expression/statement distinction is unnecessary.

Not sure what you mean, the ideal for writing maintainable code is that expressions are either free of side effects or that side effects at least are independent and encapsulated in a robust manner.

Everything is unnecessary beyond the bare minimum (e.g. a Turing Machine), but for a sensible imperative language the distinction between statements and expressions is necessary, due to control flow, which is why SSA needs the phi function: http://en.wikipedia.org/wiki/Dominator_(graph_theory) . Unless you are doing something completely different, like some weird non-deterministic language.

That said, another C design-flaw is that you cannot prevent return values from being ignored, but I think that is another issue more related to resource management and ownership.

In terms of describing intent, the distinction between functions, procedures and constructors have a lot of value. And actually, also visually distinguishing between ownership transfer, referencing of objects and value assignment…
May 26, 2015
On 05/26/15 14:54, Timon Gehr via Digitalmars-d wrote:
> On 05/26/2015 06:35 AM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= <ola.fosheim.grostad+dlang@gmail.com>" wrote:
>>
>> One of C's design mistakes is to make assignments expressions and not statements.
> 
> I think it is more about returning void vs. returning the lvalue. The expression/statement distinction is unnecessary.

   int a, b, c;

   void f();
   f(a=b);

   void g(T...)(T) {}
   g(a=b);

   // and, even when 'void' is not a first class type:
   void h(int);
   h(((a=b), c));

artur