www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - opOpAssign of AA: defined behavior?

reply WebFreak001 <d.forum webfreak.org> writes:
I have the following code:

     double[string] foo;
     foo["a"] += 1;

how is the opOpAssign on the AA defined? Is it defined to set the 
value to the value to the right of the opOpAssign if it isn't set 
for primitives or does it add the given value onto T.init?

Doing

     foo["b"]++;

gives me 1, so this looks like it comes from an arbitrary 0, 
however when I do

     double v = foo["c"]++;

I wanted to find out what initial value it is basing the 
increment off, however on dmd this triggered a segfault and on 
ldc this gave me 0. Where did the 0 come from? double.init should 
be NaN

When I have a custom user defined type like

struct Foo
{
     int x = 4;

     ref Foo opOpAssign(string op : "+")(int v)
     {
         x += v;
         return this;
     }

     Foo opBinary(string op : "+")(int v)
     {
         return Foo(x + v);
     }
}

void main()
{
     Foo[string] foo;
     foo["a"] += 2;
     writeln(foo);
}

it will give me a range violation at runtime and not init it for 
me at all.

There is `aa.require("a", Foo.init) += 4;` now which solves this, 
but I would prefer having the small simple syntax well defined 
for all types instead of only primitives. Also I don't see 
anywhere in the specification that `require` must actually return 
a ref value, so I can't trust this either.
Jun 23 2020
next sibling parent Eduard Staniloiu <edi33416 gmail.com> writes:
On Tuesday, 23 June 2020 at 09:15:57 UTC, WebFreak001 wrote:
 [...]

 it will give me a range violation at runtime and not init it 
 for me at all.

 There is `aa.require("a", Foo.init) += 4;` now which solves 
 this, but I would prefer having the small simple syntax well 
 defined for all types instead of only primitives. Also I don't 
 see anywhere in the specification that `require` must actually 
 return a ref value, so I can't trust this either.
You make a very good case. I think this would make a great bootcamp issue (https://issues.dlang.org/) Cheers, Edi
Jun 23 2020
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 6/23/20 5:15 AM, WebFreak001 wrote:
 I have the following code:
 
      double[string] foo;
      foo["a"] += 1;
 
 how is the opOpAssign on the AA defined? Is it defined to set the value 
 to the value to the right of the opOpAssign if it isn't set for 
 primitives or does it add the given value onto T.init?
 
 Doing
 
      foo["b"]++;
 
 gives me 1, so this looks like it comes from an arbitrary 0, however 
 when I do
 
      double v = foo["c"]++;
 
 I wanted to find out what initial value it is basing the increment off, 
 however on dmd this triggered a segfault and on ldc this gave me 0. 
 Where did the 0 come from? double.init should be NaN
I agree. I don't think it's defined by the language, but by the implementation. Looking at the implementation, it's here: https://github.com/dlang/druntime/blob/2cc13ead1e7e535ef8ebd1f600d4ffb508a93f98/src/rt/aaA.d#L502-L577 Note the statement "[Returns:] If key was not in the aa, a mutable pointer to newly inserted value which is set to all zeros" This is specifically where the zeroing happens: https://github.com/dlang/druntime/blob/2cc13ead1e7e535ef8ebd1f600d4ffb508a93f98/src/rt/aaA.d#L220 IIUC, this is a direct binding from the language, so we may not have the ability to set to init value in object.d, it would have to be done here. -Steve
Jun 23 2020