January 11, 2013
deadalnix wrote:
> On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:
> >I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?
> 
> After a long discussion with Andrei, it seems that it is left to right.

Then the spec should be fixed.

unittest
{
	int a = 0;
	++a = a;
	assert(a == 1);
}

Don't know though whether you find it surprising that the above code
passes? But whether it is left to right or right to left does not matter
that much. At least it's defined and you can internalize it.
The more I think about the more sense it makes to have it left to right.

Jens
January 11, 2013
On Friday, 11 January 2013 at 09:20:13 UTC, Jonathan M Davis wrote:
> On Friday, January 11, 2013 10:03:54 Don wrote:
>> That's my feeling too. I think that if we want to implement AAs
>> as a library type, we first need to eliminate all of the
>> semantics would be impossible to implement in a library.
>
> Well, the AAs are _already_ a library type. That's a large part of why they
> have so many bugs. The transition to a library type was badly done, and we
> sorely need a new implementation. Also, because the compiler is generating
> hooks that druntime plugs into, the built-in AAs aren't restricted to quite
> the same semantics that a user-defined AA type would be, even though the AAs
> are implemented in druntime instead of the compiler.


Unfortunately, that's not true. All that happened was that a template layer was added on top of the built-in implementation. The compiler still knows intimate details about the implementation. The number of hooks from the compiler to the runtime have not decreased; in fact, they have increased.

AFAIK there is not a single place in the compiler where coupling between compiler and runtime decreased. There are very many places where it got much more complicated.


> So, AFAIK, we don't really have a problem with the built-in AAs doing stuff
> that a library type can't do (that's not even possible at this point, because
> they're implemented with a library type).

It's not a library type in a meaningful sense. The semantics come almost entirely from the compiler, not from the library. There's almost nothing in the implementation that can be changed without changing the compiler.
January 11, 2013
On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:
> deadalnix wrote:
>> On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:
>> >I completely agree. Doesn't the spec say that relying on
>> >the order of assignment evaluation is undefined?
>> 
>> After a long discussion with Andrei, it seems that it is left to
>> right.
>
> Then the spec should be fixed.
>
> unittest
> {
> 	int a = 0;
> 	++a = a;
> 	assert(a == 1);
> }
>

++a isn't supposed to be an lvalue (it is not assignable).

> Don't know though whether you find it surprising that the above code
> passes? But whether it is left to right or right to left does not matter
> that much. At least it's defined and you can internalize it.
> The more I think about the more sense it makes to have it left to right.
>

It shouldn't pass as the entry has never been assigned when computing the value.
January 11, 2013
aa[key] = val;

should be evaluated:

1. aa
2. key
3. val
4. aa[key] = val <-- allocating slot and set to it

Kenji Hara
2013/01/11 18:56 "deadalnix" <deadalnix@gmail.com>:

> On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:
>
>> I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?
>>
>
> After a long discussion with Andrei, it seems that it is left to right.
>


January 11, 2013
deadalnix wrote:
> On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:
> >deadalnix wrote:
> >>On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:
> >>>I completely agree. Doesn't the spec say that relying on the order of assignment evaluation is undefined?
> >>
> >>After a long discussion with Andrei, it seems that it is left to right.
> >
> >Then the spec should be fixed.
> >
> >unittest
> >{
> >	int a = 0;
> >	++a = a;
> >	assert(a == 1);
> >}
> >
> 
> ++a isn't supposed to be an lvalue (it is not assignable).

Really? I thought the semantics (of ++a) are increment a and return a
reference to it. Whereas a++ is rewritten to (auto t = e, ++e, t). That
means it returns a copy of the old value of a.

> >Don't know though whether you find it surprising that the above
> >code
> >passes? But whether it is left to right or right to left does not
> >matter
> >that much. At least it's defined and you can internalize it.
> >The more I think about the more sense it makes to have it left to
> >right.
> >
> 
> It shouldn't pass as the entry has never been assigned when computing the value.

Isn't this a speciality of AAs in D/C++? When you access an non existing element it gets created? You do not need to assign to it.

Jens
January 11, 2013
On Friday, 11 January 2013 at 10:43:33 UTC, Jens Mueller wrote:
> deadalnix wrote:
>> On Friday, 11 January 2013 at 10:16:28 UTC, Jens Mueller wrote:
>> >deadalnix wrote:
>> >>On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer
>> >>wrote:
>> >>>I completely agree. Doesn't the spec say that relying on
>> >>>the order of assignment evaluation is undefined?
>> >>
>> >>After a long discussion with Andrei, it seems that it is left to
>> >>right.
>> >
>> >Then the spec should be fixed.
>> >
>> >unittest
>> >{
>> >	int a = 0;
>> >	++a = a;
>> >	assert(a == 1);
>> >}
>> >
>> 
>> ++a isn't supposed to be an lvalue (it is not assignable).
>
> Really? I thought the semantics (of ++a) are increment a and return a
> reference to it. Whereas a++ is rewritten to (auto t = e, ++e, t). That
> means it returns a copy of the old value of a.

It is meant to be an lvalue, and is the reason this is legal in both C++ and D:

int main()
{
    int a = 0;
    ++++a;
    return 0;
}
January 11, 2013
On Friday, 11 January 2013 at 10:59:00 UTC, monarch_dodra wrote:
> It is meant to be an lvalue, and is the reason this is legal in both C++ and D:
>
> int main()
> {
>     int a = 0;
>     ++++a;
>     return 0;
> }

OK, it seems I talked too fast here. Still it seems very weird to me :D
January 11, 2013
On Friday, 11 January 2013 at 09:50:18 UTC, deadalnix wrote:
> On Friday, 11 January 2013 at 08:55:55 UTC, Bernard Helyer wrote:
>> I completely agree. Doesn't the spec say that relying on
>> the order of assignment evaluation is undefined?
>
> After a long discussion with Andrei, it seems that it is left to right.

This seems wrong to me.

In particular, if you define an opIndexAssign, then the RHS *has* to be evaluated first (from a "visual" point of view, since LHS is pretty much "this").

//----
a[0] = a[0]
//----
Becomes
//----
a.opIndexAssign(a[0], 0);
//----

On a related note, I don't know how AA's are actually implemented, but it sounds like giving them opIndexXXX would solve a lot of our problems:
* Initialization to T.init if RHS throws
* a[0] = a[0]; runs if there is no a[0].

Or even code such as this:
//----
    int[int] a;
    ++a[0];
//----
In what universe do we actually expect this to work? It shouldn't.
January 11, 2013
Don wrote:
> Consider this code:
> ---
>    int[int] x;
> 
>    int k = x[2] + 5; // Error, range violation. Makes sense.
> 
>    x[2] = x[2] + 5;  // But this works!!!
> ---
>
> That is, x[2] doesn't exist, *unless you are about to assign to
> it*.
> What happens is:
> 1. lvalue index (creates x[2], sets it to int.init)
> 2. rvalue index (returns x[2], which is now 0)
> 3. lvalue index assign (sets x[2] = 5)
> 
> In reality, step 1 returns a pointer to the newly created element.
> 
> How could this be implemented as a library type?
> The superficially similar case, x[2] += 5; can be implemented
> with opIndexOpAssign. But I don't know how to do this one.
> 
> Note that elements are not always magically created when an lvalue is required. AFAIK it only happens in assignment. For example this code gives a runtime error:
> ---
> void foo(ref int g) { ++g; }
> 
>    int[int] x;
>    foo( x[2] );  // range error, x[2] doesn't exist yet
> ---

I don't know how opIndex is defined and I cannot find appropriate
documentation at http://dlang.org/hash-map.html.
But you're right this is odd. Either opIndex throws a RangeError or it
creates a value.
I would probably go with the C++ approach: creating the value if it does
not exist. I.e. making the second statement legal. Then k is int.init +
5.
Sorry for my first post. I didn't see it clear.

Jens
January 11, 2013
On Friday, 11 January 2013 at 11:14:27 UTC, monarch_dodra wrote:
> In particular, if you define an opIndexAssign, then the RHS *has* to be evaluated first (from a "visual" point of view, since LHS is pretty much "this").
>
> //----
> a[0] = a[0]
> //----
> Becomes
> //----
> a.opIndexAssign(a[0], 0);
> //----
>

It is probably not gonna change at this point, but opIndexAssign seems wrong here, not the LTR evaluation.