Jump to page: 1 24  
Page
Thread overview
More magical AA semantics
Jan 11, 2013
Don
Jan 11, 2013
Jonathan M Davis
Jan 11, 2013
Bernard Helyer
Jan 11, 2013
Mehrdad
Jan 11, 2013
deadalnix
Jan 11, 2013
Jens Mueller
Jan 11, 2013
deadalnix
Jan 11, 2013
Jens Mueller
Jan 11, 2013
monarch_dodra
Jan 11, 2013
deadalnix
Jan 11, 2013
kenji hara
Jan 11, 2013
monarch_dodra
Jan 11, 2013
deadalnix
Jan 11, 2013
monarch_dodra
Jan 11, 2013
bearophile
Jan 12, 2013
deadalnix
Jan 12, 2013
monarch_dodra
Jan 12, 2013
Jonathan M Davis
Jan 12, 2013
monarch_dodra
Jan 12, 2013
Jonathan M Davis
Jan 12, 2013
monarch_dodra
Jan 12, 2013
Jonathan M Davis
Jan 12, 2013
monarch_dodra
Jan 12, 2013
monarch_dodra
Jan 12, 2013
monarch_dodra
Jan 13, 2013
deadalnix
Jan 13, 2013
Jonathan M Davis
Jan 12, 2013
H. S. Teoh
Jan 11, 2013
Don
Jan 11, 2013
Jonathan M Davis
Jan 11, 2013
Don
Jan 11, 2013
Jens Mueller
Jan 11, 2013
bearophile
Jan 11, 2013
Daniel Murphy
Jan 11, 2013
Jens Mueller
Jan 11, 2013
H. S. Teoh
Jan 12, 2013
Timon Gehr
January 11, 2013
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
---
January 11, 2013
On Friday, January 11, 2013 08:53:44 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 would argue that the fact that

x[2] = x[2] + 5;

works is a bug. Given some of the weirdness that happens with assigning to AA elements, it doesn't entirely surprise me. For instance,

x[2] = funcThatThrows();

results in x[2] holding the init value of x's element type. But I think that it's indicative of problems with the current AA implementation which need to be fixed and not something that we should be trying to emulate with library types.

- Jonathan M Davis
January 11, 2013
On Friday, 11 January 2013 at 08:26:54 UTC, Jonathan M Davis wrote:
> On Friday, January 11, 2013 08:53:44 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 would argue that the fact that
>
> x[2] = x[2] + 5;
>
> works is a bug.

I completely agree. Doesn't the spec say that relying on
the order of assignment evaluation is undefined?
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!!!
> ---

I think the last statement is illegal. Because from http://dlang.org/expression.html I extract:

The evaluation order of = is implementation defined and it is illegal to
depend on it. The compiler should catch these but it cannot in all
cases. If the evaluation order was fixed i.e. right-to-left in this
case, the code would throw. It also happens that the evaluation may
change depending on the optimization flags.
So I believe it's an issue of evaluation order.

Jens
January 11, 2013
On Friday, 11 January 2013 at 08:26:54 UTC, Jonathan M Davis wrote:
> On Friday, January 11, 2013 08:53:44 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*.

>> How could this be implemented as a library type?


> I would argue that the fact that
>
> x[2] = x[2] + 5;
>
> works is a bug. Given some of the weirdness that happens with assigning to AA
> elements, it doesn't entirely surprise me. For instance,
>
> x[2] = funcThatThrows();
>
> results in x[2] holding the init value of x's element type. But I think that
> it's indicative of problems with the current AA implementation which need to
> be fixed and not something that we should be trying to emulate with library
> types.

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.

Specificially, I think we need to disallow semantics which are inconsistent with:

struct AA
{
  AAImpl impl;

  ref Value opIndex(Key key);
  Value opIndexAssign(Value value, Key key);
  Value opIndexOpAssign(string op)(Value value, Key key);
}

where the last two create the entry if it doesn't already exist.

Those last two should also create a blank AA if it doesn't already exist.
Personally I'd be much happier if instead, the blank AA was created at construction, so that AAs would be pure reference types, but at least the semantics are implementable.
January 11, 2013
See also:

http://d.puremagic.com/issues/show_bug.cgi?id=3825

Bye,
bearophile
January 11, 2013
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.

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). Rather, what we need is a new, properly templated solution. But that's a lot of work, and I believe that the last person who attempted it gave up on it beacuse of all the problems that he was running into.

- Jonathan M Davis
January 11, 2013
"Don" <don@nospam.com> wrote in message news:imqicbgjotdtzfgwdeor@forum.dlang.org...
> Consider this code:
> ---
>    int[int] x;
>
>    int k = x[2] + 5; // Error, range violation. Makes sense.
>
>    x[2] = x[2] + 5;  // But this works!!!
> ---
>

Definitely a bug.


January 11, 2013
Shouldn't the rhs be evaluated before the lhs? Why would it be undefined/unspecified/etc.?
January 11, 2013
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.
« First   ‹ Prev
1 2 3 4