www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - study: use checkedint as a drop-in replacement of native long

reply mw <mingwu gmail.com> writes:
(This is a follow-up from the other thread:  
https://forum.dlang.org/thread/rdrqedmbknwrppbfixll forum.dlang.org)


I start this new thread to discuss and improve the usage of 
std.experimental.checkedint
  as a drop-in replacement of native long.

https://dlang.org/phobos/std_experimental_checkedint.html


`Checked` is a struct, try to use it as a `long` require some 
substantial change of existing code that previously use native 
long.

I list 8 things I've found below. Suggestions or PR welcome, e.g. 
even on how to improve the current implementation of `struct 
Checked`, or help from the compiler.

Thanks.


https://github.com/mingwugmail/dlang_tour/blob/master/divbug.d#L38
------------------------------------------------------------------------
alias Long = Checked!long;


void use_checked() {
   long la, lb, lc;  // native
   Long ca, cb, cc;  // checkedint

   // 0. as array literal
   long[] larr = [1, 2, 3];
//Long[] carr = [1, 2, 3];  // Error: cannot implicitly convert 
expression [1, 2, 3] of type int[] to Checked!(long, Abort)[]
   Long[] carr = [checked(1L), checked(2L), checked(3L)];  // more 
verbose

   // 1. checkedint cannot be chain assigned
   la = lb = lc = 0;
//ca = cb = cc = 0;  // Error: expression cc.opAssign(0) is void 
and has no value
   ca = 0;  // have to do separately
   cb = 0;
   cc = 0;

   // 2. have to use .get to call math func
   writeln(std.math.sgn(la));
   writeln(std.math.abs(la));
//writeln(std.math.sgn(ca));  // Error: template std.math.sgn 
cannot deduce function from argument types !()(Checked!(long, 
Abort)), candidates are:
//writeln(std.math.abs(ca));  // Error: template instance 
std.math.abs!(Checked!(long, Abort)) error instantiating
   writeln(std.math.sgn(ca.get));  // need .get
   writeln(std.math.abs(ca.get));  // need .get

   // 3. no opBinaryLeft
   lc = la * lb;
//cc = la * cb;  // Error: can only * a pointer, not a int
   cc = cb * la;  // have to switch order

   // 4. std.conv don't work
   double d = 0;
   lb = d.to!long;
//cb = d.to!Long;  // Error: template instance 
std.conv.to!(Checked!(long, Abort)).to!double error instantiating
   cb = lround(d);

   lb = "0".to!long;
//cb = "0".to!Long; // Error: template std.conv.toImpl cannot 
deduce function from argument types !(Checked!(long, 
Abort))(string), candidates are:
   cb = "0".to!long; // work around ok

   // 5. cannot assign to shared
   static struct S {
     shared long sl;
     shared Long sL;
   }
   S s;
   s.sl = la;
//s.sL = la;  // Error: template 
std.experimental.checkedint.Checked!(long, 
Abort).Checked.opAssign cannot deduce function from argument 
types !()(long) shared, candidates are:
   cast()s.sL = la;  // need the `cast`

   // 6. format %d won't work
   writefln("%04d", la);      // output: 0000
//writefln("%04d", ca);      // Expected '%s' format specifier 
for type 'Checked!(long, Abort)'
   writefln("%s",   ca);      // output: Checked!(long, Abort)(0), 
can this be just be 0?
   writefln("%04d", ca.get);  // output: 0000

   // 7. atomic won't work
   core.atomic.atomicOp!"+="(s.sl, lb);
//core.atomic.atomicOp!"+="(s.sL, cb);  // Error: template 
instance core.atomic.atomicOp!("+=", Checked!(long, Abort), 
Checked!(long, Abort)) error instantiating
//core.atomic.atomicFetchAdd(s.sL, cb);  // Error: template 
core.atomic.atomicFetchAdd cannot deduce function from argument 
types !()(shared(Checked!(long, Abort)), Checked!(long, Abort)), 
candidates are:
}
------------------------------------------------------------------------
Aug 14 2020
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Aug 15, 2020 at 04:28:05AM +0000, mw via Digitalmars-d wrote:
[...]
   // 1. checkedint cannot be chain assigned
   la = lb = lc = 0;
 //ca = cb = cc = 0;  // Error: expression cc.opAssign(0) is void and has no
 value
   ca = 0;  // have to do separately
   cb = 0;
   cc = 0;
This one should be easy to fix. Just change opAssign to return `this` instead of void, and that should solve the problem. [...]
   // 3. no opBinaryLeft
   lc = la * lb;
 //cc = la * cb;  // Error: can only * a pointer, not a int
   cc = cb * la;  // have to switch order
You mean lacking opBinaryRight? That should be simple to implement?
   // 4. std.conv don't work
   double d = 0;
   lb = d.to!long;
 //cb = d.to!Long;  // Error: template instance std.conv.to!(Checked!(long,
 Abort)).to!double error instantiating
   cb = lround(d);
Checked should implement `opCast(T : double)()`.
   lb = "0".to!long;
 //cb = "0".to!Long; // Error: template std.conv.toImpl cannot deduce
 function from argument types !(Checked!(long, Abort))(string), candidates
 are:
   cb = "0".to!long; // work around ok
Add a ctor to Checked that takes a string argument and parses it into a Checked value. [...]
   // 6. format %d won't work
   writefln("%04d", la);      // output: 0000
 //writefln("%04d", ca);      // Expected '%s' format specifier for type
 'Checked!(long, Abort)'
   writefln("%s",   ca);      // output: Checked!(long, Abort)(0), can this
 be just be 0?
   writefln("%04d", ca.get);  // output: 0000
Add an overload of toString that takes a FormatSpec argument: void toString(W,Char)(W sink, FormatSpec!Char fmt) if (isOutputRange!(W, Char)) { if (fmt.spec == "d") { ... /* do the right thing here */ } ... // etc. } These should probably be done as a series of Phobos PRs to improve the usability of Checked. T -- What's a "hot crossed bun"? An angry rabbit.
Aug 14 2020
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
More thoughts:

On Sat, Aug 15, 2020 at 04:28:05AM +0000, mw via Digitalmars-d wrote:
[...]
   // 2. have to use .get to call math func
   writeln(std.math.sgn(la));
   writeln(std.math.abs(la));
 //writeln(std.math.sgn(ca));  // Error: template std.math.sgn cannot deduce
 function from argument types !()(Checked!(long, Abort)), candidates are:
 //writeln(std.math.abs(ca));  // Error: template instance
 std.math.abs!(Checked!(long, Abort)) error instantiating
   writeln(std.math.sgn(ca.get));  // need .get
   writeln(std.math.abs(ca.get));  // need .get
Technically you *could* use `alias this` to decay it to long automatically, then these calls will work. But (1) that runs the risk of implicit conversion accidentally losing the guarantees of Checked when it silently turns into an unwrapped long, thereby defeating the purpose of using Checked; and (2) `alias this` is frowned upon these days because of its complicated relationship with subtyping. One workaround is to write forwarding overloads for these functions as member functions of Checked. But this is D, and we dislike boilerplate, so opDispatch comes to the rescue: struct Checked { ... auto opDispatch(string memb)(Checked arg) if (memb == "sgn" || memb == "abs" || ... /* etc */) { return mixin(memb ~ "(this.get)"); } ... } The bad thing is that you have to manually list the functions. But if then, you could import std.math and use introspection to extract the list of functions automatically. Good opportunity to take advantage of D's metaprogramming capabilities. Start with `__traits(allMembers, std.math)` and go from there. T -- Why is it that all of the instruments seeking intelligent life in the universe are pointed away from Earth? -- Michael Beibl
Aug 14 2020
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/14/2020 9:28 PM, mw wrote:
 I list 8 things I've found below. Suggestions or PR welcome, e.g. even on how
to 
 improve the current implementation of `struct Checked`, or help from the
compiler.
This is nice work. Thank you! Please file these issues in Bugzilla.
Aug 16 2020
parent reply mw <mingwu gmail.com> writes:
On Sunday, 16 August 2020 at 23:36:51 UTC, Walter Bright wrote:
 On 8/14/2020 9:28 PM, mw wrote:
 I list 8 things I've found below. Suggestions or PR welcome, 
 e.g. even on how to improve the current implementation of 
 `struct Checked`, or help from the compiler.
This is nice work. Thank you! Please file these issues in Bugzilla.
filed here: https://issues.dlang.org/show_bug.cgi?id=21169
Aug 17 2020
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 17, 2020 at 05:10:01PM +0000, mw via Digitalmars-d wrote:
 On Sunday, 16 August 2020 at 23:36:51 UTC, Walter Bright wrote:
 On 8/14/2020 9:28 PM, mw wrote:
 I list 8 things I've found below. Suggestions or PR welcome, e.g.
 even on how to improve the current implementation of `struct
 Checked`, or help from the compiler.
This is nice work. Thank you! Please file these issues in Bugzilla.
filed here: https://issues.dlang.org/show_bug.cgi?id=21169
You might want to consider splitting up that bug into multiple smaller, more self-contained bugs. Less intimidating that way for potential contributors to fix. ;-) T -- This sentence is false.
Aug 17 2020
parent reply mw <mingwu gmail.com> writes:
On Monday, 17 August 2020 at 17:15:59 UTC, H. S. Teoh wrote:
 On Mon, Aug 17, 2020 at 05:10:01PM +0000, mw via Digitalmars-d 
 wrote:
 
 https://issues.dlang.org/show_bug.cgi?id=21169
You might want to consider splitting up that bug into multiple smaller, more self-contained bugs. Less intimidating that way for potential contributors to fix. ;-)
Was hesitating between splitting or not, then thought user may want to take the full picture into consideration the best approach to fix the issue, e.g. in case one-stone-that-can-kill-multiple-birds. (People can always submit separate a PR to fix one particular issue).
 Chain assignment fix: https://github.com/dlang/phobos/pull/7599
Thanks for the PR, I just added comments: does this fix also work for mixed native & checked chain assignment? i.e. add to unittest: ``` long la, lb; Checked!long ca, cb; la = ca = lb = cb; // mixed chain assign ca = la = cb = lb; ```
Aug 17 2020
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 17, 2020 at 05:58:16PM +0000, mw via Digitalmars-d wrote:
 On Monday, 17 August 2020 at 17:15:59 UTC, H. S. Teoh wrote:
[...]
 Chain assignment fix: https://github.com/dlang/phobos/pull/7599
Thanks for the PR, I just added comments: does this fix also work for mixed native & checked chain assignment? i.e. add to unittest: ``` long la, lb; Checked!long ca, cb; la = ca = lb = cb; // mixed chain assign ca = la = cb = lb; ```
Currently, it doesn't work. I'm on the fence about whether it should: the whole point of using Checked is that you don't want to automatically convert to the native type because the converted value will lose the protections conferred by Check. Assigning a Checked to a native type *might* be a mistake - you thought the variable was Checked but it wasn't, so subsequent operations on it no longer has Checked semantics even though you thought it does. So I'm not sure this case should be supported. Assigning from Checked back to native should always be explicit IMO (the programmer explicitly indicates he doesn't want Checked protections anymore). T -- If I were two-faced, would I be wearing this one? -- Abraham Lincoln
Aug 17 2020
parent reply mw <mingwu gmail.com> writes:
On Monday, 17 August 2020 at 18:10:16 UTC, H. S. Teoh wrote:
 On Mon, Aug 17, 2020 at 05:58:16PM +0000, mw via Digitalmars-d 
 wrote:
 On Monday, 17 August 2020 at 17:15:59 UTC, H. S. Teoh wrote:
[...]
 Chain assignment fix: 
 https://github.com/dlang/phobos/pull/7599
Thanks for the PR, I just added comments: does this fix also work for mixed native & checked chain assignment? i.e. add to unittest: ``` long la, lb; Checked!long ca, cb; la = ca = lb = cb; // mixed chain assign ca = la = cb = lb; ```
Currently, it doesn't work. I'm on the fence about whether it should: the whole point of using Checked is that you don't want to automatically convert to the native type because the converted value will lose the protections conferred by Check. Assigning a Checked to a native type *might* be a mistake - you thought the variable was Checked but it wasn't, so subsequent operations on it no longer has Checked semantics even though
Yes, that's the principle we all agree. However, we are talking about opAssign() here. The user specifies his/her intention via the variable's type declaration, e.g. native `long` vs checked `Long`. The *subsequent* operations you talking about will be on user specified variable (type), there will be no surprise here: if the LHS is declared as a `long`, the subsequent operations will be on `long`, and if the LHS is `Long`, the subsequent operations will be on `Long`, all as user has specified. opAssign() just make the boxing/unboxing life easier between these two types. And there is not any mathematical operation performed inside opAssign(), hence for this particular function, native == checked is always true. So I think let opAssign() return the underlying type will make the drop-in replacement process more smooth, and without extra correctness concern.
 you thought it does.  So I'm not sure this case should be 
 supported.  Assigning from Checked back to native should always 
 be explicit IMO (the programmer explicitly indicates he doesn't 
 want Checked protections anymore).
Aug 17 2020
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/17/20 4:53 PM, mw wrote:
 On Monday, 17 August 2020 at 18:10:16 UTC, H. S. Teoh wrote:
 On Mon, Aug 17, 2020 at 05:58:16PM +0000, mw via Digitalmars-d wrote:
 On Monday, 17 August 2020 at 17:15:59 UTC, H. S. Teoh wrote:
[...]
 Chain assignment fix: > https://github.com/dlang/phobos/pull/7599
Thanks for the PR, I just added comments: does this fix also work for mixed native & checked chain assignment? i.e. add to unittest: ```   long la, lb;   Checked!long ca, cb;   la = ca = lb = cb;  // mixed chain assign   ca = la = cb = lb; ```
Currently, it doesn't work.  I'm on the fence about whether it should: the whole point of using Checked is that you don't want to automatically convert to the native type because the converted value will lose the protections conferred by Check. Assigning a Checked to a native type *might* be a mistake - you thought the variable was Checked but it wasn't, so subsequent operations on it no longer has Checked semantics even though
Yes, that's the principle we all agree. However, we are talking about opAssign() here. The user specifies his/her intention via the variable's type declaration, e.g. native `long` vs checked `Long`. The *subsequent* operations you talking about will be on user specified variable (type), there will be no surprise here: if the LHS is declared as a `long`, the subsequent operations will be on `long`, and if the LHS is `Long`, the subsequent operations will be on `Long`, all as user has specified. opAssign() just make the boxing/unboxing life easier between these two types. And there is not any mathematical operation performed inside opAssign(), hence for this particular function, native == checked is always true. So I think let opAssign() return the underlying type will make the drop-in replacement process more smooth, and without extra correctness concern.
Whenever I implement opAssign I have it return void and try to remember to propose that the compiler takes care of chained assignments by itself. Requiring user-defined assignment to `return *this;` was goofy in C++. Requiring user-defined assignment to `return this;` is goofy in D. Assignment should return void and the compiler should take care of it.
Aug 17 2020
next sibling parent mw <mingwu gmail.com> writes:
On Tuesday, 18 August 2020 at 01:23:01 UTC, Andrei Alexandrescu 
wrote:
 On 8/17/20 4:53 PM, mw wrote:
 On Monday, 17 August 2020 at 18:10:16 UTC, H. S. Teoh wrote:
 On Mon, Aug 17, 2020 at 05:58:16PM +0000, mw via 
 Digitalmars-d wrote:
 On Monday, 17 August 2020 at 17:15:59 UTC, H. S. Teoh wrote:
[...]
 Chain assignment fix: > 
 https://github.com/dlang/phobos/pull/7599
Thanks for the PR, I just added comments: does this fix also work for mixed native & checked chain assignment? i.e. add to unittest: ```   long la, lb;   Checked!long ca, cb;   la = ca = lb = cb;  // mixed chain assign   ca = la = cb = lb; ```
Currently, it doesn't work.  I'm on the fence about whether it should: the whole point of using Checked is that you don't want to automatically convert to the native type because the converted value will lose the protections conferred by Check. Assigning a Checked to a native type *might* be a mistake - you thought the variable was Checked but it wasn't, so subsequent operations on it no longer has Checked semantics even though
Yes, that's the principle we all agree. However, we are talking about opAssign() here. The user specifies his/her intention via the variable's type declaration, e.g. native `long` vs checked `Long`. The *subsequent* operations you talking about will be on user specified variable (type), there will be no surprise here: if the LHS is declared as a `long`, the subsequent operations will be on `long`, and if the LHS is `Long`, the subsequent operations will be on `Long`, all as user has specified. opAssign() just make the boxing/unboxing life easier between these two types. And there is not any mathematical operation performed inside opAssign(), hence for this particular function, native == checked is always true. So I think let opAssign() return the underlying type will make the drop-in replacement process more smooth, and without extra correctness concern.
Whenever I implement opAssign I have it return void and try to remember to propose that the compiler takes care of chained assignments by itself. Requiring user-defined assignment to `return *this;` was goofy in C++. Requiring user-defined assignment to `return this;` is goofy in D. Assignment should return void and the compiler should take care of it.
Right, the library fix (work-around) is sub-optimal, it's better be fixed by the compiler. Walter, it's your turn :-)
Aug 17 2020
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 17, 2020 at 09:23:01PM -0400, Andrei Alexandrescu via Digitalmars-d
wrote:
[...]
 Whenever I implement opAssign I have it return void and try to
 remember to propose that the compiler takes care of chained
 assignments by itself.
 
 Requiring user-defined assignment to `return *this;` was goofy in C++.
 Requiring user-defined assignment to `return this;` is goofy in D.
 Assignment should return void and the compiler should take care of it.
+1. Is there an enhancement request for this? As Walter often says, if it's not in bugzilla, it doesn't exist. ;-) T -- If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
Aug 17 2020
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 8/18/20 1:07 AM, H. S. Teoh wrote:
 On Mon, Aug 17, 2020 at 09:23:01PM -0400, Andrei Alexandrescu via
Digitalmars-d wrote:
 [...]
 Whenever I implement opAssign I have it return void and try to
 remember to propose that the compiler takes care of chained
 assignments by itself.

 Requiring user-defined assignment to `return *this;` was goofy in C++.
 Requiring user-defined assignment to `return this;` is goofy in D.
 Assignment should return void and the compiler should take care of it.
+1. Is there an enhancement request for this? As Walter often says, if it's not in bugzilla, it doesn't exist. ;-)
https://issues.dlang.org/show_bug.cgi?id=21175 Not holding my breath.
Aug 18 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Aug 18, 2020 at 05:39:42PM -0400, Andrei Alexandrescu via Digitalmars-d
wrote:
[...]
 https://issues.dlang.org/show_bug.cgi?id=21175
 
 Not holding my breath.
If someone can kick up a PR for this, the chances would increase significantly. ;-) T -- Error: Keyboard not attached. Press F1 to continue. -- Yoon Ha Lee, CONLANG
Aug 18 2020
prev sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 18 August 2020 at 01:23:01 UTC, Andrei Alexandrescu 
wrote:
 Requiring user-defined assignment to `return *this;` was goofy 
 in C++. Requiring user-defined assignment to `return this;` is 
 goofy in D. Assignment should return void and the compiler 
 should take care of it.
Do you mean the compiler should take care of returning *this when opAssign returns void? Because there are some use-cases for opAssign to return something different than `this`, but I cannot remember at the moment what it was. It's a niche case.
Mar 24 2021
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 24 March 2021 at 17:23:00 UTC, Q. Schroll wrote:
 On Tuesday, 18 August 2020 at 01:23:01 UTC, Andrei Alexandrescu 
 wrote:
 Requiring user-defined assignment to `return *this;` was goofy 
 in C++. Requiring user-defined assignment to `return this;` is 
 goofy in D. Assignment should return void and the compiler 
 should take care of it.
Do you mean the compiler should take care of returning *this when opAssign returns void? Because there are some use-cases for opAssign to return something different than `this`, but I cannot remember at the moment what it was. It's a niche case.
Ideally `lhs = rhs` would always evaluate to `lhs`, as it does for the built-in assignment operator. But that would require a deprecation cycle, since, as you say, existing code could be relying on the current behavior.
Mar 24 2021
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 24, 2021 at 05:23:00PM +0000, Q. Schroll via Digitalmars-d wrote:
 On Tuesday, 18 August 2020 at 01:23:01 UTC, Andrei Alexandrescu wrote:
 Requiring user-defined assignment to `return *this;` was goofy in C++.
 Requiring user-defined assignment to `return this;` is goofy in D.
 Assignment should return void and the compiler should take care of it.
Do you mean the compiler should take care of returning *this when opAssign returns void? Because there are some use-cases for opAssign to return something different than `this`, but I cannot remember at the moment what it was. It's a niche case.
I think Andrei meant that opAssign should *always* return void, and the compiler should always automatically make the RHS of `a = b;` its value. More precisely: struct S { ... } S a, b, c; return a = b = c; should be lowered to: struct S { ... } S a, b, c; b.opAssign(c); a.opAssign(b); return a; Having the value of `a = b` return anything other than the RHS of the assignment is goofy semantics. Since there is only one sensible meaning of `a = b = c`, the compiler should just automate it instead of requiring the user to restate the obvious (and potentially making a mistake or introducing goofy semantics that hurt the readability / maintainability of the resulting code). T -- If you're not part of the solution, you're part of the precipitate.
Mar 24 2021
next sibling parent Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 On Wed, Mar 24, 2021 at 05:23:00PM +0000, Q. Schroll via 
 Digitalmars-d wrote:
 On Tuesday, 18 August 2020 at 01:23:01 UTC, Andrei 
 Alexandrescu wrote:
 Requiring user-defined assignment to `return *this;` was 
 goofy in C++. Requiring user-defined assignment to `return 
 this;` is goofy in D. Assignment should return void and the 
 compiler should take care of it.
Do you mean the compiler should take care of returning *this when opAssign returns void? Because there are some use-cases for opAssign to return something different than `this`, but I cannot remember at the moment what it was. It's a niche case.
I think Andrei meant that opAssign should *always* return void, and the compiler should always automatically make the RHS of `a = b;` its value. More precisely: struct S { ... } S a, b, c; return a = b = c; should be lowered to: struct S { ... } S a, b, c; b.opAssign(c); a.opAssign(b); return a; Having the value of `a = b` return anything other than the RHS
LHS I guess
 of the assignment is goofy semantics. Since there is only one 
 sensible meaning of `a = b = c`, the compiler should just 
 automate it instead of requiring the user to restate the 
 obvious (and potentially making a mistake or introducing goofy 
 semantics that hurt the readability / maintainability of the 
 resulting code).
Does this expand to opIndexAssign and opIndexOpAssign? Because not returning by `ref` is common there and copying is not intended. What about property setters? It sure shouldn't implicitly call the getter -- and even if, a getter need not even exist. Enabling chained assignments isn't as trivial as fixing opAssign. I now know where I've seen an assignment operator not returning a reference to the object: C++'s slice_array and gslice_array[1] have void operator= and the reason probably is that (a) returning the (g)slice_array is conceptually wrong, (b) returning a valarray is wrong, too. Maybe there are constructs in D where the same reasoning applies. [1] https://www.cplusplus.com/reference/valarray/
Mar 24 2021
prev sibling parent reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined... I think the intent was to not force an order of execution of subexpressions on either side of a single assignment (e.g. a[i] = a[j] with an opIndex implementation), but this seems to leave too much leeway for assignment chains. Side note: having an undefined order of execution of the sides of an assignment can be a great source of fun (read: surprising) bugs in C++. Just going from GCC to clang switches the order around.
Mar 24 2021
next sibling parent mw <mingwu gmail.com> writes:
On Wednesday, 24 March 2021 at 18:38:07 UTC, Gregor Mückl wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
Where is the spec you referring to? Let's fix the spec first then.
Mar 24 2021
prev sibling next sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 24 March 2021 at 18:38:07 UTC, Gregor Mückl wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
You're probably mistaken. Evaluation order does not mean associating an expression differently. Assignment is right associative in any case: a = b = c is the same as a = (b = c) but when *evaluating* a, b, and c, the compiler can choose to evaluate e.g. b first. It makes more sense if you look at functions retuning by `ref`: int _a, _b, _c; ref int a() { return _a; } ref int b() { return _b; } ref int c() { return _c; } Then, in (parentheses are optional in D) a() = (b() = c()) the compiler could execute b() first, a() second and c() last, getting references (i.e. pointers) and assigning them in the associated way.
Mar 24 2021
parent reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Wednesday, 24 March 2021 at 19:29:54 UTC, Q. Schroll wrote:
 On Wednesday, 24 March 2021 at 18:38:07 UTC, Gregor Mückl wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
You're probably mistaken. Evaluation order does not mean associating an expression differently. Assignment is right associative in any case: a = b = c is the same as a = (b = c) but when *evaluating* a, b, and c, the compiler can choose to evaluate e.g. b first. It makes more sense if you look at functions retuning by `ref`: int _a, _b, _c; ref int a() { return _a; } ref int b() { return _b; } ref int c() { return _c; } Then, in (parentheses are optional in D) a() = (b() = c()) the compiler could execute b() first, a() second and c() last, getting references (i.e. pointers) and assigning them in the associated way.
Right, I've made a mistake and confused order of evaluation and associativity in my first look at the spec. Oops :( Still, I think that this part the spec isn't actually well written. I'm mostly referring to the section on order of evaluation, but the rest of this page is also relevant: https://dlang.org/spec/expression.html#order-of-evaluation What I'm missing is a clear statement about operator associativity and operator precedence for expressions. When I'm reading the spec without applying any additional common sense, a + b * c could be evaluated strictly from left to right because I don't see how mathematical operator precedence is explicitly established anywhere. Neither do I see associativity established anywhere. If assignment was clearly defined to be right-associative, things would be unambiguous.
Mar 24 2021
next sibling parent Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Wednesday, 24 March 2021 at 21:38:20 UTC, Gregor Mückl wrote:
 [...]
I'd actually like my claim to be proven wrong. I just can't seem to accomplish that myself ;).
Mar 24 2021
prev sibling parent ag0aep6g <anonymous example.com> writes:
On 24.03.21 22:38, Gregor Mückl wrote:
 What I'm missing is a clear statement about operator associativity and 
 operator precedence for expressions. When I'm reading the spec without 
 applying any additional common sense, a + b * c could be evaluated 
 strictly from left to right because I don't see how mathematical 
 operator precedence is explicitly established anywhere. Neither do I see 
 associativity established anywhere. If assignment was clearly defined to 
 be right-associative, things would be unambiguous.
It's in the grammar. `a + b * c` is parsed as (skipping the boring parts): AddExpression "a" "+" MulExpression "b" "*" "c" And `a = b = c`: AssignExpression "a" "=" AssignExpression "b" "=" "c"
Mar 24 2021
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 24, 2021 at 06:38:07PM +0000, Gregor Mückl via Digitalmars-d wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
Link to spec please? If what you say is true, that sounds like an error in the spec. Walter has said many times that evaluation order in D is left to right, unless otherwise indicated. Having it unspecified just leads to a huge can o' worms. [...]
 Side note: having an undefined order of execution of the sides of an
 assignment can be a great source of fun (read: surprising) bugs in
 C++. Just going from GCC to clang switches the order around.
Exactly, this is why Walter is against having unspecific order of execution. This supposed "optimization opportunity" leads to far more trouble than it's worth. T -- What doesn't kill me makes me stranger.
Mar 24 2021
parent reply Gregor =?UTF-8?B?TcO8Y2ts?= <gregormueckl gmx.de> writes:
On Wednesday, 24 March 2021 at 21:28:24 UTC, H. S. Teoh wrote:
 On Wed, Mar 24, 2021 at 06:38:07PM +0000, Gregor Mückl via 
 Digitalmars-d wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
Link to spec please? If what you say is true, that sounds like an error in the spec. Walter has said many times that evaluation order in D is left to right, unless otherwise indicated. Having it unspecified just leads to a huge can o' worms.
See point 1 under "Implementation Defined": https://dlang.org/spec/expression.html#order-of-evaluation "The order of evaluation of the operands of AssignExpression."
Mar 24 2021
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 24, 2021 at 09:40:07PM +0000, Gregor Mückl via Digitalmars-d wrote:
 On Wednesday, 24 March 2021 at 21:28:24 UTC, H. S. Teoh wrote:
 On Wed, Mar 24, 2021 at 06:38:07PM +0000, Gregor Mückl via Digitalmars-d
 wrote:
 On Wednesday, 24 March 2021 at 17:38:14 UTC, H. S. Teoh wrote:
 Since there is only one sensible meaning of `a = b = c`...
If I'm reading the spec on expressions right, the compiler is actually free to interpret that as (a = b) = c if it feels particularly adventurous. The evaluation order of assignment operations is explicitly undefined...
Link to spec please? If what you say is true, that sounds like an error in the spec. Walter has said many times that evaluation order in D is left to right, unless otherwise indicated. Having it unspecified just leads to a huge can o' worms.
See point 1 under "Implementation Defined": https://dlang.org/spec/expression.html#order-of-evaluation "The order of evaluation of the operands of AssignExpression."
The keyword here is "operands". The order of evaluation of the *operands* is implementation-defined; the order of the evaluation of the assignment *operators* is from right to left. T -- Only boring people get bored. -- JM
Mar 24 2021
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 17, 2020 at 05:10:01PM +0000, mw via Digitalmars-d wrote:
[...]
 https://issues.dlang.org/show_bug.cgi?id=21169
Chain assignment fix: https://github.com/dlang/phobos/pull/7599 T -- Some days you win; most days you lose.
Aug 17 2020
prev sibling parent Nathan S. <no.public.email example.com> writes:
On Saturday, 15 August 2020 at 04:28:05 UTC, mw wrote:
   // 7. atomic won't work
   core.atomic.atomicOp!"+="(s.sl, lb);
 //core.atomic.atomicOp!"+="(s.sL, cb);  // Error: template 
 instance core.atomic.atomicOp!("+=", Checked!(long, Abort), 
 Checked!(long, Abort)) error instantiating
 //core.atomic.atomicFetchAdd(s.sL, cb);  // Error: template 
 core.atomic.atomicFetchAdd cannot deduce function from argument 
 types !()(shared(Checked!(long, Abort)), Checked!(long, 
 Abort)), candidates are:
 }
 ------------------------------------------------------------------------
I've been taking a crack at fixing some of these issue but I think `atomicOp` is incompatible with the premise of `Checked!T`. Either the Hook won't have the opportunity to intercept the update based on what the result would be or the update won't be atomic. `atomicLoad`, `atomicStore`, and `cas` should be fine though as long as there is no Hook state.
Mar 24 2021