Object `+= lfuns are not called when they should have been. It appears that we don't call += lfuns directly, instead we expand a += b+c into a = a+b+c. Well, that is all fine and dandy, but, the f_add function then tries to fold this back into a += by checking if the number of refs on the object == 1, and then calling `+= instead.
This check does not give any meaningful results given normal objects and += usage: - For one, the object might have an extra instance on the stack, in which case the minimum number of references becomes 2 already. - And for another, the object might have multiple instances which all refer to the same object; which *implies* that updating one of those using += should modify all of them.
The following sample code:
class refmeharder { int val; void create(int i) { werror("Set %d\n",val = i); }; void getval(string s) { werror("%s%d\n",s,val); }; void incval() { werror("Inc %d\n",++val); }; refmeharder `+=(int i) { werror("+= called %d\n", val+=i); return this; }; refmeharder `+(int i) { werror("+ called %d\n", val+i); return refmeharder(val+i); }; };
int main(int argc, array(string) argv) { refmeharder a=refmeharder(2); refmeharder b=refmeharder(3); refmeharder c=a,d=a,e=a,f=a,g=a,h=a; a->incval(); b->incval(); c->incval(); a+=5; b+=11; c+=23; a->getval("a"); b->getval("b"); c->getval("c"); return 0; }
Together with the following debug-patch:
diff --git a/src/operators.c b/src/operators.c index 45de79f..18fbe4a 100644 --- a/src/operators.c +++ b/src/operators.c @@ -1505,6 +1505,8 @@ PMOD_EXPORT void f_add(INT32 args) /* The first argument is an object. */ o = sp[-args].u.object; p = o->prog->inherits[SUBTYPEOF(sp[-args])].prog; +if((i = FIND_LFUN(p, LFUN_ADD_EQ)) != -1) +fprintf(stderr, "Object %p type %p refs %d\n", o, p, o->refs); if(o->refs==1 && (i = FIND_LFUN(p, LFUN_ADD_EQ)) != -1) {
Results in an output of: Set 2000 Set 3000 Inc 2001 Inc 3001 Inc 2002 Object 0x200e750 type 0x2055450 refs 8 + called 2007 Set 2007 Object 0x200e7b0 type 0x2055450 refs 2 + called 3012 Set 3012 Object 0x200e750 type 0x2055450 refs 7 + called 2025 Set 2025 a2007 b3012 c2025
But should have resulted in an output of: Set 2000 Set 3000 Inc 2001 Inc 3001 Inc 2002 Object 0x200e750 type 0x2055450 refs 8 += called 2007 Object 0x200e7b0 type 0x2055450 refs 2 += called 3012 Object 0x200e750 type 0x2055450 refs 7 += called 2040 a2040 b3012 c2040
I'll be adding something to this effect to the testsuites. The immediate questions though here are: - The refs==1 check is way too limiting, the question is, is there a meaningful alternative? (Probably not). - The only way to solve this seems to be by supporting calling `+= lfuns directly. But, if we do, do we need to somehow suppress the autoconversion from a+=b+c to a=a+b+c ? Can it be suppressed for objects only? Where in the code is this conversion being done?