digitalmars.D.announce - DIP 1016--ref T accepts r-values--Formal Assessment
- Mike Parker (9/9) Jan 23 2019 Walter and Andrei have declined to accept DIP 1016, "ref T
- Nicholas Wilson (5/25) Jan 24 2019 But it does, or at least gives an example of:
- Nicholas Wilson (20/36) Jan 24 2019 Although not specified by the DIP, I think this could be easily
- kinke (22/45) Jan 24 2019 Describing this stuff in detail (rewritten expression?!), isn't
- Nicholas Wilson (3/23) Jan 24 2019 Exactly, doing something that could result in a different outcome
- Manu (10/43) Jan 24 2019 Sure, it's not 'trivial', but it is 'simple' in that it's isolated,
- Walter Bright (5/7) Jan 24 2019 There must be an individual gate for each of __pfx and pfy. With the rew...
- Nicholas Wilson (5/13) Jan 24 2019 Indeed, and thats why it should be have exactly the same as if
- kinke (8/14) Jan 25 2019 There is no individual gate, there's just one to rule the
- Steven Schveighoffer (5/19) Jan 25 2019 I think the point of the DIP is not to lower expressions. It makes no
- Walter Bright (2/12) Jan 25 2019 What happens, then, when b() throws?
- kinke (31/45) Jan 25 2019 `__pfx` goes out of scope, and is dtor expression
- Neia Neutuladh (41/58) Jan 25 2019 And nested calls are serialized as you'd expect:
- Manu (21/79) Jan 25 2019 Is this fine?
- Neia Neutuladh (7/10) Jan 25 2019 The contention was that, if the arguments are constructed properly,
- Manu (7/16) Jan 25 2019 No, that was never the intent, and certainly not written anywhere.
- Nicholas Wilson (4/15) Jan 24 2019 Raises a good point, not covered by @disable where the intent is
- Manu (20/37) Jan 24 2019 Actually, this was discussed, but somehow this piece of discussion
- Nicholas Wilson (5/31) Jan 24 2019 Probably.
- kinke (11/21) Jan 24 2019 `out` with current semantics cannot be used as drop-in
- kinke (2/4) Jan 24 2019 Should be 'by the callER' of course.
- Rubn (11/32) Jan 24 2019 Yah I'd like this approach way better and it could be an error if
- kinke (10/12) Jan 24 2019 I don't like that, conditional mutations like this should work
- Manu (10/32) Jan 24 2019 Shared in/out functions are very rare by contrast to out parameters.
- kinke (16/22) Jan 24 2019 The code I write is the exact opposite of your perception - some
- Walter Bright (8/12) Jan 24 2019 The compiler should elide the default-initializations before the call (a...
- Andrei Alexandrescu (16/34) Jan 28 2019 It seems to me that a proposal adding the "@rvalue" attribute in
- Andrei Alexandrescu (41/81) Jan 28 2019 One more thought.
- Steven Schveighoffer (6/10) Jan 28 2019 I agree with you. It's one thing for the caller to pass in an rvalue
- Andrei Alexandrescu (2/3) Jan 28 2019 Can you please post more detail? It may be of relevance to future work.
- Steven Schveighoffer (10/14) Jan 28 2019 Any time you have the alias this, then you can get confused when calling...
- Manu (10/90) Jan 28 2019 I started reading this post, and I was compelled to reply with this
- Andrei Alexandrescu (35/42) Jan 29 2019 The problem is with rvalues resulting as temporaries from lvalues. As in...
- kinke (33/50) Jan 30 2019 The inverse, the @norval attribute, would feature all but the
- Rubn (3/20) Jan 24 2019 Why isn't it covered by @disable ?
- Nicholas Wilson (4/16) Jan 24 2019 Given the simplicity of the fixes for the problems identified I
- 12345swordy (7/10) Jan 24 2019 Yes, please! Let not restart the whole dip process for this! The
- Manu (85/94) Jan 24 2019 Hooray!
- Walter Bright (3/8) Jan 24 2019 It's no problem if you want to rework the existing text, just submit it ...
- Manu (19/27) Jan 24 2019 This process has a long and deep pipe, why should it be a new DIP?
- Walter Bright (14/14) Jan 24 2019 No, it is not rejected in principle. Finding serious errors in it on the...
- Nicholas Wilson (11/21) Jan 25 2019 Praytell, what serious errors? Also you should heed your own
- Manu (56/63) Jan 25 2019 I'm very clearly NOT in a hurry here. We've been sitting on this for 10 ...
- Dein (7/26) Jan 25 2019 Bit ironic considering how some DIPs are being handled. Changes
- 12345swordy (3/5) Jan 24 2019 And wait for another 180+ days for a fix? Come on dude, can you
- Walter Bright (5/7) Jan 24 2019 Of course it's frustrating. On the other hand, we've had a lot of proble...
- Manu (16/23) Jan 24 2019 Which issues? The initialization order issue? That's relatively
- Manu (5/12) Jan 24 2019 "Rush"? We've literally been debating this since my first post on this
- Nicholas Wilson (6/12) Jan 24 2019 This will be discussed at DConf at the DLF meeting, which is only
- Dgame (4/15) Jan 24 2019 Couldn't that just be rewritten as something like
- 12345swordy (6/15) Jan 24 2019 Welp, one of the features of the implicit convertion dip that I
- Elie Morisse (5/18) Jan 24 2019 Sorry, but that's just irrelevant / missing the point.
- Walter Bright (3/5) Jan 24 2019 On the contrary. It is good to find conceptual errors before implementin...
- Nicholas Wilson (7/12) Jan 24 2019 You mean the conceptual errors you made when reviewing it? Like:
- Walter Bright (47/52) Jan 25 2019 The first oddity about @disable is it is attached to the foo(int), not t...
- Nicholas Wilson (4/7) Jan 25 2019 The pot calling the kettle black. DIP1000? DIP1017?
- Nicholas Wilson (2/7) Jan 25 2019 Or DIP1008: https://issues.dlang.org/show_bug.cgi?id=19463
- Olivier FAURE (11/13) Jan 25 2019 I haven't participated to writing this DIP, but I personally
- Rubn (3/66) Jan 25 2019 For future reference, this is what a formal review should be. I'd
- rikki cattermole (4/86) Jan 25 2019 So in other words, a formal review should include somebody acting as an
- Manu (50/68) Jan 25 2019 And right here, I can see our fundamental difference of perspective...
- Walter Bright (36/61) Jan 25 2019 The DIP mentions them several times in the "forum threads" section. I se...
- Rubn (30/84) Jan 26 2019 Anything that could be implicitly converted to use foo(int) can
- Walter Bright (4/5) Jan 26 2019 The point is, the DIP needs to spell this out in an organized and comple...
- Manu (61/123) Jan 28 2019 I think the `@disable` semantic is correct; I understand your
- Walter Bright (6/22) Jan 29 2019 It's where it should be, under "Function Overloading":
- Manu (10/17) Jan 25 2019 Incidentally, the reason I invented a syntax in this DIP, was because
- Andrei Alexandrescu (90/100) Jan 28 2019 Hi everyone, I've followed the responses to this, some conveying
- Manu (36/137) Jan 29 2019 A few things in here...
- Nicholas Wilson (9/14) Jan 29 2019 I _think_ what is meant is:
- Andrei Alexandrescu (3/18) Jan 29 2019 Affirmative. (Just wrote about the same in another post). Thanks very mu...
- Andrei Alexandrescu (60/88) Jan 29 2019 No. This is a nonnegotiable matter:
- Andrei Alexandrescu (2/4) Jan 29 2019 Meant "...not only no solution..."
- Walter Bright (11/18) Jan 29 2019 Even simpler:
- Manu (5/24) Jan 30 2019 Why are you so stuck on this case? The DIP is about accepting rvalues, n...
- Neia Neutuladh (4/7) Jan 30 2019 The result of a CastExpression is an rvalue. An implicit cast is a
- Manu (9/16) Jan 30 2019 But there's no existing language rule that attempts to perform an
- 12345swordy (18/35) Jan 30 2019 Because of the rewrite that your proposed in your dip.
- Manu (5/32) Jan 30 2019 "a short variable named: S" is an lvalue, so why would the rewrite be
- Walter Bright (39/43) Jan 31 2019 This illustrates why this should be compared with C++. Consider this C++...
- Steven Schveighoffer (8/18) Jan 31 2019 The biggest reason I see to not worry about const is that we already
- Andrei Alexandrescu (25/44) Jan 30 2019 That's exactly what lowerings are for: to precisely specify what should
- Manu (17/61) Jan 30 2019 What? `expr` must be an rvalue... if it's not, then none of this applies...
- Nicholas Wilson (8/40) Jan 30 2019 It would.
- Manu (15/66) Jan 30 2019 I can't do that, it's been rejected, with mostly incorrect rejection
- Andrei Alexandrescu (16/93) Jan 30 2019 The trouble is major.
- Steven Schveighoffer (39/58) Jan 31 2019 Yes, the trouble specifically is loops. Because loops execute their
- Andrei Alexandrescu (4/8) Jan 30 2019 You're right. I have deleted this post a few seconds after having sent
- Olivier FAURE (16/17) Jan 31 2019 I don't know if it's truck-sized, but here's another corner case:
- Steven Schveighoffer (22/46) Jan 31 2019 Yeah, that's already a thing that ref in D doesn't protect against:
- Andrei Alexandrescu (6/57) Jan 31 2019 Affirmative. This discussion should be part of the revised DIP along
- Olivier FAURE (4/6) Jan 31 2019 It took me a while to understand what the compiler was doing.
- jmh530 (3/6) Jan 31 2019 It doesn't compile with dip1000 without first giving the getter
- Olivier FAURE (5/7) Jan 31 2019 But it still compiles with -dip1000 once you give x() and y()
- jmh530 (4/11) Jan 31 2019 Agreed. I had checked that it didn't work and as I figured out
- Andrei Alexandrescu (4/10) Jan 31 2019 The proposal could actually disallow rvalues that have lvalue syntax,
- Walter Bright (10/13) Jan 31 2019 That's why it's problematic to have a rule that rvalues can be implicitl...
- Rubn (3/16) Jan 31 2019 Why would it cause the assert to fail? A new array is constructed
- Steven Schveighoffer (7/18) Jan 31 2019 No, because those calls might actually do something with side effects!
- Steven Schveighoffer (13/19) Jan 31 2019 The problem is that `this` is passed by reference EVEN for rvalues.
- jmh530 (26/30) Jan 31 2019 The way you put it makes it sound like a bug...
- H. S. Teoh (6/41) Jan 31 2019 Why is it a problem that this code compiles without error?
- jmh530 (34/75) Jan 31 2019 Sorry if I didn't really complete my thought.
- Olivier FAURE (5/8) Feb 01 2019 Isn't it a recurring theme on this forum that D is really cool
- Steven Schveighoffer (5/29) Jan 31 2019 BTW, the DIP discusses how to annotate these rare situations:
- Olivier FAURE (7/11) Jan 31 2019 I don't think that's a solution. The problem is in the getter
- Steven Schveighoffer (4/16) Jan 31 2019 How is the problem not in doubleMyValue? It's sole purpose is to update
- aliak (28/46) Feb 01 2019 Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a
- Timon Gehr (4/20) Feb 01 2019 http://wilzbach.github.io/d-dip/DIP24
- aliak (5/26) Feb 01 2019 You mean if __temp is modified in the doubleMyValue and pt.x
- 12345swordy (6/27) Feb 01 2019 Not only that, but C# forbids you passing properties as an
- Aliak (4/17) Feb 01 2019 By properties I mean accessors? I don’t mean normal fieldsat
- 12345swordy (2/19) Feb 01 2019 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes...
- Olivier FAURE (11/15) Feb 01 2019 The thing is, D doesn't really differentiate between a getter and
- Paul Backus (2/4) Feb 01 2019 In theory, at least, that's what @property is for.
- 12345swordy (3/18) Feb 01 2019 Languages like c# solve this problem by disallowing passing
- Andrei Alexandrescu (3/26) Feb 03 2019 Such would be a good conservative approach for us too. We can relax it
- Olivier FAURE (29/32) Feb 01 2019 But right now, updating an rvalue is what ref is supposed to be
- Nicholas Wilson (5/19) Jan 30 2019 It appears to say it does:
- Manu (16/37) Jan 30 2019 Yes, as said above, read `short(10)`. I can understand the confusion
- Steven Schveighoffer (9/29) Jan 30 2019 I think changing it to `short(10)` helps the argument that you didn't
- Manu (22/50) Jan 30 2019 I mean, the heading of the DIP is "ref T accepts r-values", the whole
- Andrei Alexandrescu (94/138) Jan 30 2019 Affirmative.
- Olivier FAURE (3/15) Jan 29 2019 Fair enough.
- Don (29/29) Jan 29 2019 I'm on the reviewers side here.
- 12345swordy (4/8) Jan 29 2019 ...Did you even read the arguments in the dip? This has been
- Don (28/36) Jan 29 2019 Well, I read the DIP and the whole forum discussion back in the
- 12345swordy (9/25) Jan 30 2019 I do not accept gut feeling as a valid objection here. The
- Don (13/20) Jan 30 2019 Like I said previously I am on the reviews side and that's it.
- 12345swordy (12/23) Jan 30 2019 In terms of what exactly?
- bitwise (11/28) Feb 04 2019 I think this solves all of the above:
- Dominikus Dittes Scherkl (4/14) Feb 04 2019 I don't understand this.
- bitwise (5/8) Feb 04 2019 I meant for that to be interpreted like a Regular expression,
- Paul Backus (5/13) Feb 04 2019 It's actually fine to leave the `return` there
- bitwise (6/20) Feb 08 2019 Even better ;)
- Nicholas Wilson (2/17) Feb 08 2019 Immediately called lamdas are always inlined.
- Dennis (20/21) Feb 08 2019 ```
- H. S. Teoh (6/32) Feb 08 2019 Does LDC/GDC inline it?
- H. S. Teoh (12/24) Feb 08 2019 [...]
- Dennis (7/9) Feb 08 2019 That's definitely true, but that leaves the question whether
- H. S. Teoh (19/28) Feb 08 2019 TBH, I've been finding that ldc compilation times aren't all that bad
- bitwise (4/14) Feb 08 2019 Since the user doesn't explicitly place the lambda in their code,
- H. S. Teoh (12/27) Feb 08 2019 Using lowering to lambdas as a way of defining semantics is not the same
- Daniel N (11/23) Feb 10 2019 The lambda even correctly handles "@disable this(this);", I like
Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md
Jan 23 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdBut it does, or at least gives an example of: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md#function-calls-as-arguments The statement the expression is part of is "enscoped" and the temporaries live in the scope of that statement.fun(10) ==> { T __temp0 = void; fun(__temp0 := 10); }The first problem the Language Maintainers identified with this approach is that the rewrite is from an expression to a statement, rendering it invalid. The expression should be rewritten as an expression to clarify how it behaves in larger expressions.
Jan 24 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdThe second problem is the use of := (which the DIP Author defines as representing "the initial construction, and not a copy operation as would be expected if this code were written with an = expression"). This approach shows its deficiencies in the multiple arguments case; if the first constructor throws an exception, all remaining values will be destroyed in the void state as they never have the chance to become initialized.Although not specified by the DIP, I think this could be easily remedied by saying that the order of construction is the same as if the temporaries were not bound to ref, i.e. --- struct A {~this();} struct B{ ~this();} A a(); B b(); void f(A a, B b); void g(ref A a, ref B b); f(a(),b()); //(1) g(a(),b()); //(2) --- and a() or b() may throw (and are pure), that (1) and (2) exhibit the same exception/destructor semantics. Which is what I would expect to happen under no given specification (although specifying it is a good idea!). To put it another way, they should be constructed in place in the order they are used, and given they are a temporary, they can only be used once.
Jan 24 2019
On Thursday, 24 January 2019 at 09:04:41 UTC, Nicholas Wilson wrote:On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Describing this stuff in detail (rewritten expression?!), isn't trivial and requires knowledge about how calls and construction/destruction of argument expressions works. E.g., the f() call in the code above is lowered to (-vcg-ast): (bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy); Here, (only seemingly unused) temporary `__gate` is used to control the destruction of temporaries with dtors (here just the `__pfx` arg, as `__pfy` is a special case [no potentially throwing later argument expressions...]) at the caller side (false => destruct; true => skip destruction, as it has been moved successfully to the callee, which destructed it). The dtor expressions of these temporaries (e.g., `__gate || __pfx.~this()` for `__pfx`) aren't visible with `-vg-ast`. With this DIP, *all* rvalues passed by ref must be lowered to temporaries. In case they require destruction, the only difference wrt. the by-value case is that they are *always* destructed by the caller (after the call, or if an exception is thrown while they are in scope), i.e., their destruction isn't controlled by `__gate`.The second problem is the use of := (which the DIP Author defines as representing "the initial construction, and not a copy operation as would be expected if this code were written with an = expression"). This approach shows its deficiencies in the multiple arguments case; if the first constructor throws an exception, all remaining values will be destroyed in the void state as they never have the chance to become initialized.Although not specified by the DIP, I think this could be easily remedied by saying that the order of construction is the same as if the temporaries were not bound to ref, i.e. --- struct A {~this();} struct B{ ~this();} A a(); B b(); void f(A a, B b); void g(ref A a, ref B b); f(a(),b()); //(1) g(a(),b()); //(2) --- and a() or b() may throw (and are pure), that (1) and (2) exhibit the same exception/destructor semantics.
Jan 24 2019
On Thursday, 24 January 2019 at 21:03:29 UTC, kinke wrote:Describing this stuff in detail (rewritten expression?!), isn't trivial and requires knowledge about how calls and construction/destruction of argument expressions works. E.g., the f() call in the code above is lowered to (-vcg-ast): (bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy); Here, (only seemingly unused) temporary `__gate` is used to control the destruction of temporaries with dtors (here just the `__pfx` arg, as `__pfy` is a special case [no potentially throwing later argument expressions...]) at the caller side (false => destruct; true => skip destruction, as it has been moved successfully to the callee, which destructed it). The dtor expressions of these temporaries (e.g., `__gate || __pfx.~this()` for `__pfx`) aren't visible with `-vg-ast`. With this DIP, *all* rvalues passed by ref must be lowered to temporaries. In case they require destruction, the only difference wrt. the by-value case is that they are *always* destructed by the caller (after the call, or if an exception is thrown while they are in scope), i.e., their destruction isn't controlled by `__gate`.Exactly, doing something that could result in a different outcome would be a disaster.
Jan 24 2019
On Thu, Jan 24, 2019 at 1:05 PM kinke via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Thursday, 24 January 2019 at 09:04:41 UTC, Nicholas Wilson wrote:Sure, it's not 'trivial', but it is 'simple' in that it's isolated, and it only affects the part of the DIP that defines the rewrite semantic. It doesn't lead to "practically a completely differnet DIP" as was suggested. Changing the detail of the rewrite such that it has the proper effect required by the surrounding text and handles exceptions correctly can probably be done in such a way that not a single line of text requires any changes.On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Describing this stuff in detail (rewritten expression?!), isn't trivial and requires knowledge about how calls and construction/destruction of argument expressions works.The second problem is the use of := (which the DIP Author defines as representing "the initial construction, and not a copy operation as would be expected if this code were written with an = expression"). This approach shows its deficiencies in the multiple arguments case; if the first constructor throws an exception, all remaining values will be destroyed in the void state as they never have the chance to become initialized.Although not specified by the DIP, I think this could be easily remedied by saying that the order of construction is the same as if the temporaries were not bound to ref, i.e. --- struct A {~this();} struct B{ ~this();} A a(); B b(); void f(A a, B b); void g(ref A a, ref B b); f(a(),b()); //(1) g(a(),b()); //(2) --- and a() or b() may throw (and are pure), that (1) and (2) exhibit the same exception/destructor semantics.
Jan 24 2019
On 1/24/2019 1:03 PM, kinke wrote:(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed. (All this rigamarole is part of why exception handling isn't free, even in the happy case.)
Jan 24 2019
On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:On 1/24/2019 1:03 PM, kinke wrote:Indeed, and thats why it should be have exactly the same as if there were no `ref` involved, except for the not copying stuff. Behaving and differently is, a) wrong and, b) going to cause a lot of confusion.(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed. (All this rigamarole is part of why exception handling isn't free, even in the happy case.)
Jan 24 2019
On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:On 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries. That's the current state, and there's no need for that to change. I was trying to say that a rewrite as expression, as requested as part of the assessment, clearly isn't enough, as the dtor expressions aren't visible this way, and neither is the scoping (when the dtor expression of `__pfx` comes into play etc.).(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On 1/25/19 5:57 AM, kinke wrote:On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:I think the point of the DIP is not to lower expressions. It makes no sense to, they have to be statements (just like all temporaries live until the end of a statement). -SteveOn 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries. That's the current state, and there's no need for that to change. I was trying to say that a rewrite as expression, as requested as part of the assessment, clearly isn't enough, as the dtor expressions aren't visible this way, and neither is the scoping (when the dtor expression of `__pfx` comes into play etc.).(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On 1/25/2019 2:57 AM, kinke wrote:On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:What happens, then, when b() throws?On 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries.(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On Friday, 25 January 2019 at 19:08:55 UTC, Walter Bright wrote:On 1/25/2019 2:57 AM, kinke wrote:`__pfx` goes out of scope, and is dtor expression (cleanup/finally) is run as part of stack unwinding. Rewritten as block statement: { bool __gate = false; A __pfx = a(); try { B __pfy = b(); // may throw __gate = true; return f(__pfx, __pfy); // move args to callee } finally { __gate || __pfx.__xdtor(); // only destruct if not moved to callee } } With this DIP, the g() call (both rvalue args passed by ref) would now become: { A __pfx = a(); try { B __pfy = b(); // may throw try { return g(__pfx, __pfy); // pass by ref } finally { __pfy.__xdtor(); // always destructed by caller } } finally { __pfx.__xdtor(); } }On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:What happens, then, when b() throws?On 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries.(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On Fri, 25 Jan 2019 23:08:52 +0000, kinke wrote:On Friday, 25 January 2019 at 19:08:55 UTC, Walter Bright wrote:And nested calls are serialized as you'd expect: int foo(ref S i, ref S j); S bar(ref S i, ref S j); S someRvalue(int i); foo( bar(someRvalue(1), someRvalue(2)), someRvalue(4)); // translates to something like: { bool __gate1 = false; S __tmp1 = void; S __tmp2 = void; S __tmp3 = void; __tmp1 = someRvalue(1); try { __tmp2 = someRvalue(2); __gate1 = true; __tmp3 = bar(__tmp1, __tmp2); } finally { if (!__gate1) __tmp1.__xdtor(); } S __tmp4 = void; bool __gate2 = false; try { __tmp4 = someRvalue(4); __gate2 = true; return foo(__tmp3, __tmp4); } finally { if (!__gate2) { __tmp3.__xdtor(); } } }On 1/25/2019 2:57 AM, kinke wrote:`__pfx` goes out of scope, and is dtor expression (cleanup/finally) is run as part of stack unwinding. Rewritten as block statement:On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:What happens, then, when b() throws?On 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries.(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On Fri, Jan 25, 2019 at 4:20 PM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Fri, 25 Jan 2019 23:08:52 +0000, kinke wrote:Is this fine? Given above example: int foo(ref S i, ref S j); S bar(ref S i, ref S j); S someRvalue(int i); foo( bar(someRvalue(1), someRvalue(2)), someRvalue(4)); ===> { S __tmp0 = someRvalue(1); S __tmp1 = someRvalue(2); S __tmp2 = bar(__tmp0, __tmp1); S __tmp3 = someRvalue(4); foo(__tmp2, __tmp3); } Removing the `void` stuff end expanding such that the declaration + initialisation is at the appropriate moments; any function can throw normally, and the unwind works naturally?On Friday, 25 January 2019 at 19:08:55 UTC, Walter Bright wrote:And nested calls are serialized as you'd expect: int foo(ref S i, ref S j); S bar(ref S i, ref S j); S someRvalue(int i); foo( bar(someRvalue(1), someRvalue(2)), someRvalue(4)); // translates to something like: { bool __gate1 = false; S __tmp1 = void; S __tmp2 = void; S __tmp3 = void; __tmp1 = someRvalue(1); try { __tmp2 = someRvalue(2); __gate1 = true; __tmp3 = bar(__tmp1, __tmp2); } finally { if (!__gate1) __tmp1.__xdtor(); } S __tmp4 = void; bool __gate2 = false; try { __tmp4 = someRvalue(4); __gate2 = true; return foo(__tmp3, __tmp4); } finally { if (!__gate2) { __tmp3.__xdtor(); } } }On 1/25/2019 2:57 AM, kinke wrote:`__pfx` goes out of scope, and is dtor expression (cleanup/finally) is run as part of stack unwinding. Rewritten as block statement:On Thursday, 24 January 2019 at 23:59:30 UTC, Walter Bright wrote:What happens, then, when b() throws?On 1/24/2019 1:03 PM, kinke wrote:There is no individual gate, there's just one to rule the caller-destruction of all temporaries.(bool __gate = false;) , ((A __pfx = a();)) , ((B __pfy = b();)) , __gate = true , f(__pfx, __pfy);There must be an individual gate for each of __pfx and pfy. With the rewrite above, if b() throws then _pfx won't be destructed.
Jan 25 2019
On Fri, 25 Jan 2019 18:14:56 -0800, Manu wrote:Removing the `void` stuff end expanding such that the declaration + initialisation is at the appropriate moments; any function can throw normally, and the unwind works naturally?The contention was that, if the arguments are constructed properly, ownership is given to the called function, which is responsible for calling destructors. But if the argument construction fails, the caller is responsible for calling destructors. I'm not sure what the point of that was. The called function doesn't own its parameters and shouldn't ever call destructors. So now I'm confused.
Jan 25 2019
On Fri, Jan 25, 2019 at 6:50 PM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Fri, 25 Jan 2019 18:14:56 -0800, Manu wrote:No, that was never the intent, and certainly not written anywhere. Ownership is assigned the the calling scope that we introduce surrounding the statement. That's where the temporaries declared; I didn't consider that ownership unclear.Removing the `void` stuff end expanding such that the declaration + initialisation is at the appropriate moments; any function can throw normally, and the unwind works naturally?The contention was that, if the arguments are constructed properly, ownership is given to the called function, which is responsible for calling destructors.I'm not sure what the point of that was. The called function doesn't own its parameters and shouldn't ever call destructors. So now I'm confused.Correct. You're not confused. The callee does NOT own ref parameters.
Jan 25 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdvoid atomicIncrement(ref shared long x); atomicIncrement(myInt);Raises a good point, not covered by disable where the intent is to modify it and modifying a temporary is wrong. `out ref` perhaps?
Jan 24 2019
On Thu, Jan 24, 2019 at 1:25 AM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Actually, this was discussed, but somehow this piece of discussion didn't get folded back it to the DIP. It is mentioned in the 'Community Review Round 1' digest though. We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue. That doesn't address the specific `atomicIncrement` case here, but now we're in VERY niche territory; we analysed a lot of cases, and concluded that such cases were relatively few, and other choices exist to mitigate those cases. There are cases that want to do mutation to rvalues (like in pipeline functions), and then most cases can use 'out' instead. Remaining cases are quite hard to find, and in this particular case, I'd suggest that `atomicIncrement`, a very low-level implementation-detail function, should just receive a pointer.Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdvoid atomicIncrement(ref shared long x); atomicIncrement(myInt);Raises a good point, not covered by disable where the intent is to modify it and modifying a temporary is wrong. `out ref` perhaps?
Jan 24 2019
On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:On Thu, Jan 24, 2019 at 1:25 AM Nicholas Wilson via We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.True I forgot about that.That doesn't address the specific `atomicIncrement` case here, but now we're in VERY niche territory; we analysed a lot of cases, and concluded that such cases were relatively few, and other choices exist to mitigate those cases. There are cases that want to do mutation to rvalues (like in pipeline functions), and then most cases can use 'out' instead. Remaining cases are quite hard to find, and in this particular case, I'd suggest that `atomicIncrement`, a very low-level implementation-detail function, should just receive a pointer.Probably. Oh well, I'll add this to the long list of things to make sure is covered at dconf by the foundation.
Jan 24 2019
On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an the side effect clearly visible. I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)
Jan 24 2019
On Thursday, 24 January 2019 at 20:01:45 UTC, kinke wrote:(most args are probably already default-initialized by the callee in the line above the call...)Should be 'by the callER' of course.
Jan 24 2019
On Thursday, 24 January 2019 at 20:01:45 UTC, kinke wrote:On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:Yah I'd like this approach way better and it could be an error if the function didn't assign the variable a value instead. Rather than just default initializing it. void test(out int value) { // error: value must be assigned a value } In the event the value passed isn't initialized. Can be dangerous if it is a struct with a destructor though. If they void initialized it, but I guess that's a problem with the current implementation as well.We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an explicitly required `out` at the I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)
Jan 24 2019
On Thursday, 24 January 2019 at 21:57:57 UTC, Rubn wrote:it could be an error if the function didn't assign the variable a value insteadI don't like that, conditional mutations like this should work too: void conditionalIncrement(bool condition, out long value) { if (condition) ++value; } conditionalIncrement(true, 10); // not allowed, rvalue long value = 10; conditionalIncrement(true, out value); // fine
Jan 24 2019
On Thu, Jan 24, 2019 at 12:05 PM kinke via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:Shared in/out functions are very rare by contrast to out parameters.We discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry.Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an the side effect clearly visible. I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)Maybe... but there are satisfying options for basically any case we could imagine; and worst case, use a pointer rather than ref. Adding stuff like norval feels heavy-handed, and I personally judge this issue as being severe enough to warrant that baggage. What are some legit cases where, assuming a world where we want to avoid naked ref in cases where we want to receive compile errors when users pass rvalues, aren't satisfied by other options?
Jan 24 2019
On Thursday, 24 January 2019 at 22:38:01 UTC, Manu wrote:Shared in/out functions are very rare by contrast to out parameters.The code I write is the exact opposite of your perception - some occasional side-effect-mutations of params, and almost no stuff 'returned' as out params.What are some legit cases where, assuming a world where we want to avoid naked ref in cases where we want to receive compile errors when users pass rvalues, aren't satisfied by other options?Proposed `out` semantics: --- void increment(out long value) { ++value; } increment(out value); --- vs. pointer version with current `out` semantics: --- void increment(long* pValue) { ++(*pValue); } increment(&value); --- The pointer workaround is both ugly (C) and unsafe (you can pass null).
Jan 24 2019
On Thursday, 24 January 2019 at 23:18:11 UTC, kinke wrote:Proposed `out` semantics: --- void increment(out long value) { ++value; } increment(out value); --- vs. pointer version with current `out` semantics: --- void increment(long* pValue) { ++(*pValue); } increment(&value); --- The pointer workaround is both ugly (C) and unsafe (you can pass null).safe void safestFunction() { int* ptr; increment(out *ptr); // can also pass null to ref/out even in safe } It's probably going to be a hard sell to change the behavior of out now as well. It'd break quite a bit of code I think, did a search through druntime and phobos and quite a few functions use it. Maybe user code uses it less, I know I never use it.
Jan 24 2019
On Thu, Jan 24, 2019 at 3:50 PM Rubn via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Thursday, 24 January 2019 at 23:18:11 UTC, kinke wrote:I think any issues with `out` are tangential though. `out` does 'work' right now, and it's a valid way to address the concern with respect to one broad use case for ref. Theoretically, `out` is the right solution for that particular class of calling contexts, and any further issued with `out` should be taken as a separate issue/discussion.Proposed `out` semantics: --- void increment(out long value) { ++value; } increment(out value); --- vs. pointer version with current `out` semantics: --- void increment(long* pValue) { ++(*pValue); } increment(&value); --- The pointer workaround is both ugly (C) and unsafe (you can pass null).safe void safestFunction() { int* ptr; increment(out *ptr); // can also pass null to ref/out even in safe } It's probably going to be a hard sell to change the behavior of out now as well. It'd break quite a bit of code I think, did a search through druntime and phobos and quite a few functions use it. Maybe user code uses it less, I know I never use it.
Jan 24 2019
On 1/24/2019 12:01 PM, kinke wrote:`out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...)The compiler should elide the default-initializations before the call (although it currently does not). The 'out' comes from IDL (Interface Definition Language) and by using out parameters it is directly convertible to/from IDL. Even earlier, 'out' comes from Ada (!) From an efficiency standpoint, having the initialization occur in the function is better than all that duplicated initialization code in all the callers.
Jan 24 2019
On 1/24/19 3:01 PM, kinke wrote:On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:It seems to me that a proposal adding the " rvalue" attribute in function signatures to each parameter that would accept either an rvalue or an lvalue would be easy to argue. - No exposing existing APIs to wrong uses - The function's writer makes the decision ("I'm fine with this function taking an rvalue") - Appears in the function's documentation - Syntax is light and localized where it belongs - Scales well with number of parameters - Transparent to callers Whether existing keyword combinations ("in", "out", "ref" etc) could be used is a secondary point. The advantage is there's a simple and clear path forward for API definition and use. AndreiWe discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and make the side effect clearly visible. I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)
Jan 28 2019
On 1/28/19 1:00 PM, Andrei Alexandrescu wrote:On 1/24/19 3:01 PM, kinke wrote:One more thought. The main danger is restricted to a specific conversion: lvalue of type T is converted to ref of type U. That way both the caller and the function writer believe the value gets updated, when in fact it doesn't. Consider: real modf(real x, ref real i); Stores integral part in i, returns the fractional part. At this point there are two liabilities: 1. User passes the wrong parameter type: double integral; double frac = modf(x, integral); // oops, integral is always NaN The function silently converts integral from double to real and passes the resulting temporary into the function. The temporary is filled and lost, leaving user's value unchanged. 2. The API gets changed: // Fine, let's use double real modf(real x, ref double i); At this point all correct callers are silently broken - everybody who correctly used a real for the integral part now has their call broken (real implicitly converts to a double temporary, and the change does not propagate to the user's value). (If the example looks familiar it may be because of https://dlang.org/library/std/math/modf.html.) So it seems that the real problem is that the participants wrongly believe an lvalue is updated. But let's say the caller genuinely doesn't care about the integral part. To do so is awkward: real unused; double frac = modf(x, unused); That code isn't any better or less dangerous than: double frac = modf(x, double()); Here the user created willingly created an unnamed temporary of type double. Given that there's no doubt the user is not interested in that value after the call, the compiler could (in a proposed semantics) allow the conversion of the unnamed temporary to ref. TL;DR: it could be argued that the only dangerous conversions are lvalue -> temp rvalue -> ref, so only disable those. The conversion rvalue -> temp rvalue -> ref is not dangerous because the starting value on the caller side could not be inspected after the call anyway. AndreiOn Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:It seems to me that a proposal adding the " rvalue" attribute in function signatures to each parameter that would accept either an rvalue or an lvalue would be easy to argue. - No exposing existing APIs to wrong uses - The function's writer makes the decision ("I'm fine with this function taking an rvalue") - Appears in the function's documentation - Syntax is light and localized where it belongs - Scales well with number of parameters - Transparent to callers Whether existing keyword combinations ("in", "out", "ref" etc) could be used is a secondary point. The advantage is there's a simple and clear path forward for API definition and use. AndreiWe discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an explicitly required `out` at the call I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)
Jan 28 2019
On 1/28/19 2:58 PM, Andrei Alexandrescu wrote:TL;DR: it could be argued that the only dangerous conversions are lvalue -> temp rvalue -> ref, so only disable those. The conversion rvalue -> temp rvalue -> ref is not dangerous because the starting value on the caller side could not be inspected after the call anyway.I agree with you. It's one thing for the caller to pass in an rvalue that can clearly no longer be accessed. It's another thing to pass in an lvalue and have it "update" a temporary instead. I already see this kind of bug all the time with alias this. -Steve
Jan 28 2019
On 1/28/19 5:23 PM, Steven Schveighoffer wrote:I already see this kind of bug all the time with alias this.Can you please post more detail? It may be of relevance to future work.
Jan 28 2019
On 1/28/19 9:15 PM, Andrei Alexandrescu wrote:On 1/28/19 5:23 PM, Steven Schveighoffer wrote:Any time you have the alias this, then you can get confused when calling a function expecting it to be sent as the original type, but the alias this is used instead. In cases where both are seemingly accepted by overloads, then it can be confusing which overload is used. Sometimes it's as simple as the overload that you were expecting to be called won't compile or is deselected by constraints. But the code still compiles and runs, just does something different from what you expected. I meant that the effect is similar to what you were noting. -SteveI already see this kind of bug all the time with alias this.Can you please post more detail? It may be of relevance to future work.
Jan 28 2019
On Mon, Jan 28, 2019 at 12:00 PM Andrei Alexandrescu via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/28/19 1:00 PM, Andrei Alexandrescu wrote:I started reading this post, and I was compelled to reply with this same response, and then I realised you got there yourself. I understand your concern, and it has actually been discussed lightly, but regardless, you'll find that the issue you describe is not suggested anywhere in this DIP. This DIP is about passing rvalues to ref... so the issue you describe passing lvalues to ref does not apply here. There is no suggestion to change lvalue rules anywhere in this DIP.On 1/24/19 3:01 PM, kinke wrote:One more thought. The main danger is restricted to a specific conversion: lvalue of type T is converted to ref of type U. That way both the caller and the function writer believe the value gets updated, when in fact it doesn't. Consider: real modf(real x, ref real i); Stores integral part in i, returns the fractional part. At this point there are two liabilities: 1. User passes the wrong parameter type: double integral; double frac = modf(x, integral); // oops, integral is always NaN The function silently converts integral from double to real and passes the resulting temporary into the function. The temporary is filled and lost, leaving user's value unchanged. 2. The API gets changed: // Fine, let's use double real modf(real x, ref double i); At this point all correct callers are silently broken - everybody who correctly used a real for the integral part now has their call broken (real implicitly converts to a double temporary, and the change does not propagate to the user's value). (If the example looks familiar it may be because of https://dlang.org/library/std/math/modf.html.) So it seems that the real problem is that the participants wrongly believe an lvalue is updated. But let's say the caller genuinely doesn't care about the integral part. To do so is awkward: real unused; double frac = modf(x, unused); That code isn't any better or less dangerous than: double frac = modf(x, double()); Here the user created willingly created an unnamed temporary of type double. Given that there's no doubt the user is not interested in that value after the call, the compiler could (in a proposed semantics) allow the conversion of the unnamed temporary to ref. TL;DR: it could be argued that the only dangerous conversions are lvalue -> temp rvalue -> ref, so only disable those. The conversion rvalue -> temp rvalue -> ref is not dangerous because the starting value on the caller side could not be inspected after the call anyway.On Thursday, 24 January 2019 at 09:49:14 UTC, Manu wrote:It seems to me that a proposal adding the " rvalue" attribute in function signatures to each parameter that would accept either an rvalue or an lvalue would be easy to argue. - No exposing existing APIs to wrong uses - The function's writer makes the decision ("I'm fine with this function taking an rvalue") - Appears in the function's documentation - Syntax is light and localized where it belongs - Scales well with number of parameters - Transparent to callers Whether existing keyword combinations ("in", "out", "ref" etc) could be used is a secondary point. The advantage is there's a simple and clear path forward for API definition and use. AndreiWe discussed and concluded that one mechanism to mitigate this issue was already readily available, and it's just that 'out' gains a much greater sense of identity (which is actually a positive side-effect if you ask me!). You have a stronger motivation to use 'out' appropriately, because it can issue compile errors if you accidentally supply an rvalue.`out` with current semantics cannot be used as drop-in replacement for shared in-/output ref params, as `out` params are default-initialized on entry. Ignoring backwards compatibility for a second, I think getting rid of that would actually be beneficial (most args are probably already default-initialized by the callee in the line above the call...) - and I'd prefer an explicitly required `out` at the call I'd have otherwise proposed a ` noRVal` param UDA, but redefining `out` is too tempting indeed. ;)
Jan 28 2019
On 1/29/19 1:01 AM, Manu wrote:This DIP is about passing rvalues to ref... so the issue you describe passing lvalues to ref does not apply here. There is no suggestion to change lvalue rules anywhere in this DIP.The problem is with rvalues resulting as temporaries from lvalues. As in: void bump(ref int x) { ++x; } short y = 42; bump(y); assert(y == 43); // oops This is a smoking gun. If DIP 1016 does not propose allowing the code above, it has caused a gross misunderstanding. Similarly, you mention in a different response:4. "Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[]" <-- Do you mean "... with any T[] rvalue ..."? What would be the aim of that call? Can you suggest a particularly sinister construction?If your intent was to NOT allow rvalues resulting from conversions, it didn't come through at all. On the contrary, some examples suggest DIP 1016 _does_ allow it, as in this example: ======== This inconvenience extends broadly to every manner of rvalue passed to functions, including: ... fun(my_short); // implicit type conversions (ie, short->int promotion) ======== The reader assumes since the DIP characterizes passing a short lvalue to a ref int is an inconvenience, the DIP has set out to resolve it. The lowering rules proposed by DIP 1006 prescribe the following: void bump(ref int x) { ++x; } short y = 42; bump(y); ===> void bump(ref int x) { ++x; } short y = 42; { int __temp0 = y; bump(__temp0); } So... yes, lvalues do (undesirably) get converted to rvalues of a different type according to the DIP. Are you sure the DIP expresses what you are trying to accomplish? Andrei
Jan 29 2019
On Monday, 28 January 2019 at 19:58:24 UTC, Andrei Alexandrescu wrote:On 1/28/19 1:00 PM, Andrei Alexandrescu wrote:The inverse, the norval attribute, would feature all but the first advantages, at least once people get used to normal ref allowing (some) rvalues too. I can't think of many good reasons for a callee to disallow rvalues (with matching type at least); your example wrt. interlockedIncrement() is a good one, as synchronization overhead clearly isn't required for an rvalue. But my usages of (mutable) ref are >99% of the time something like: Header readHeader(ref Stream stream) { // read from & advance stream, return parsed header } where I'd like to be able put everything into one line if trivial (stream not required later on): readHeader(Stream("file")).doSomething(); (And const ref is mostly used for performance with bigger structs, where rvalues are absolutely fine). TLDR: I'd guess that I'd need to type ` rvalue` about 1000 times as often as ` norval`. I'm not kidding.It seems to me that a proposal adding the " rvalue" attribute in function signatures to each parameter that would accept either an rvalue or an lvalue would be easy to argue. - No exposing existing APIs to wrong uses - The function's writer makes the decision ("I'm fine with this function taking an rvalue") - Appears in the function's documentation - Syntax is light and localized where it belongs - Scales well with number of parameters - Transparent to callersTL;DR: it could be argued that the only dangerous conversions are lvalue -> temp rvalue -> ref, so only disable those. The conversion rvalue -> temp rvalue -> ref is not dangerous because the starting value on the caller side could not be inspected after the call anyway.I agree that the DIP needs to be clearer wrt. rvalues resulting from implicit conversions. I also agree with the above point. I'm just not sure we need to allow implicit conversions from rvalue argument expressions at all. To minimize the changes for overload resolution, it might be enough to allow rvalue argument expressions of matching type only, i.e., the types which are allowed for current ref semantics. After all, I don't plan to use the proposed ref semantics for primitive types where that might come in handy (something silly like `modf(x, 123.456)` if the second param is a `ref real`), and explicit casts would IMO be an acceptable price to pay for safety and visibility in the remaining cases.
Jan 30 2019
On Thursday, 24 January 2019 at 09:24:19 UTC, Nicholas Wilson wrote:On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Why isn't it covered by disable ?Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdvoid atomicIncrement(ref shared long x); atomicIncrement(myInt);Raises a good point, not covered by disable where the intent is to modify it and modifying a temporary is wrong. `out ref` perhaps?
Jan 24 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md[Reasons]. As such, they suggest that a new DIP be drafted from scratch, and that a stronger proposal would have a better chance of acceptance.Given the simplicity of the fixes for the problems identified I really don't think thats the best way forward. One round of forum review + formal reassessment ought to be enough.
Jan 24 2019
On Thursday, 24 January 2019 at 09:47:46 UTC, Nicholas Wilson wrote:Given the simplicity of the fixes for the problems identified I really don't think thats the best way forward. One round of forum review + formal reassessment ought to be enough.Yes, please! Let not restart the whole dip process for this! The DIP is very lengthy as it currently is, and just encourage the notation of "where good work goes to die" if this have to go through the process all over again. -Alex
Jan 24 2019
Hooray! Who didn't see that coming 10 years off! I understand the criticism, the most critical issue seems relatively trivial and easy to patch (initialisation order), but this comment is a problem for me "However, this proposal does not maintain the distinction and instead conflates the two cases". It's not like that's an accident, or I didn't think of this. The whole point of this proposal is to lift an arbitrary restriction, thereby achieving the 2 use cases stated on the opening paragraph, and increasing language uniformity by removing edges. "Maintaining the distinction" assumes an approach that is not lifting one restriction and thereby creating greater uniformity in the language, but rather, creating 2 more distinct sources of rules, and that's not attractive as a solution space at all. We've talked about solutions to this problem that involve adding more rules extensively, and they're not satisfying. So it's not because I'm an idiot that I conflated them, it's because that's literally the point. Then there's this one: "The first problem the Language Maintainers identified with this approach is that the rewrite is from an expression to a statement, rendering it invalid" And then present this snippet: ``` fun(10) ==> { T __temp0 = void; fun(__temp0 := 10); } ``` I'm upset by this comment, because it's almost like a deliberate misrepresentation... Not only did I never suggest anywhere that the intent is to translate an expression into a statement, but the following examples demonstrate why it's expanded that way with more complex compound statements, ie: ``` void fun(ref int x, ref int y); int gun(ref int x); fun(10, gun(20)); // <- clearly a statement ``` But even the simplest form introduced first (and presented as evidence) DOES HAVE A SEMICOLON on the end in my DIP, but removed from the review? So I can't agree with this comment: "The expression should be rewritten as an expression to clarify how it behaves in larger expressions."; this whole thing is about statements, not expressions. Misunderstanding that is to misunderstand the proposal entirely. So my feeling is that this initial point "First, reasoning about the proposed semantics is futile, as they are built upon a flawed foundation" is just plain invalid, the suggestion I'm talking about expressions is incorrect. And that invalidates this second point "Second,reasoning about the consequences of potentially revised semantics is equivalent to considering a new DIP." I think it's possible to consider this DIP, but the suggestion is that something other than what I wrote is what has been considered. Anyway, I can only blame myself for somehow failing to communicate these details, although I don't know how I managed that. The criticism about initialisation ordering is absolutely fair, and I suspect it requires a small amount of tweaking (perhaps initialising eagerly, or rather just expanding the code and ordering/sequencing the expansion a little more carefully), to address the throwing-an-exception mid-statement issue, and not an entirely different DIP written by someone competent like Timon. I think all of that is mostly just fluff to lend weight to this critical sentence "The Language Maintainers insist that any proposal allowing ref parameters to accept rvalue references must clearly define how functions which make use of ref for side effects will not accept rvalues." And that's 'just like, your opinion man'. A key point is for functions that do side-effects to receive rvalues too, and this was arrived at after much community discussion. We identified real cases where we *want* functions that receive rvalues to do side-effects, specifically, UFCS chains are frequently broken by need to capture an lvalue mid-steam. Pipeline programming is a big deal, and improving that experience was a motivator for this proposal. This process is pretty unsatisfying, because it ships off to a black-box committee, who were apparently able to misunderstand the substance of the proposal and then not seek clarification, and despite the only legitimate issue from my perspective being easily corrected, it's been suggested to start a whole new DIP. On Wed, Jan 23, 2019 at 11:20 PM Mike Parker via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.md
Jan 24 2019
On 1/24/2019 1:31 AM, Manu wrote:This process is pretty unsatisfying, because it ships off to a black-box committee, who were apparently able to misunderstand the substance of the proposal and then not seek clarification, and despite the only legitimate issue from my perspective being easily corrected, it's been suggested to start a whole new DIP.It's no problem if you want to rework the existing text, just submit it as a new DIP.
Jan 24 2019
On Thu, Jan 24, 2019 at 3:45 PM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/24/2019 1:31 AM, Manu wrote:This process has a long and deep pipe, why should it be a new DIP? There's nothing from the rejection text that would motivate me to change any words... Is it that you reject it 'in principle'? If so, there's nothing I can ever do about that. This took a substantial amount of my life, and you could have sought clarification, or a revision before a rejection. The rejection appears to be premised by misunderstanding more than anything, and a one very real (but isolated) technical issue that I believe can be corrected readily enough without affecting the surrounding text. The only improvement I could make is to better fold the discussion from the community review into the core text, but it's not like that digest wasn't already right there during consideration. I have no idea how you guys managed to edit and re-frame my DIP as applying to expressions? You removed the semicolons from the statements, and then told me I had no idea what I was doing, mixing expressions with statements that way... why did you do that?This process is pretty unsatisfying, because it ships off to a black-box committee, who were apparently able to misunderstand the substance of the proposal and then not seek clarification, and despite the only legitimate issue from my perspective being easily corrected, it's been suggested to start a whole new DIP.It's no problem if you want to rework the existing text, just submit it as a new DIP.
Jan 24 2019
No, it is not rejected in principle. Finding serious errors in it on the eve of approval is disappointing, and is not auspicious for being in a hurry to approve it. For example, I spent a lot of time working on ARC, and was all set to move forward with it when Timon stepped in and showed it was fundamentally memory unsafe. I couldn't come up with a reasonable solution. I'm grateful that Timon saved me from much further wasted work and embarrassment. Rvalue references are not a simple problem (although they appear to be). I do believe the problems with it are solvable, but it is a bit unfair to the implementor to dump an incomplete spec on him and have him fill in the gaps. The statement thing is a "do what I meant, not what I wrote" example, and DIPs need to be better than that. You're leaving him to design where the temporaries go, where the gates go, and ensure everything is properly exception safe. I know it's frustrating for you, but it's worse if an rvalue-ref implementation is implemented, shipped, and heralded, and then turns out to be very broken.
Jan 24 2019
On Friday, 25 January 2019 at 07:33:02 UTC, Walter Bright wrote:No, it is not rejected in principle.Good.Finding serious errors in it on the eve of approval is disappointing, and is not auspicious for being in a hurry to approve it.Praytell, what serious errors? Also you should heed your own advice DIP1017 and send it back to draft.Rvalue references are not a simple problem (although they appear to be).Please, do enlighten us..The statement thing is a "do what I meant, not what I wrote" example,_You_ removed the semicolons. The DIP applying to statements was at worst implied by their use, and frankly quite obvious, how else could they transcend multiple expressions?You're leaving him to design where the temporaries go, where the gates go, and ensure everything is properly exception safe.You could have at least had the decency to notify Manu. As noted elsewhere this problem is nowhere even close to DIP breaking, and I'm certain he could have resolved that.
Jan 25 2019
On Thu, Jan 24, 2019 at 11:35 PM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:No, it is not rejected in principle. Finding serious errors in it on the eve of approval is disappointing, and is not auspicious for being in a hurry to approve it.I'm very clearly NOT in a hurry here. We've been sitting on this for 10 years. What's weird is that you closed the door on iteration, and instead suggested I should write a new one with someone competent. More strangely, part of your feedback is broken, it appears you've reviewed and made assessment against code and text that's just not there, and you've neglected to respond to those points several times now. The error you found seems entirely revision-worthy rather than rejection-worthy. Your comments about treating expressions as statements is just wrong, and from that point on where you've mis-interpreted something so fundamental to the DIP, I don't think it's possible to trust any outcome from your 'formal assessment'. I appreciate that you identified the exception issue, we'll fix it, but I think you need to reconsider the formal rejection.but it is a bit unfair to the implementor to dump an incomplete spec on him and have him fill in the gapsHow is that the intent? We can get the rewrite semantics right with an iteration. So is it rejected on that premise? I don't understand how re-reading it with satisfactory rewrite logic going to change your assessment of the DIP in general? No surrounding text would change, and assuming that the rewrite is corrected, then do you just find a different reason to reject it? If so, then that needs to be the reason for the rejection, and not the error in the rewrite. I presume the real reason for rejection is this part: "They say that with the current semantics, this function only operates on long values as it should. With the proposed semantics, the call will accept all shared integral types. Any similar proposal must address this hole in order to be accepted." But to make that criticism is to miss the point entirely. The point is to do the thing you say needs to be addressed... and a whole bunch of techniques to motivate the compiler to emit desirable compile errors can be deployed in various circumstances. None of them are particularly unpleasant or awkward. TL;DR: use `out`, use `*`, use disable, use const. These can and should all be deployed appropriately anyway. Is that the reason it was rejected? If so, then I can't fix that by rewriting the DIP, that *is* the DIP. If you're not persuaded by the advantages, and that (rather extensive) set of tools to mitigate the side effects you're concerned about, then that's really more of an opinion than a technical rejection. I guess you're entitled to say "I don't like it, and I reject it because I don't like it", but you have to *say* that, and not make up some other stuff.The statement thing is a "do what I meant, not what I wrote" example, and DIPs need to be better than that. You're leaving him to design where the temporaries go, where the gates go, and ensure everything is properly exception safe.I agree, we'll fix the temporaries; but getting that correct is a revision surely. There's no spec to change there. The criticism talking about rewriting expressions as statements is still mysterious to me. I don't understand how a rejection can be presented based on an incorrect reading of the DIP... and how am I supposed to accept the rejection text containing those criticisms when the criticisms don't address what's written? You had to change the code (removing the semicolon from the statement) to make the claim that I was rewriting expressions as statements, and I honestly have no idea why you did that? Anyway...
Jan 25 2019
On Friday, 25 January 2019 at 07:33:02 UTC, Walter Bright wrote:No, it is not rejected in principle. Finding serious errors in it on the eve of approval is disappointing, and is not auspicious for being in a hurry to approve it. For example, I spent a lot of time working on ARC, and was all set to move forward with it when Timon stepped in and showed it was fundamentally memory unsafe. I couldn't come up with a reasonable solution. I'm grateful that Timon saved me from much further wasted work and embarrassment. Rvalue references are not a simple problem (although they appear to be). I do believe the problems with it are solvable, but it is a bit unfair to the implementor to dump an incomplete spec on him and have him fill in the gaps. The statement thing is a "do what I meant, not what I wrote" example, and DIPs need to be better than that. You're leaving him to design where the temporaries go, where the gates go, and ensure everything is properly exception safe. I know it's frustrating for you, but it's worse if an rvalue-ref implementation is implemented, shipped, and heralded, and then turns out to be very broken.Bit ironic considering how some DIPs are being handled. Changes for dip 1000 are already being integrated in Phobos yet the dip is still only a draft and looks like part of it still needs to be rewritten. If you needed to iron out details by implementing it, then that should have been done on your own fork like everyone else instead of adding experimental code into master.
Jan 25 2019
On Thursday, 24 January 2019 at 23:43:21 UTC, Walter Bright wrote:It's no problem if you want to rework the existing text, just submit it as a new DIP.And wait for another 180+ days for a fix? Come on dude, can you understand the frustration being display here?
Jan 24 2019
On 1/24/2019 4:31 PM, 12345swordy wrote:And wait for another 180+ days for a fix? Come on dude, can you understand the frustration being display here?Of course it's frustrating. On the other hand, we've had a lot of problems stemming from implementing features without thoroughly understanding them. Rvalue references have a lot of subtleties to them, and we should not rush into it, especially since these issues only turned up at the last minute.
Jan 24 2019
On Thu, Jan 24, 2019 at 6:35 PM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/24/2019 4:31 PM, 12345swordy wrote:Which issues? The initialization order issue? That's relatively trivial, isolated, and doesn't change the substance of the proposal in any way (unless a working rewrite is impossible, which I'm confident is not the case). The rest of your criticisms certainly did not 'turn up at last minute', they were extensively discussed, and discussion material is available, and present in the community review summary. And then there's the weird expression vs statement comments, which are bizarre, because you literally had to modify my code snippets (removing the semicolons) to read it that way... I can't accept that feedback, that just demonstrates a mis-reading of the DIP. If the DIP could be misunderstood that way, then that's surely revision-worthy, not throw-it-out-and-start-over worthy, and it has nothing to say about the substance of the design.And wait for another 180+ days for a fix? Come on dude, can you understand the frustration being display here?Of course it's frustrating. On the other hand, we've had a lot of problems stemming from implementing features without thoroughly understanding them. Rvalue references have a lot of subtleties to them, and we should not rush into it, especially since these issues only turned up at the last minute.
Jan 24 2019
On Thu, Jan 24, 2019 at 6:35 PM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/24/2019 4:31 PM, 12345swordy wrote:"Rush"? We've literally been debating this since my first post on this forum... like, 10 years ago. It's the issue I specifically joined this forum to complain about.And wait for another 180+ days for a fix? Come on dude, can you understand the frustration being display here?Of course it's frustrating. On the other hand, we've had a lot of problems stemming from implementing features without thoroughly understanding them. Rvalue references have a lot of subtleties to them, and we should not rush into it, especially since these issues only turned up at the last minute.
Jan 24 2019
On Friday, 25 January 2019 at 00:31:50 UTC, 12345swordy wrote:On Thursday, 24 January 2019 at 23:43:21 UTC, Walter Bright wrote:This will be discussed at DConf at the DLF meeting, which is only 75ish days, along with all other DIPs that are not yet implemented, under review (incl. draft) or recently rejected and a whole host of other topics, so that we can finally get some process direction and vision happening.It's no problem if you want to rework the existing text, just submit it as a new DIP.And wait for another 180+ days for a fix? Come on dude, can you understand the frustration being display here?
Jan 24 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Couldn't that just be rewritten as something like fun(tuple(10).expand); ?fun(10) ==> { T __temp0 = void; fun(__temp0 := 10); }The first problem the Language Maintainers identified with this approach is that the rewrite is from an expression to a statement, rendering it invalid. The expression should be rewritten as an expression to clarify how it behaves in larger expressions.
Jan 24 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdWelp, one of the features of the implicit convertion dip that I am going to write depend on this, and it is quite disapointed to see that it is rejected without giving manu a chance for clarification and providing a solution without restarting the dip process again.
Jan 24 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdHere, the DIP Author clearly expresses two reasons why a programmer may choose to declare a function to accept ref arguments. The Language Maintainers see this as the core of the proposal and would expect the distinction between the two cases to be maintained throughout. However, this proposal does not maintain the distinction and instead conflates the two cases. The Language Maintainers insist that any proposal allowing ref parameters to accept rvalue references must clearly define how functions which make use of ref for side effects will not accept rvalues.Sorry, but that's just irrelevant / missing the point. On Thursday, 24 January 2019 at 09:31:41 UTC, Manu wrote:Hooray! Who didn't see that coming 10 years off!I didn't see that coming and I'm deeply frustrated and disappointed by this review and rejection.
Jan 24 2019
On 1/24/2019 4:21 PM, Elie Morisse wrote:I didn't see that coming and I'm deeply frustrated and disappointed by this review and rejection.On the contrary. It is good to find conceptual errors before implementing it. They're a LOT cheaper to fix earlier rather than when it is in the field.
Jan 24 2019
On Friday, 25 January 2019 at 07:02:47 UTC, Walter Bright wrote:On 1/24/2019 4:21 PM, Elie Morisse wrote:You mean the conceptual errors you made when reviewing it? Like: That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by disable. That the DIP applies to statements, not expressions. That the construction order issue is trivially fixable, by specifying the same behaviour as the non ref case modulo ref.I didn't see that coming and I'm deeply frustrated and disappointed by this review and rejection.On the contrary. It is good to find conceptual errors before implementing it.
Jan 24 2019
On 1/24/2019 11:53 PM, Nicholas Wilson wrote:That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by disable.The first oddity about disable is it is attached to the foo(int), not the foo(ref int). If I wanted to know if foo(ref int) takes rvalue references, I'd have to go looking for the existence of another function. This is one of those cases where it's hard to prove a negative, as other functions can be introduced by mixins. This is a strong usability negative. Next, the disable applies to the entire parameter list. However, overload selection is done by looking at each parameter. The DIP says: "The DIP author responded that ideas to improve this are welcome, but that he cannot imagine a use case." I can guarantee that the use case of more than one reference parameter will come up. The workarounds the DIP suggests are simply awful. Let's look at what C++ does for rvalue references: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html The syntax is attached to the parameter declaration of the function it applies to, not some other function, and not every parameter: int foo(T&& t); // C++ rvalue ref There are no weird workarounds, at least for that aspect. There are indeed unlikable things about the C++ rules, but the DIP needs to pay more attention to how C++ does this, and justify why D differs. Particularly because D will likely have to have some mechanism of ABI compatibility with C++ functions that take rvalue references. This is not a small problem. A further problem is implicit conversions, which the DIP ignores by only talking about ints. void bar(int); void foo(ref int); enum short s = 10; bar(s); // compiles foo(s); // currently fails to compile Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)? Here's a discussion of Rust and rvalue references which may offer insight: https://www.reddit.com/r/rust/comments/3ko5pm/explaining_cs_rvalue_references_from_a_rust/That the DIP applies to statements, not expressions.The DIP should not invent its own syntax, give no explanation of it, and have the reader guess. (It did explain the :=, but not the use of { } and statements.) And, even if one did a mental rewrite, the semantics of the statement version are simply wrong. (For example, if 'fun' was actually a function pointer returned by another function, and that other function threw an exception - then the destructor would be run on an uninitialized variable.)That the construction order issue is trivially fixable, by specifying the same behaviour as the non ref case modulo ref.It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters. In the past I've done a lot of work on exception safety, and it isn't trivial once one goes beyond trivial cases. All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work.
Jan 25 2019
On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote:It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters.The pot calling the kettle black. DIP1000? DIP1017? Again, all that requires is a minor revision. It is not DIP breaking.
Jan 25 2019
On Friday, 25 January 2019 at 12:03:36 UTC, Nicholas Wilson wrote:On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote:Or DIP1008: https://issues.dlang.org/show_bug.cgi?id=19463It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters.The pot calling the kettle black. DIP1000? DIP1017?
Jan 25 2019
On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote:All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work.I haven't participated to writing this DIP, but I personally appreciate this level of feedback. I think it would have been more appreciated if the original answer had that level of detail. Otherwise, I don't think this should be an all-or-nothing situation. It would make sense to bump the DIP back one stage or two for minor adjustments, and if the author decide that they need to make major changes to get past the problems you mention, require these changes to go through the entire process again (through another DIP).
Jan 25 2019
On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote:On 1/24/2019 11:53 PM, Nicholas Wilson wrote:For future reference, this is what a formal review should be. I'd also rather your exact words than some summarization of them.That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by disable.The first oddity about disable is it is attached to the foo(int), not the foo(ref int). If I wanted to know if foo(ref int) takes rvalue references, I'd have to go looking for the existence of another function. This is one of those cases where it's hard to prove a negative, as other functions can be introduced by mixins. This is a strong usability negative. Next, the disable applies to the entire parameter list. However, overload selection is done by looking at each parameter. The DIP says: "The DIP author responded that ideas to improve this are welcome, but that he cannot imagine a use case." I can guarantee that the use case of more than one reference parameter will come up. The workarounds the DIP suggests are simply awful. Let's look at what C++ does for rvalue references: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html The syntax is attached to the parameter declaration of the function it applies to, not some other function, and not every parameter: int foo(T&& t); // C++ rvalue ref There are no weird workarounds, at least for that aspect. There are indeed unlikable things about the C++ rules, but the DIP needs to pay more attention to how C++ does this, and justify why D differs. Particularly because D will likely have to have some mechanism of ABI compatibility with C++ functions that take rvalue references. This is not a small problem. A further problem is implicit conversions, which the DIP ignores by only talking about ints. void bar(int); void foo(ref int); enum short s = 10; bar(s); // compiles foo(s); // currently fails to compile Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)? Here's a discussion of Rust and rvalue references which may offer insight: https://www.reddit.com/r/rust/comments/3ko5pm/explaining_cs_rvalue_references_from_a_rust/That the DIP applies to statements, not expressions.The DIP should not invent its own syntax, give no explanation of it, and have the reader guess. (It did explain the :=, but not the use of { } and statements.) And, even if one did a mental rewrite, the semantics of the statement version are simply wrong. (For example, if 'fun' was actually a function pointer returned by another function, and that other function threw an exception - then the destructor would be run on an uninitialized variable.)That the construction order issue is trivially fixable, by specifying the same behaviour as the non ref case modulo ref.It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters. In the past I've done a lot of work on exception safety, and it isn't trivial once one goes beyond trivial cases. All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work.
Jan 25 2019
On 26/01/2019 10:00 AM, Rubn wrote:On Friday, 25 January 2019 at 11:56:58 UTC, Walter Bright wrote:So in other words, a formal review should include somebody acting as an 'interviewer' prompting questions. If that is the case, I do think it would be a good idea.On 1/24/2019 11:53 PM, Nicholas Wilson wrote:For future reference, this is what a formal review should be. I'd also rather your exact words than some summarization of them.That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by disable.The first oddity about disable is it is attached to the foo(int), not the foo(ref int). If I wanted to know if foo(ref int) takes rvalue references, I'd have to go looking for the existence of another function. This is one of those cases where it's hard to prove a negative, as other functions can be introduced by mixins. This is a strong usability negative. Next, the disable applies to the entire parameter list. However, overload selection is done by looking at each parameter. The DIP says: "The DIP author responded that ideas to improve this are welcome, but that he cannot imagine a use case." I can guarantee that the use case of more than one reference parameter will come up. The workarounds the DIP suggests are simply awful. Let's look at what C++ does for rvalue references: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html The syntax is attached to the parameter declaration of the function it applies to, not some other function, and not every parameter: int foo(T&& t); // C++ rvalue ref There are no weird workarounds, at least for that aspect. There are indeed unlikable things about the C++ rules, but the DIP needs to pay more attention to how C++ does this, and justify why D differs. Particularly because D will likely have to have some mechanism of ABI compatibility with C++ functions that take rvalue references. This is not a small problem. A further problem is implicit conversions, which the DIP ignores by only talking about ints. void bar(int); void foo(ref int); enum short s = 10; bar(s); // compiles foo(s); // currently fails to compile Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)? Here's a discussion of Rust and rvalue references which may offer insight: https://www.reddit.com/r/rust/comments/3ko5pm/explaining_cs_rvalue_ref rences_from_a_rust/That the DIP applies to statements, not expressions.The DIP should not invent its own syntax, give no explanation of it, and have the reader guess. (It did explain the :=, but not the use of { } and statements.) And, even if one did a mental rewrite, the semantics of the statement version are simply wrong. (For example, if 'fun' was actually a function pointer returned by another function, and that other function threw an exception - then the destructor would be run on an uninitialized variable.)That the construction order issue is trivially fixable, by specifying the same behaviour as the non ref case modulo ref.It should never have gotten this far without giving a precise explanation of how exception safety is achieved when faced with multiple parameters. In the past I've done a lot of work on exception safety, and it isn't trivial once one goes beyond trivial cases. All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work.
Jan 25 2019
On Fri, Jan 25, 2019 at 4:00 AM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/24/2019 11:53 PM, Nicholas Wilson wrote:And right here, I can see our fundamental difference of perspective... I never said anything about 'rvalue references', and I never meant anything like that; at least, not in the C++ sense, which you seem to be alluding to. In C++, rval references are syntactically distinct and identifiable as such, for the purposes of implementing move semantics. If we want to talk about "rvalue references", then we need to be having a *completely* different conversation. That said, I'm not sure why you've raised this matter, since it's not written anywhere in the DIP. What I'm talking about is "not-rvalue-references accepting rvalues", which if you want to transpose into C++ terms, is like `const T&`.That the conflation of pass by reference to avoid copying and mutation is not only deliberate but also mitigated by disable.The first oddity about disable is it is attached to the foo(int), not the foo(ref int). If I wanted to know if foo(ref int) takes rvalue references,There are indeed unlikable things about the C++ rules, but the DIP needs to pay more attention to how C++ does this, and justify why D differs. Particularly because D will likely have to have some mechanism of ABI compatibility with C++ functions that take rvalue references.I'm not paying attention to C++ T&& rules, because this DIP has nothing to do with T&&, and there would be no allusion to connecting this to a T&& method. Again, I find that to be a very interesting topic of conversation, but it has nothing to do with this DIP.[...] Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)?As per the DIP; yes, that is the point. The text you seek is written: "[...]. The user should not experience edge cases, or differences in functionality when calling fun(int x) vs fun(ref int x)." That text appears at least 2 times through the document as the stated goal. Don't accept naked ref unless you want these semantics. There is a suite of tools offered to use where this behaviour is undesirable. Naked `ref` doesn't do anything particularly interesting in the language today that's not *identical* semantically to using a pointer and adding a single '&' character at the callsite. This DIP attempts to make `ref` interesting and useful as a feature in its own right. In discussions designing this thing, I've come to appreciate the UFCS advantages as the most compelling opportunity, among all the other things that burn me almost practically every time I write D code.The DIP should not invent its own syntaxI removed it, and replaced it with simpler code (that I think is exception-correct) in my prior post here. It's also a super-trivial amendment.It should never have gotten this far without giving a precise explanation of howexception safety is achieved when faced with multiple parameters. I apologise. I've never used exceptions in any code I've ever written, so it's pretty easy for me to overlook that detail. Nobody else that did the community reviews flagged it, and that includes you and Andrei, as members of the community.All that criticism aside, I'd like to see rvalue references in D. But the DIP needs significant work.This is *NOT* an "rvalue-references" DIP; this is a "references" DIP. If you want to see an rvalue references DIP, I agree that's a completely different development, and it's also interesting to me... I had *absolutely no idea* that an rvalue-references DIP was welcome. I thought D was somewhat aggressively proud of the fact that we don't have rvalue-references... apparently I took the wrong impression. That said, this remains infinitely more important to me than an rvalue-references DIP. It's been killing me for 10 years, and I'm personally yet to feel hindered by our lack of rvalue-reference support.
Jan 25 2019
On 1/25/2019 7:44 PM, Manu wrote:I never said anything about 'rvalue references',The DIP mentions them several times in the "forum threads" section. I see you want to distinguish the DIP from that; I recommend a section clearing that up. However, my points about the serious problems with disable syntax remain. A section comparing with the C++ solution is necessary as well, more than the one sentence dismissal. For example, how C++ deals with the: void foo(const int x); void foo(const int& x); situation needs to be understood and compared. Failing to understand it can lead to serious oversights. For example, C++ doesn't require an disable syntax to make it work.I don't see how that addresses implicit type conversion at all.[...] Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)?As per the DIP; yes, that is the point. The text you seek is written: "[...]. The user should not experience edge cases, or differences in functionality when calling fun(int x) vs fun(ref int x)."Don't accept naked ref unless you want these semantics. There is a suite of tools offered to use where this behaviour is undesirable. Naked `ref` doesn't do anything particularly interesting in the language today that's not *identical* semantically to using a pointer and adding a single '&' character at the callsite.It's not good enough. The DIP needs to specifically address what happens with implicit conversions. The reader should not be left wondering about what is implied. I often read a spec and think yeah, yeah, of course it must be that way. But it is spelled out in the spec, and reading it gives me confidence that I'm understanding the semantics, and it gives me confidence that whoever wrote the spec understood it. (Of course, writing out the implications sometimes causes the writer to realize he didn't actually understand it at all.) Furthermore, D has these match levels: 1. exact 2. const 3. conversion 4. no match If there are two or more matches at the same level, the decision is made based on partial ordering. How does adding the new ref/value overloading fit into that?It's so, so easy to get that wrong. C++ benefits from decades of compiler bug fixes with that.It should never have gotten this far without giving a precise explanation of howexception safety is achieved when faced with multiple parameters. I apologise. I've never used exceptions in any code I've ever written, so it's pretty easy for me to overlook that detail.Nobody else that did the community reviews flagged it,That's unfortunately right. Note that 'alias this' was approved and implemented, and then multiple serious conceptual problems have appeared with it. I don't want a repeat of that.and that includes you and Andrei, as members of the community.The idea was that Andrei & I wouldn't get too involved in the DIPs until they are vetted by the community. I.e. delegation.That said, this remains infinitely more important to me than an rvalue-references DIP. It's been killing me for 10 years, and I'm personally yet to feel hindered by our lack of rvalue-reference support.I look forward to a much improved DIP from you (and anyone else who wishes to help you out with the work!).
Jan 25 2019
On Saturday, 26 January 2019 at 06:15:22 UTC, Walter Bright wrote:On 1/25/2019 7:44 PM, Manu wrote:Anything that could be implicitly converted to use foo(int) can be implicitly converted to pass a ref to the temporary that was implicitly converted to int into foo(ref int). No rules change in this regard. If you don't see how this address type conversion perhaps a code sample might help? The one that was given with short: void foo(ref int); void bar(int); bar( short(10) ); // is ok foo( short(10) ); // expected to be ok short->int ; ref to temp passed to foo Just as bar(int) can be passed a short(10), foo(ref int) can be passed a reference to the temporary that was created as well.I never said anything about 'rvalue references',The DIP mentions them several times in the "forum threads" section. I see you want to distinguish the DIP from that; I recommend a section clearing that up. However, my points about the serious problems with disable syntax remain. A section comparing with the C++ solution is necessary as well, more than the one sentence dismissal. For example, how C++ deals with the: void foo(const int x); void foo(const int& x); situation needs to be understood and compared. Failing to understand it can lead to serious oversights. For example, C++ doesn't require an disable syntax to make it work.I don't see how that addresses implicit type conversion at all.[...] Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)?As per the DIP; yes, that is the point. The text you seek is written: "[...]. The user should not experience edge cases, or differences in functionality when calling fun(int x) vs fun(ref int x)."The DIP goes over this, though not in a lot of detail. All the same rules apply as with the current implementation. Where there would be a compiler error trying to pass an rvalue would instead forward the value. Effectively what is being implemented is the following (for type matching only): void foo( ref int ); void foo( int value ) { foo( value ); } Anything that would have been passed to foo(int) is passed to foo(ref int) as a reference to a temporary instead. No rules are changed in this regard for matching, all the same rules apply (as stated in the DIP). It's pretty clear, unless you can give a specific problem faced where this doesn't hold? D is pretty strict to ensure rvalues aren't passed to ref's and that's what makes this relatively simple to implement without changing matching rules.Don't accept naked ref unless you want these semantics. There is a suite of tools offered to use where this behaviour is undesirable. Naked `ref` doesn't do anything particularly interesting in the language today that's not *identical* semantically to using a pointer and adding a single '&' character at the callsite.It's not good enough. The DIP needs to specifically address what happens with implicit conversions. The reader should not be left wondering about what is implied. I often read a spec and think yeah, yeah, of course it must be that way. But it is spelled out in the spec, and reading it gives me confidence that I'm understanding the semantics, and it gives me confidence that whoever wrote the spec understood it. (Of course, writing out the implications sometimes causes the writer to realize he didn't actually understand it at all.) Furthermore, D has these match levels: 1. exact 2. const 3. conversion 4. no match If there are two or more matches at the same level, the decision is made based on partial ordering. How does adding the new ref/value overloading fit into that?
Jan 26 2019
On 1/26/2019 8:28 AM, Rubn wrote:[...]The point is, the DIP needs to spell this out in an organized and complete fashion, like any proper spec does. We all want a better specified language, let's make it happen.
Jan 26 2019
On Fri, Jan 25, 2019 at 10:20 PM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/25/2019 7:44 PM, Manu wrote:I think the ` disable` semantic is correct; I understand your criticism that you have to search for the negative to understand the restruction, but that perspective arises presumably from a presumption that you want to explicity state inclusion, which is the opposite of the intent. The goal is to state exclusion, we are *adding* restrictions (ie, removing potential calls) from the default more-inclusive behaviour, and from that perspective, ` disable` is in the proper place.I never said anything about 'rvalue references',The DIP mentions them several times in the "forum threads" section. I see you want to distinguish the DIP from that; I recommend a section clearing that up. However, my points about the serious problems with disable syntax remain.A section comparing with the C++ solution is necessary as well, more than the one sentence dismissal. For example, how C++ deals with the: void foo(const int x); void foo(const int& x); situation needs to be understood and compared. Failing to understand it can lead to serious oversights. For example, C++ doesn't require an disable syntax to make it work.C++ doesn't desire a disable semantic, it just works as described in this DIP. Eg: ```c++ void fun(const int& x) {} void test() { fun(10); fun(short(10)); // <- no problem! } ``` It's the dlang critics of this functionality that demand explicit controls on functions accepting one kind or the other. I personally see no value in all that noise, but I added it in due to popular demand.It explicitly permits it as one of the goals of the DIP. Uniformity in function calling is one of the main goals here.I don't see how that addresses implicit type conversion at all.[...] Should `s` be promoted to an int temporary, then pass the temporary by reference? I can find no guidance in the DIP. What if `s` is a uint (i.e. the implicit conversion is a type paint and involves no temporary)?As per the DIP; yes, that is the point. The text you seek is written: "[...]. The user should not experience edge cases, or differences in functionality when calling fun(int x) vs fun(ref int x)."As I said above, it couldn't be stated more clearly in the DIP; it is very explicitly permitted, and stated that "the user should not experience any difference in calling semantics when using ref".Don't accept naked ref unless you want these semantics. There is a suite of tools offered to use where this behaviour is undesirable. Naked `ref` doesn't do anything particularly interesting in the language today that's not *identical* semantically to using a pointer and adding a single '&' character at the callsite.It's not good enough. The DIP needs to specifically address what happens with implicit conversions. The reader should not be left wondering about what is implied.I often read a spec and think yeah, yeah, of course it must be that way. But it is spelled out in the spec, and reading it gives me confidence that I'm understanding the semantics, and it gives me confidence that whoever wrote the spec understood it.Okay, but it is spelled out. How could I make it clearer?(Of course, writing out the implications sometimes causes the writer to realize he didn't actually understand it at all.) Furthermore, D has these match levels: 1. exact 2. const 3. conversion 4. no match If there are two or more matches at the same level, the decision is made based on partial ordering. How does adding the new ref/value overloading fit into that?I haven't described this well. I can try and improve this. Where can I find these existing rules detailed comprehensively? I have never seen them mentioned in the dlang language reference. It's hard for me to speak in these terms, when I've never seen any text in the language spec that does so. Note; this criticism was nowhere to be found in your rejection text, and it would have been trivial during community reviews to make this note. I feel like this is a mostly simple revision to make.I think my revision is water-tight (up a few posts). If the new rewrite were written by hand and had some problem there, then there's a serious problem with core language.It's so, so easy to get that wrong. C++ benefits from decades of compiler bug fixes with that.It should never have gotten this far without giving a precise explanation of howexception safety is achieved when faced with multiple parameters. I apologise. I've never used exceptions in any code I've ever written, so it's pretty easy for me to overlook that detail.I couldn't have used D to solve so many problems over the years without `alias this`. If we refuse struct inheritence, then without `alias this`, we're screwed.Nobody else that did the community reviews flagged it,That's unfortunately right. Note that 'alias this' was approved and implemented, and then multiple serious conceptual problems have appeared with it. I don't want a repeat of that.You're still members of the community. If something slipped through community review that you could have easily spotted, then the strategy you describe is no good. If you had have spotted it 4-5 months ago, I would have wasted a LOT less time... and now you're asking me to sign up for another few hundred days of do-over from square-1. I'd suggest that feedback at community-review stage would have been extremely valuable.and that includes you and Andrei, as members of the community.The idea was that Andrei & I wouldn't get too involved in the DIPs until they are vetted by the community. I.e. delegation.That's good, but telling me to start over rather than revise is unbelievably annoying and de-motivating. I'd like you and Andrei to revise the response at the end of the rejected DIP to remove the text that is erroneous. It's not fair to present a rejection where a good portion of the detailed reasoning is just plain wrong. By my reading, there will only be one paragraph left, and then the motivation for a hard rejection will look very different.That said, this remains infinitely more important to me than an rvalue-references DIP. It's been killing me for 10 years, and I'm personally yet to feel hindered by our lack of rvalue-reference support.I look forward to a much improved DIP from you (and anyone else who wishes to help you out with the work!).
Jan 28 2019
On 1/28/2019 10:10 PM, Manu wrote:It's where it should be, under "Function Overloading": https://dlang.org/spec/function.html#function-overloadingFurthermore, D has these match levels: 1. exact 2. const 3. conversion 4. no match If there are two or more matches at the same level, the decision is made based on partial ordering. How does adding the new ref/value overloading fit into that?I haven't described this well. I can try and improve this. Where can I find these existing rules detailed comprehensively? I have never seen them mentioned in the dlang language reference.If you had have spotted it 4-5 months ago, I would have wasted a LOT less time... and now you're asking me to sign up for another few hundred days of do-over from square-1.It's pretty clear that just writing the DIP myself will take me less time than point-by-point endlessly arguing about it. But it will take more calendar time, because all this stuff falls to me. It's why I get paid the big bucks.
Jan 29 2019
On Fri, Jan 25, 2019 at 7:44 PM Manu <turkeyman gmail.com> wrote:On Fri, Jan 25, 2019 at 4:00 AM Walter Bright via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:Incidentally, the reason I invented a syntax in this DIP, was because we have no initialisation syntax in D, despite the language clearly having the ability to initialise values (when they're declared); we have an amazingly complex and awkward library implementation of `emplace`, which is pretty embarrassing really. The fact that I needed to invent a syntax to perform an initialisation is a very serious problem in its own right. But forget about that; I removed the need to express initialisation from the rewrite.The DIP should not invent its own syntaxI removed it, and replaced it with simpler code (that I think is exception-correct) in my prior post here. It's also a super-trivial amendment.
Jan 25 2019
On 1/24/19 2:18 AM, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdHi everyone, I've followed the responses to this, some conveying frustration about the decision and some about the review process itself. As the person who carried a significant part of the review, allow me to share a few thoughts of possible interest. * Fundamentally: a DIP should stand on its own and be judged on its own merit, regardless of rhetoric surrounding it, unstated assumptions, or trends of opinion in the forums. There has been a bit of material in this forum discussion that should have been argued properly as a part of the DIP itself. * The misinterpretation of the rewrite (expression -> statement vs. statement -> statement) is mine, apologies. (It does not influence our decision and should not be construed as an essential aspect of the review.) The mistake was caused by the informality of the DIP, which shows rewrites as a few simplistic examples instead of a general rewrite rule. Function calls are expressions, so I naturally assumed the path would be to start with the function call expression. Formulating a general rule as a statement rewrite is possible but not easy and fraught with peril, as discussion in this thread has shown. I very much recommend going the expression route (e.g. with the help of lambdas) because that makes it very easy to expand to arbitrarily complex expressions involving function calls. Clarifying what temporaries get names and when in a complex expression is considerably more difficult (probably not impossible but why suffer). * Arguments of the form: "You say DIP 1016 is bad, but look at how bad DIP XYZ is!" are great when directed at the poor quality of DIP XYZ. They are NOT good arguments in favor of DIP 1016. * Arguments of the form "Functions that take ref parameters just for changing them are really niche anyway" should be properly made in the DIP, not in the forums and assumed without stating in the DIP. Again, what's being evaluated is "DIP" not "DIP + surrounding rhetoric". A good argument would be e.g. analyzing a number of libraries and assess that e.g. 91% uses of ref is for efficiency purposes, 3% is unclear, and only 6% is for side-effect purpose. All preexisting code using ref parameters written under the current rule assumes that only lvalues will be bound to them. A subset of these functions take by ref for changing them only. The DIP should explain why that's not a problem, or if it is one it is a small problem, etc. My point is - the DIP should _approach_ the matter and build an argument about it. One more example from preexisting code for illustration, from the standard library: // in the allocators API bool expand(ref void[] b, size_t delta); bool reallocate(ref void[] b, size_t s); These primitives modify their first argument in essential ways. The intent is to fill b with the new slice resulted after expansion/reallocation. Under the current rules, calling these primitives is cumbersome, but usefully so because the processing done requires extra care if typed data is being reallocated. Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[], passing the temporary to expand/reallocate, then return as if all is well - yet the original slice has not been changed. The DIP should create a salient argument regarding these situations (and not only this example, but the entire class). It could perhaps argue that: - Such code is bad to start with, and should not have been written. - Such code is so rare, we can take the hit. We then have a recommendation for library writers on how to amend their codebase (use disable or some other mechanisms). - The advantages greatly outweigh this problem. - The bugs caused are minor easy to find. - ... Point being: the matter, again should be _addressed_ by the DIP. * Regarding our recommendation that the proposal is resubmited as a distinct DIP as opposed to a patch on the existing DIP: this was not embracing bureaucracy. Instead, we considered that the DIP was too poor to be easily modified into a strong proposal, and recommended that it be rewritten simply because it would be easier and would engender a stronger DIP. * Regarding the argument "why not make this an iterative process where concerns are raised and incrementally addressed?" We modeled the DIP process after similar processes - conference papers, journal papers, proposals in other languages. There is a proposal by one or more responsibles, perfected by a community review, and submitted for review. This encourages building a strong proposal - as strong as can be - prior to submission. Washing that down to a negotiation between the proposers and the reviewers leads to a "worst acceptable proposal" state of affairs in which proposers are incentivized to submit the least-effort proposal, reactively change it as issues are raised by reviewers. As anyone who has submitted a conference paper, that's not how it works, and even if the process is highly frustrating (yes, reviewers in so many cases misunderstand parts of the paper...) it does lead to strong work. There are cases in which papers are "accepted with amends" - those are strong submissions that have a few issues that are easily fixable. With apologies, we do not consider this DIP to be in that category. This result was frustrating and disheartening on our side, too: a stronger DIP should have resulted after all these years. I encourage interested people to make a DIP that is scientifically-argued, clearly formalized, and provides a thorough analysis of the consequences of the proposed design. Hope this helps, Andrei
Jan 28 2019
On Mon, Jan 28, 2019 at 9:25 AM Andrei Alexandrescu via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/24/19 2:18 AM, Mike Parker wrote:A few things in here... 1. All of this is more useful criticism than the official and final criticism affixed to the rejection, which when revised to remove the incorrect criticisms, is basically left with the text "The Language Maintainers found other issues with the proposal, most of which may have been remedied through simple revision" 2. All of this criticism could have been given at any point in the past half a year or so prior to submission, and that would have been appreciated, rather than wasting our time. 3. "It does not influence our decision and should not be construed as an essential aspect of the review" <-- Then why did it feature as one of just 3 core criticism in the rejection text? And supplied as one of the 2 reasons that could not "have been remedied through simple revision". 4. "Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[]" <-- Do you mean "... with any T[] rvalue ..."? What would be the aim of that call? Can you suggest a particularly sinister construction? 5. "and recommended that it be rewritten simply because it would be easier and would engender a stronger DIP." <-- I wrote the DIP I wrote... your official feedback affixed to the bottom of the DIP was pretty much entirely unhelpful, almost offensively so. I would just write the same DIP if I started again. I genuinely hope someone can be bothered to do this. After 10 years on this, I think I'm over it. 6. "This result was frustrating and disheartening on our side, too: a stronger DIP ..." <-- I'm sorry you hated it. You could have reviewed it at any point, made suggestions at any point, written it yourself, or encouraged someone competent to do it. 7. Your general tone is superior, as usual. If it's so terrible as you say, after having survived community treatment and approval, then I suggest there's a serious problem in process. As you've already made clear, I'm not qualified to address this.Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language. They are not opposed to the feature in principle and suggested that a proposal that closes those holes and covers all the bases will have a higher chance of getting accepted. You can read a summary of the Formal Assessment at the bottom of the document: https://github.com/dlang/DIPs/blob/master/DIPs/rejected/DIP1016.mdHi everyone, I've followed the responses to this, some conveying frustration about the decision and some about the review process itself. As the person who carried a significant part of the review, allow me to share a few thoughts of possible interest. * Fundamentally: a DIP should stand on its own and be judged on its own merit, regardless of rhetoric surrounding it, unstated assumptions, or trends of opinion in the forums. There has been a bit of material in this forum discussion that should have been argued properly as a part of the DIP itself. * The misinterpretation of the rewrite (expression -> statement vs. statement -> statement) is mine, apologies. (It does not influence our decision and should not be construed as an essential aspect of the review.) The mistake was caused by the informality of the DIP, which shows rewrites as a few simplistic examples instead of a general rewrite rule. Function calls are expressions, so I naturally assumed the path would be to start with the function call expression. Formulating a general rule as a statement rewrite is possible but not easy and fraught with peril, as discussion in this thread has shown. I very much recommend going the expression route (e.g. with the help of lambdas) because that makes it very easy to expand to arbitrarily complex expressions involving function calls. Clarifying what temporaries get names and when in a complex expression is considerably more difficult (probably not impossible but why suffer). * Arguments of the form: "You say DIP 1016 is bad, but look at how bad DIP XYZ is!" are great when directed at the poor quality of DIP XYZ. They are NOT good arguments in favor of DIP 1016. * Arguments of the form "Functions that take ref parameters just for changing them are really niche anyway" should be properly made in the DIP, not in the forums and assumed without stating in the DIP. Again, what's being evaluated is "DIP" not "DIP + surrounding rhetoric". A good argument would be e.g. analyzing a number of libraries and assess that e.g. 91% uses of ref is for efficiency purposes, 3% is unclear, and only 6% is for side-effect purpose. All preexisting code using ref parameters written under the current rule assumes that only lvalues will be bound to them. A subset of these functions take by ref for changing them only. The DIP should explain why that's not a problem, or if it is one it is a small problem, etc. My point is - the DIP should _approach_ the matter and build an argument about it. One more example from preexisting code for illustration, from the standard library: // in the allocators API bool expand(ref void[] b, size_t delta); bool reallocate(ref void[] b, size_t s); These primitives modify their first argument in essential ways. The intent is to fill b with the new slice resulted after expansion/reallocation. Under the current rules, calling these primitives is cumbersome, but usefully so because the processing done requires extra care if typed data is being reallocated. Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[], passing the temporary to expand/reallocate, then return as if all is well - yet the original slice has not been changed. The DIP should create a salient argument regarding these situations (and not only this example, but the entire class). It could perhaps argue that: - Such code is bad to start with, and should not have been written. - Such code is so rare, we can take the hit. We then have a recommendation for library writers on how to amend their codebase (use disable or some other mechanisms). - The advantages greatly outweigh this problem. - The bugs caused are minor easy to find. - ... Point being: the matter, again should be _addressed_ by the DIP. * Regarding our recommendation that the proposal is resubmited as a distinct DIP as opposed to a patch on the existing DIP: this was not embracing bureaucracy. Instead, we considered that the DIP was too poor to be easily modified into a strong proposal, and recommended that it be rewritten simply because it would be easier and would engender a stronger DIP. * Regarding the argument "why not make this an iterative process where concerns are raised and incrementally addressed?" We modeled the DIP process after similar processes - conference papers, journal papers, proposals in other languages. There is a proposal by one or more responsibles, perfected by a community review, and submitted for review. This encourages building a strong proposal - as strong as can be - prior to submission. Washing that down to a negotiation between the proposers and the reviewers leads to a "worst acceptable proposal" state of affairs in which proposers are incentivized to submit the least-effort proposal, reactively change it as issues are raised by reviewers. As anyone who has submitted a conference paper, that's not how it works, and even if the process is highly frustrating (yes, reviewers in so many cases misunderstand parts of the paper...) it does lead to strong work. There are cases in which papers are "accepted with amends" - those are strong submissions that have a few issues that are easily fixable. With apologies, we do not consider this DIP to be in that category. This result was frustrating and disheartening on our side, too: a stronger DIP should have resulted after all these years. I encourage interested people to make a DIP that is scientifically-argued, clearly formalized, and provides a thorough analysis of the consequences of the proposed design. Hope this helps, AndreiHope this helps,Sure.
Jan 29 2019
On Tuesday, 29 January 2019 at 08:35:11 UTC, Manu wrote:4. "Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[]" <-- Do you mean "... with any T[] rvalue ..."? What would be the aim of that call? Can you suggest a particularly sinister construction?I _think_ what is meant is: void[] allocate(size_t size); bool reallocate(ref void[] b, size_t s); void deallocate(ref void[]); T[] arr = allocate(42); arr.reallocate(8192); // reallocates a temporary as T[] is cast to void arr.deallocate(); // double free
Jan 29 2019
On 1/29/19 6:38 AM, Nicholas Wilson wrote:On Tuesday, 29 January 2019 at 08:35:11 UTC, Manu wrote:Affirmative. (Just wrote about the same in another post). Thanks very much. Andrei4. "Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[]" <-- Do you mean "... with any T[] rvalue ..."? What would be the aim of that call? Can you suggest a particularly sinister construction?I _think_ what is meant is: void[] allocate(size_t size); bool reallocate(ref void[] b, size_t s); void deallocate(ref void[]); T[] arr = allocate(42); arr.reallocate(8192); // reallocates a temporary as T[] is cast to void arr.deallocate(); // double free
Jan 29 2019
On 1/29/19 3:35 AM, Manu wrote:1. All of this is more useful criticism than the official and final criticism affixed to the rejection, which when revised to remove the incorrect criticisms, is basically left with the text "The Language Maintainers found other issues with the proposal, most of which may have been remedied through simple revision"No. This is a nonnegotiable matter: void bump(ref int x) { ++x; } short y = 42; bump(y); assert(y == 43); // fails The code above should not compile. The DIP allows it to compile. We didn't see how the DIP can be lightly edited to fix this problem, so we recommended a complete rethinking and rewrite.2. All of this criticism could have been given at any point in the past half a year or so prior to submission, and that would have been appreciated, rather than wasting our time.We have given this criticism ever since ten years ago when you brought the matter up. Literally every time, including in person. Whenever you said "why not bind rvalues to ref?" we religiously replied with (paraphrased of course) "We are worried about rvalues resulting from implicit conversion of lvalues. You'd need to find a solution to that." It is truly remarkable that DIP 1016 provides not only a solution to the problem, but almost neglects to mention it.3. "It does not influence our decision and should not be construed as an essential aspect of the review" <-- Then why did it feature as one of just 3 core criticism in the rejection text? And supplied as one of the 2 reasons that could not "have been remedied through simple revision".If the matter above can be resolve through simple revision that would be great. I don't think it can, but would love to be proven wrong!4. "Under DIP 1016, a call with any T[] will silently "succeed" by converting the slice to void[]" <-- Do you mean "... with any T[] rvalue ..."? What would be the aim of that call? Can you suggest a particularly sinister construction?I am talking about this: int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); if (alloc.reallocate(a, 200 * int.sizeof) { assert(a.length == 200); } By applying the lowering rules in the DIP (including your pending revision), the code is lowered to: int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); void[] __temp0 = a; if (alloc.reallocate(__temp0, 200 * int.sizeof) { assert(a.length == 200); } The code is far from sinister. But the assertion will fail. I want to make sure there is mutual understanding that: 1. DIP 1016 proposes this semantics 2. We do not accept this semantics Even if there is no agreement with (2), these are facts that I want to make sure are understood by everyone involved - you, us, the rest of the community.5. "and recommended that it be rewritten simply because it would be easier and would engender a stronger DIP." <-- I wrote the DIP I wrote... your official feedback affixed to the bottom of the DIP was pretty much entirely unhelpful, almost offensively so. I would just write the same DIP if I started again. I genuinely hope someone can be bothered to do this. After 10 years on this, I think I'm over it.I am sorry you found the review unfit for your needs. I thought it puts the main matter plain and simple: we do not accept code like this: void bump(ref int x) { ++x; } short y = 42; bump(y); assert(y == 43); // fails Honest, I don't think you have spent 10 years on this DIP. It is not thorough work and fails to address the main problem it was supposed to address: The one above.6. "This result was frustrating and disheartening on our side, too: a stronger DIP ..." <-- I'm sorry you hated it. You could have reviewed it at any point, made suggestions at any point, written it yourself, or encouraged someone competent to do it.We didn't hate it. We made suggestions literally every time you brought up the issue over the past decade. For my part, I have no idea what more input I could have provided. "Solve the rvalues coming from implicit conversions" is all feedback the DIP needs.7. Your general tone is superior, as usual.No need to attempt to make this personal, and claim the moral ground in the process. Nobody is after you. We all want the D language to become better. The DIP is not good. This is what it is. All is not lost - the DIP is a good inspiration for a couple of ideas that deserve investigation. It seems that allowing _only_ rvalues to be bound to ref may work. I'm cautious because there may be cases we didn't think of. Andrei
Jan 29 2019
On 1/29/19 6:45 AM, Andrei Alexandrescu wrote:It is truly remarkable that DIP 1016 provides not only a solution to the problem, but almost neglects to mention it.Meant "...not only no solution..."
Jan 29 2019
On 1/29/2019 3:45 AM, Andrei Alexandrescu wrote:I am talking about this: int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); if (alloc.reallocate(a, 200 * int.sizeof) { assert(a.length == 200); }Even simpler: void func(ref void* p) { free(p); // frees (1) p = malloc(100); // (2) } int* p = cast(int*)malloc(16); // (1) func(p); // p copied to temp for conversion to void* free(p); // frees (1) again // (2) is left dangling It's a memory corruption issue, with no way to detect it.
Jan 29 2019
On Tue., 29 Jan. 2019, 10:25 pm Walter Bright via Digitalmars-d-announce < digitalmars-d-announce puremagic.com wrote:On 1/29/2019 3:45 AM, Andrei Alexandrescu wrote:Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.I am talking about this: int[] a = cast(int[]) alloc.allocate(100 * int.sizeof); if (alloc.reallocate(a, 200 * int.sizeof) { assert(a.length == 200); }Even simpler: void func(ref void* p) { free(p); // frees (1) p = malloc(100); // (2) } int* p = cast(int*)malloc(16); // (1) func(p); // p copied to temp for conversion to void* free(p); // frees (1) again // (2) is left dangling It's a memory corruption issue, with no way to detect it.
Jan 30 2019
On Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.
Jan 30 2019
On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error). We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.
Jan 30 2019
On Wednesday, 30 January 2019 at 18:29:37 UTC, Manu wrote:On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:Because of the rewrite that your proposed in your dip. void fun(ref int x); fun(10); { T __temp0 = void; fun(__temp0 := 10); } lets replace 10 with a short variable named: S void fun(ref int x); fun(S) { T __temp0 = void; fun(__temp0 := S); } fun(__temp0 := S) This is where the cast is being attempted. As __temp0 is an integer type and S is a short typeOn Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted?Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.
Jan 30 2019
On Wed, Jan 30, 2019 at 12:40 PM 12345swordy via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Wednesday, 30 January 2019 at 18:29:37 UTC, Manu wrote:"a short variable named: S" is an lvalue, so why would the rewrite be attempted? S must be an rvalue for any rewrite to occur. We're talking about rvalues here.On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:Because of the rewrite that your proposed in your dip. void fun(ref int x); fun(10); { T __temp0 = void; fun(__temp0 := 10); } lets replace 10 with a short variable named: SOn Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted?Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.
Jan 30 2019
On 1/30/2019 5:55 PM, Manu wrote:This illustrates why this should be compared with C++. Consider this C++ code: const int& foo(const int& x) { return x; } const int& test() { short s; return foo(s); } It compiles with clang++. The code generated for test() is: push RBP mov RBP,RSP sub RSP,010h lea RDI,-8[RBP] movsx EAX,word ptr -2[RBP] mov -8[RBP],EAX call foo add RSP,010h pop RBP ret See what it is doing? It's converting s to an int, putting the int into a temporary, then passing a reference to that temporary to foo(). So when you ask why would a person think that this would happen with the DIP, if they know C++, they would assume similar behavior. This is why the DIP needs to specifically say this is not the proposed behavior. It is why a comparison to C++ behavior is essential. It is a lot easier to understand the DIP if people can apply their existing understanding, with a few modifications, to the D behavior. It's also necessary to compare with C++ to see if the DIP missed something important, and to justify any other behavioral differences. The interesting question is, since C++ supports this behavior, what about the truck-sized hole? The answer is in the declaration of foo(const int&). The const is the reason. The referenced value cannot be modified. The realloc example is blocked by the compiler. But the DIP says const ref is not required. Therefore, copying an lvalue to a temporary cannot be allowed, therefore implicit conversion of lvalues cannot happen. Then we're faced with the question of if implicit conversion of lvalues is not allowed, should implicit conversion of rvalues be allowed? I'm not so sure it should be. For one thing, a lot of people are confused about lvalues vs rvalues, and would find the difference in behavior puzzling. For another, it can complicate overloading rules. I'd say allowing the conversions needs a strong rationale.lets replace 10 with a short variable named: S"a short variable named: S" is an lvalue, so why would the rewrite be attempted? S must be an rvalue for any rewrite to occur. We're talking about rvalues here.
Jan 31 2019
On 1/31/19 3:25 AM, Walter Bright wrote:But the DIP says const ref is not required. Therefore, copying an lvalue to a temporary cannot be allowed, therefore implicit conversion of lvalues cannot happen.The biggest reason I see to not worry about const is that we already don't for member functions. And the world hasn't ended (yet).Then we're faced with the question of if implicit conversion of lvalues is not allowed, should implicit conversion of rvalues be allowed? I'm not so sure it should be. For one thing, a lot of people are confused about lvalues vs rvalues, and would find the difference in behavior puzzling. For another, it can complicate overloading rules. I'd say allowing the conversions needs a strong rationale.We could certainly start out with no conversions allowed (except you MUST allow conversions of compile-time data -- e.g. literals and CTFE produced values -- that is very key), and then relax the rules later if that's important. -Steve
Jan 31 2019
On 1/30/19 1:29 PM, Manu wrote:On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:That's exactly what lowerings are for: to precisely specify what should happen when the proposed construct is used. DIP 1016 proposes a lowering of the form: { T __temp0 = expr; fun(__temp0); } In the first step, an implicit conversion of an lvalue may take place.On Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...?Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error).Not according to DIP 1016. Here is an example pasted from it: ======== This inconvenience extends broadly to every manner of rvalue passed to functions, including: ... fun(my_short); // implicit type conversions (ie, short->int promotion) ======== Presumably my_short is a variable of type short. Is that correct? Again (this is not a rhetorical or sarcastic question): are you sure DIP 1016 expresses what you are trying to accomplish?We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.What we have here is: * DIP 1016 proposes a hole in the language one could drive a truck through. * The problem goes undetected in community review. * Its own author seems to not have an understanding of what the DIP proposes. Andrei
Jan 30 2019
On Wed, Jan 30, 2019 at 1:05 PM Andrei Alexandrescu via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/30/19 1:29 PM, Manu wrote:What? `expr` must be an rvalue... if it's not, then none of this applies.On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:That's exactly what lowerings are for: to precisely specify what should happen when the proposed construct is used. DIP 1016 proposes a lowering of the form: { T __temp0 = expr; fun(__temp0); } In the first step, an implicit conversion of an lvalue may take place.On Wed, 30 Jan 2019 09:15:36 -0800, Manu wrote:But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...?Why are you so stuck on this case? The DIP is about accepting rvalues, not lvalues... Calling with 'p', an lvalue, is not subject to this DIP.The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.Oh I see.Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error).Not according to DIP 1016. Here is an example pasted from it: ======== This inconvenience extends broadly to every manner of rvalue passed to functions, including: ... fun(my_short); // implicit type conversions (ie, short->int promotion) ========fun(short(10)); // implicit type conversions (ie, short->int promotion)I did not intend for this DIP to apply to anything other than rvalues. I can totally see how that's not clear. `my_short` should be an rvalue of some form, like the rest. Is that the only such line?Presumably my_short is a variable of type short. Is that correct?It is not. It should be an rvalue like everything else. Perhaps it's an enum... but I should write `short(10)`, that would be clear.Again (this is not a rhetorical or sarcastic question): are you sure DIP 1016 expresses what you are trying to accomplish?Fairly certain.I still can't see a truck-sized hole.We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.What we have here is: * DIP 1016 proposes a hole in the language one could drive a truck through.* The problem goes undetected in community review.I don't know how I could have influenced this outcome.* Its own author seems to not have an understanding of what the DIP proposes.More classy comments. I can't get enough of the way you belittle people. I made a 1-word error, where I should have written `short(10)` to be clear. 1-word error feels amendment-worthy, and not a call for "let's start over from scratch".
Jan 30 2019
On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:On Wed, Jan 30, 2019 at 1:05 PM Andrei Alexandrescu viaI think so.fun(my_short); // implicit type conversions (ie, short->int promotion) ========Oh I see.fun(short(10)); // implicit type conversions (ie, short->int promotion)I did not intend for this DIP to apply to anything other than rvalues. I can totally see how that's not clear. `my_short` should be an rvalue of some form, like the rest. Is that the only such line?It would.Presumably my_short is a variable of type short. Is that correct?It is not. It should be an rvalue like everything else. Perhaps it's an enum... but I should write `short(10)`, that would be clear.You should just PR it back to review with that fix and a note about how it lowers to statements (incl. an example of lambdification for if/while/for/switch statements (see https://forum.dlang.org/post/qysmnatmjquuhylaqumm forum.dlang.org ))* DIP 1016 proposes a hole in the language one could drive a truck through.I still can't see a truck-sized hole.* The problem goes undetected in community review.I don't know how I could have influenced this outcome.* Its own author seems to not have an understanding of what the DIP proposes.More classy comments. I can't get enough of the way you belittle people. I made a 1-word error, where I should have written `short(10)` to be clear. 1-word error feels amendment-worthy, and not a call for "let's start over from scratch".
Jan 30 2019
On Wed, Jan 30, 2019 at 7:05 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:I can't do that, it's been rejected, with mostly incorrect rejection text affixed to the bottom.On Wed, Jan 30, 2019 at 1:05 PM Andrei Alexandrescu viaI think so.fun(my_short); // implicit type conversions (ie, short->int promotion) ========Oh I see.fun(short(10)); // implicit type conversions (ie, short->int promotion)I did not intend for this DIP to apply to anything other than rvalues. I can totally see how that's not clear. `my_short` should be an rvalue of some form, like the rest. Is that the only such line?It would.Presumably my_short is a variable of type short. Is that correct?It is not. It should be an rvalue like everything else. Perhaps it's an enum... but I should write `short(10)`, that would be clear.You should just PR it back to review* DIP 1016 proposes a hole in the language one could drive a truck through.I still can't see a truck-sized hole.* The problem goes undetected in community review.I don't know how I could have influenced this outcome.* Its own author seems to not have an understanding of what the DIP proposes.More classy comments. I can't get enough of the way you belittle people. I made a 1-word error, where I should have written `short(10)` to be clear. 1-word error feels amendment-worthy, and not a call for "let's start over from scratch".with that fix and a note about how it lowers to statements (incl. an example of lambdification for if/while/for/switch statements (see https://forum.dlang.org/post/qysmnatmjquuhylaqumm forum.dlang.org ))I'm pretty sure that's not necessary. I haven't understood why this noise about expressions. This DIP applies to statements. I can't see how there's any problem with the lowering if the statement is a control statement? if (ref_fun(10)) { ... } ==> { int __tmp = 10; if (ref_fun(__tmp)) { ... } } What's the trouble?
Jan 30 2019
On 1/30/19 10:12 PM, Manu wrote:On Wed, Jan 30, 2019 at 7:05 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:The trouble is major. Replace "if" with "while": while (ref_fun(10)) { ... } ==> { int __tmp = 10; while (ref_fun(__tmp)) { ... } } That means ref_fun is called with the same lvalue multiple times. In all likelihood this is not what you want! A possible retort is: "Of course, while would not be lowered that way, but a slightly different way!" etc. The point is, ALL OF THAT must be in the DIP, not assumed obvious or clarified in informal discusson outside the DIP. Again: please be thorough, state your assumptions, cover all cases.On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:I can't do that, it's been rejected, with mostly incorrect rejection text affixed to the bottom.On Wed, Jan 30, 2019 at 1:05 PM Andrei Alexandrescu viaI think so.fun(my_short); // implicit type conversions (ie, short->int promotion) ========Oh I see.fun(short(10)); // implicit type conversions (ie, short->int promotion)I did not intend for this DIP to apply to anything other than rvalues. I can totally see how that's not clear. `my_short` should be an rvalue of some form, like the rest. Is that the only such line?It would.Presumably my_short is a variable of type short. Is that correct?It is not. It should be an rvalue like everything else. Perhaps it's an enum... but I should write `short(10)`, that would be clear.You should just PR it back to review* DIP 1016 proposes a hole in the language one could drive a truck through.I still can't see a truck-sized hole.* The problem goes undetected in community review.I don't know how I could have influenced this outcome.* Its own author seems to not have an understanding of what the DIP proposes.More classy comments. I can't get enough of the way you belittle people. I made a 1-word error, where I should have written `short(10)` to be clear. 1-word error feels amendment-worthy, and not a call for "let's start over from scratch".with that fix and a note about how it lowers to statements (incl. an example of lambdification for if/while/for/switch statements (see https://forum.dlang.org/post/qysmnatmjquuhylaqumm forum.dlang.org ))I'm pretty sure that's not necessary. I haven't understood why this noise about expressions. This DIP applies to statements. I can't see how there's any problem with the lowering if the statement is a control statement? if (ref_fun(10)) { ... } ==> { int __tmp = 10; if (ref_fun(__tmp)) { ... } } What's the trouble?
Jan 30 2019
On 1/31/19 2:26 AM, Andrei Alexandrescu wrote:The trouble is major. Replace "if" with "while": while (ref_fun(10)) { ... } ==> { int __tmp = 10; while (ref_fun(__tmp)) { ... } } That means ref_fun is called with the same lvalue multiple times. In all likelihood this is not what you want!Yes, the trouble specifically is loops. Because loops execute their internal expressions over and over again. Unfortunately, this means lowering isn't possible. That is, lowering to something expressible in the normal language isn't possible. However, we all know that loops are essentially "lowered" in the AST to simple ifs and gotos. We just need to operate at that level. So for instance the above looks something like this in AST: loop_continue: if(ref_fun(10)) { ... } else goto loop_end; goto loop_continue; loop_end: (I know I'm omitting a lot of extra stuff like scope cleanup, that is implied here, as I don't know the exact details). What needs to happen is the temporary (with extra scope)is inserted between the loop start and the if statement: loop_continue: { int __tmp = 10; if(ref_fun(__tmp)) { ... } else goto loop_end; } goto loop_continue; loop_end:A possible retort is: "Of course, while would not be lowered that way, but a slightly different way!" etc. The point is, ALL OF THAT must be in the DIP, not assumed obvious or clarified in informal discusson outside the DIP. Again: please be thorough, state your assumptions, cover all cases.Agree, this needs to handle all possible cases. Really I think loops are the only problems, foreach, for, and while/do..while A possible way forward is inventing a new syntax to allow declarations in this space, and then lowering can happen. Something similar to if(auto x = ...) -Steve
Jan 31 2019
On 1/30/19 9:10 PM, Manu wrote:You're right. I have deleted this post a few seconds after having sent it on account of that remark, but somehow it got resuscitated. Please accept my apologies.* Its own author seems to not have an understanding of what the DIP proposes.More classy comments. I can't get enough of the way you belittle people.
Jan 30 2019
On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:I still can't see a truck-sized hole.I don't know if it's truck-sized, but here's another corner case: int doubleMyValue(ref int x) { x *= 2; return x; } Point pt; pt.x = 5; pt.y = foobar(); doubleMyValue(pt.x); assert(pt.x == 10); Question: in the above code, will the assertion pass? Answer: it depends on Point's implementation. If x is a member variable, then yes. If it's a getter, then doubleMyValue will take a rvalue and x won't be mutated and the assertion will fail. I think this is a non-trivial conceptual problem.
Jan 31 2019
On 1/31/19 11:04 AM, Olivier FAURE wrote:On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:Yeah, that's already a thing that ref in D doesn't protect against: struct Point { private int _x, _y; ref int x() { return _x; } ref int y() { return _y; } } struct Rect { private Point _origin, _lengths; Point origin() { return _origin; } Point lengths() { return _lengths; } void origin(Point p) { _origin = p; } void lengths(Point p) { _lengths = p; } } Rect r; r.origin = Point(1, 2); r.lengths = Point(5, 5); doubleMyValue(r.lengths.x); assert(r.lengths.x == 10); // fail -SteveI still can't see a truck-sized hole.I don't know if it's truck-sized, but here's another corner case: int doubleMyValue(ref int x) { x *= 2; return x; } Point pt; pt.x = 5; pt.y = foobar(); doubleMyValue(pt.x); assert(pt.x == 10); Question: in the above code, will the assertion pass? Answer: it depends on Point's implementation. If x is a member variable, then yes. If it's a getter, then doubleMyValue will take a rvalue and x won't be mutated and the assertion will fail. I think this is a non-trivial conceptual problem.
Jan 31 2019
On 1/31/19 11:38 AM, Steven Schveighoffer wrote:On 1/31/19 11:04 AM, Olivier FAURE wrote:Affirmative. This discussion should be part of the revised DIP along with an assessment of its gravity. Goes the same with scope-level variables replaced with homonym functions that return rvalues. AndreiOn Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:Yeah, that's already a thing that ref in D doesn't protect against: struct Point { private int _x, _y; ref int x() { return _x; } ref int y() { return _y; } } struct Rect { private Point _origin, _lengths; Point origin() { return _origin; } Point lengths() { return _lengths; } void origin(Point p) { _origin = p; } void lengths(Point p) { _lengths = p; } } Rect r; r.origin = Point(1, 2); r.lengths = Point(5, 5); doubleMyValue(r.lengths.x); assert(r.lengths.x == 10); // fail -SteveI still can't see a truck-sized hole.I don't know if it's truck-sized, but here's another corner case: int doubleMyValue(ref int x) { x *= 2; return x; } Point pt; pt.x = 5; pt.y = foobar(); doubleMyValue(pt.x); assert(pt.x == 10); Question: in the above code, will the assertion pass? Answer: it depends on Point's implementation. If x is a member variable, then yes. If it's a getter, then doubleMyValue will take a rvalue and x won't be mutated and the assertion will fail. I think this is a non-trivial conceptual problem.
Jan 31 2019
On Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:Yeah, that's already a thing that ref in D doesn't protect against:It took me a while to understand what the compiler was doing. This really feels like something that shouldn't compile.
Jan 31 2019
On Thursday, 31 January 2019 at 21:42:04 UTC, Olivier FAURE wrote:[snip] It took me a while to understand what the compiler was doing. This really feels like something that shouldn't compile.It doesn't compile with dip1000 without first giving the getter functions a return attribute for this.
Jan 31 2019
On Thursday, 31 January 2019 at 21:44:53 UTC, jmh530 wrote:It doesn't compile with dip1000 without first giving the getter functions a return attribute for this.But it still compiles with -dip1000 once you give x() and y() return attributes, even though what's happening is clearly different from what the user wants (and the compiler has enough info to know that).
Jan 31 2019
On Thursday, 31 January 2019 at 21:50:19 UTC, Olivier FAURE wrote:On Thursday, 31 January 2019 at 21:44:53 UTC, jmh530 wrote:Agreed. I had checked that it didn't work and as I figured out how to get it work I got distracted reading the documentation and return and scope.It doesn't compile with dip1000 without first giving the getter functions a return attribute for this.But it still compiles with -dip1000 once you give x() and y() return attributes, even though what's happening is clearly different from what the user wants (and the compiler has enough info to know that).
Jan 31 2019
On 1/31/19 4:42 PM, Olivier FAURE wrote:On Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.Yeah, that's already a thing that ref in D doesn't protect against:It took me a while to understand what the compiler was doing. This really feels like something that shouldn't compile.
Jan 31 2019
On 1/31/2019 1:46 PM, Andrei Alexandrescu wrote:The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.That's why it's problematic to have a rule that rvalues can be implicitly converted, but not lvalues. There's not a hard line between lvalues and rvalues. For example, foreach(i; 0..2) { int[] a = [1, 2]; assert(a[0] == 1]); a[0] = 3; // will this cause the assert to fail? }
Jan 31 2019
On Thursday, 31 January 2019 at 22:00:10 UTC, Walter Bright wrote:On 1/31/2019 1:46 PM, Andrei Alexandrescu wrote:Why would it cause the assert to fail? A new array is constructed each loop.The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.That's why it's problematic to have a rule that rvalues can be implicitly converted, but not lvalues. There's not a hard line between lvalues and rvalues. For example, foreach(i; 0..2) { int[] a = [1, 2]; assert(a[0] == 1]); a[0] = 3; // will this cause the assert to fail? }
Jan 31 2019
On 1/31/19 4:46 PM, Andrei Alexandrescu wrote:On 1/31/19 4:42 PM, Olivier FAURE wrote:No, because those calls might actually do something with side effects! rvalues can contain other references. The only way to fix this would be to overload member functions on the lvalue-ness of `this`. I don't recommend this at all, as I see this as a weird but rare problem that doesn't affect most D code. -SteveOn Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:The proposal could actually disallow rvalues that have lvalue syntax, such as "symbol", "symbol[expr]", "symbol.symbol", "symbol.symbol[expr]", etc. Ugh. Gets hairy quickly.Yeah, that's already a thing that ref in D doesn't protect against:It took me a while to understand what the compiler was doing. This really feels like something that shouldn't compile.
Jan 31 2019
On 1/31/19 4:42 PM, Olivier FAURE wrote:On Thursday, 31 January 2019 at 16:38:42 UTC, Steven Schveighoffer wrote:The problem is that `this` is passed by reference EVEN for rvalues. Knowing this, you can construct difficult situations, but normally these don't appear in the wild. If you've ever tried to make a simple math wrapper type, you would see how this is weird. And it has been like this since the beginning of D2. You get things like: a + Foo(1); // error Foo(1) + a; // OK! That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem. -SteveYeah, that's already a thing that ref in D doesn't protect against:It took me a while to understand what the compiler was doing. This really feels like something that shouldn't compile.
Jan 31 2019
On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:[snip] That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.The way you put it makes it sound like a bug... I don't know if it helps, but below compiles without error. struct Foo { private int _x; int* x() { return &_x; } } struct Bar { private Foo _y; Foo* y() { return &_y; } void y(Foo foo) { _y = foo; } } void main() { Foo a = Foo(1); assert(*a.x == 1); *a.x *= 2; assert(*a.x == 2); Bar b; b.y = Foo(1); assert(*b.y.x == 1); *b.y.x *= 2; assert(*b.y.x == 2); }
Jan 31 2019
On Thu, Jan 31, 2019 at 10:26:39PM +0000, jmh530 via Digitalmars-d-announce wrote:On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:[...]Why is it a problem that this code compiles without error? T -- Perhaps the most widespread illusion is that if we were in power we would behave very differently from those who now hold it---when, in truth, in order to get power we would have to become very much like them. -- UnknownThat being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.The way you put it makes it sound like a bug... I don't know if it helps, but below compiles without error. struct Foo { private int _x; int* x() { return &_x; } } struct Bar { private Foo _y; Foo* y() { return &_y; } void y(Foo foo) { _y = foo; } } void main() { Foo a = Foo(1); assert(*a.x == 1); *a.x *= 2; assert(*a.x == 2); Bar b; b.y = Foo(1); assert(*b.y.x == 1); *b.y.x *= 2; assert(*b.y.x == 2); }
Jan 31 2019
On Thursday, 31 January 2019 at 22:35:26 UTC, H. S. Teoh wrote:On Thu, Jan 31, 2019 at 10:26:39PM +0000, jmh530 via Digitalmars-d-announce wrote:Sorry if I didn't really complete my thought. The code below corresponds to the ref version mentioned above and gets the same error originally reported. The only difference is that I use Foo instead of Foo* for the getter in Bar. So if you instead make that member function a ref function, then it also compiles without error (regardless of if you use the doubleMyValue function or not). So they were right that the issue is with the getter. However, you're not really protected in any way if you have a ref getter at one point in a chain and a non-ref getter somewhere else. Making all the getters auto ref also avoids the issue. struct Foo { private int _x; ref int x() { return _x; } } struct Bar { private Foo _y; Foo y() { return _y; } void y(Foo foo) { _y = foo; } } void main() { Foo a = Foo(1); assert(a.x == 1); a.x *= 2; assert(a.x == 2); Bar b; b.y = Foo(1); assert(b.y.x == 1); b.y.x *= 2; assert(b.y.x == 2); }On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:[...]Why is it a problem that this code compiles without error? TThat being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.The way you put it makes it sound like a bug... I don't know if it helps, but below compiles without error. struct Foo { private int _x; int* x() { return &_x; } } struct Bar { private Foo _y; Foo* y() { return &_y; } void y(Foo foo) { _y = foo; } } void main() { Foo a = Foo(1); assert(*a.x == 1); *a.x *= 2; assert(*a.x == 2); Bar b; b.y = Foo(1); assert(*b.y.x == 1); *b.y.x *= 2; assert(*b.y.x == 2); }
Jan 31 2019
On Thursday, 31 January 2019 at 21:57:21 UTC, Steven Schveighoffer wrote:That being said, you can look at the fact that most people don't even know about this problem, even seasoned veterans, as a sign that it's really not a big problem.Isn't it a recurring theme on this forum that D is really cool but also kind of obnoxious because of weird corner cases that veterans know, but aren't documented anywhere?
Feb 01 2019
On 1/31/19 11:04 AM, Olivier FAURE wrote:On Thursday, 31 January 2019 at 02:10:05 UTC, Manu wrote:BTW, the DIP discusses how to annotate these rare situations: int doubleMyValue(ref int x) { ... } disable int doubleMyValue(int x); -SteveI still can't see a truck-sized hole.I don't know if it's truck-sized, but here's another corner case: int doubleMyValue(ref int x) { x *= 2; return x; } Point pt; pt.x = 5; pt.y = foobar(); doubleMyValue(pt.x); assert(pt.x == 10); Question: in the above code, will the assertion pass? Answer: it depends on Point's implementation. If x is a member variable, then yes. If it's a getter, then doubleMyValue will take a rvalue and x won't be mutated and the assertion will fail. I think this is a non-trivial conceptual problem.
Jan 31 2019
On Thursday, 31 January 2019 at 18:31:22 UTC, Steven Schveighoffer wrote:BTW, the DIP discusses how to annotate these rare situations: int doubleMyValue(ref int x) { ... } disable int doubleMyValue(int x); -SteveI don't think that's a solution. The problem is in the getter method, not in doubleMyValue. If nothing else, since the DIP is designed to work on existing functions, it could happen on doubleMyValue functions which would be both designed by and used by people completely unaware of DIP-1016.
Jan 31 2019
On 1/31/19 4:46 PM, Olivier FAURE wrote:On Thursday, 31 January 2019 at 18:31:22 UTC, Steven Schveighoffer wrote:How is the problem not in doubleMyValue? It's sole purpose is to update an lvalue. It is the perfect candidate to mark with disable for rvalues. -SteveBTW, the DIP discusses how to annotate these rare situations: int doubleMyValue(ref int x) { ... } disable int doubleMyValue(int x);I don't think that's a solution. The problem is in the getter method, not in doubleMyValue. If nothing else, since the DIP is designed to work on existing functions, it could happen on doubleMyValue functions which would be both designed by and used by people completely unaware of DIP-1016.
Jan 31 2019
On Thursday, 31 January 2019 at 21:50:32 UTC, Steven Schveighoffer wrote:On 1/31/19 4:46 PM, Olivier FAURE wrote:Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else: { auto __temp = pt.x; doubleMyValue(__temp); pt.x = __temp; } do as well. Or something... a DIP to fix properties anyone? :) Also, this applies to a much wider variety of operations on properties that return rvalues and not just on functions calls no? struct B { int x; } struct A { B _b; property B b() { return _b; } } void main() { A a; a.b.x += 1; writeln(a.b.x); // 0 }On Thursday, 31 January 2019 at 18:31:22 UTC, Steven Schveighoffer wrote:How is the problem not in doubleMyValue? It's sole purpose is to update an lvalue. It is the perfect candidate to mark with disable for rvalues. -SteveBTW, the DIP discusses how to annotate these rare situations: int doubleMyValue(ref int x) { ... } disable int doubleMyValue(int x);I don't think that's a solution. The problem is in the getter method, not in doubleMyValue. If nothing else, since the DIP is designed to work on existing functions, it could happen on doubleMyValue functions which would be both designed by and used by people completely unaware of DIP-1016.
Feb 01 2019
On 01.02.19 10:10, aliak wrote:http://wilzbach.github.io/d-dip/DIP24 I'm not sure your rewrite is good though, because it does not preserve aliasing during the function call.Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else: { auto __temp = pt.x; doubleMyValue(__temp); pt.x = __temp; } well. Or something... a DIP to fix properties anyone? :)
Feb 01 2019
On Friday, 1 February 2019 at 11:48:51 UTC, Timon Gehr wrote:On 01.02.19 10:10, aliak wrote:You mean if __temp is modified in the doubleMyValue and pt.x aliases something else? Or? Wouldn't the assignment back "just work"? And is there a rewrite that would work then?http://wilzbach.github.io/d-dip/DIP24 I'm not sure your rewrite is good though, because it does not preserve aliasing during the function call.Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else: { auto __temp = pt.x; doubleMyValue(__temp); pt.x = __temp; } I believe this is something along the lines of what Swift and Or something... a DIP to fix properties anyone? :)
Feb 01 2019
On Friday, 1 February 2019 at 11:48:51 UTC, Timon Gehr wrote:On 01.02.19 10:10, aliak wrote:ref/out parameter. (The properties should be redefined as accessor, such that you can't take the address of it) -Alexhttp://wilzbach.github.io/d-dip/DIP24 I'm not sure your rewrite is good though, because it does not preserve aliasing during the function call.Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else: { auto __temp = pt.x; doubleMyValue(__temp); pt.x = __temp; } I believe this is something along the lines of what Swift and Or something... a DIP to fix properties anyone? :)
Feb 01 2019
On Friday, 1 February 2019 at 14:41:52 UTC, 12345swordy wrote:On Friday, 1 February 2019 at 11:48:51 UTC, Timon Gehr wrote:By properties I mean accessors? I don’t mean normal fieldsat variables, properties, and accessors?On 01.02.19 10:10, aliak wrote:ref/out parameter. (The properties should be redefined as accessor, such that you can't take the address of it) -Alex[...]http://wilzbach.github.io/d-dip/DIP24 I'm not sure your rewrite is good though, because it does not preserve aliasing during the function call.
Feb 01 2019
On Friday, 1 February 2019 at 15:58:50 UTC, Aliak wrote:On Friday, 1 February 2019 at 14:41:52 UTC, 12345swordy wrote:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/propertiesOn Friday, 1 February 2019 at 11:48:51 UTC, Timon Gehr wrote:By properties I mean accessors? I don’t mean normal fieldsat variables, properties, and accessors?On 01.02.19 10:10, aliak wrote:ref/out parameter. (The properties should be redefined as accessor, such that you can't take the address of it) -Alex[...]http://wilzbach.github.io/d-dip/DIP24 I'm not sure your rewrite is good though, because it does not preserve aliasing during the function call.
Feb 01 2019
On Friday, 1 February 2019 at 09:10:15 UTC, aliak wrote:Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else:The thing is, D doesn't really differentiate between a getter and any other method. So with DIP-1016, when given doubleMyValue(pt.x); The compiler would assume the programmer means - Call pt.x() - Store the result in a temporary - Pass that temporary as a ref parameter to doubleMyValue At no point is the compiler aware that the user intends for x to be interpreted as a getter.
Feb 01 2019
On Friday, 1 February 2019 at 23:24:44 UTC, Olivier FAURE wrote:At no point is the compiler aware that the user intends for x to be interpreted as a getter.In theory, at least, that's what property is for.
Feb 01 2019
On Friday, 1 February 2019 at 23:24:44 UTC, Olivier FAURE wrote:On Friday, 1 February 2019 at 09:10:15 UTC, aliak wrote:property to ref parameter arguments.Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else:The thing is, D doesn't really differentiate between a getter and any other method. So with DIP-1016, when given doubleMyValue(pt.x); The compiler would assume the programmer means - Call pt.x() - Store the result in a temporary - Pass that temporary as a ref parameter to doubleMyValue At no point is the compiler aware that the user intends for x to be interpreted as a getter.
Feb 01 2019
On 2/1/19 8:15 PM, 12345swordy wrote:On Friday, 1 February 2019 at 23:24:44 UTC, Olivier FAURE wrote:Such would be a good conservative approach for us too. We can relax it later if we come with a good idea.On Friday, 1 February 2019 at 09:10:15 UTC, aliak wrote:ref parameter arguments.Shouldn't doubleMyValue(pt.x) be a compiler error if pt.x is a getter? For it not to be a compile error pt.x should also have a setter, in which case the code needs to be lowered to something else:The thing is, D doesn't really differentiate between a getter and any other method. So with DIP-1016, when given doubleMyValue(pt.x); The compiler would assume the programmer means - Call pt.x() - Store the result in a temporary - Pass that temporary as a ref parameter to doubleMyValue At no point is the compiler aware that the user intends for x to be interpreted as a getter.
Feb 03 2019
On Thursday, 31 January 2019 at 21:50:32 UTC, Steven Schveighoffer wrote:How is the problem not in doubleMyValue? It's sole purpose is to update an lvalue. It is the perfect candidate to mark with disable for rvalues.But right now, updating an rvalue is what ref is supposed to be used for. Besides, the fact remains that accepting DIP 1016 would add a new corner case with the potential to create hard-to-detect bugs, which feels to me like it should be a dealbreaker. The fact that this corner case can be patched using disable isn't good enough, because: - Existing codebases that use ref won't have the disable patch, which means using them will become (slightly) dangerous because of DIP 1016. - Making libraries that behave predictably should be the default path (the "pit of success" philosophy), not require an additional construct. Besides, D's type system should be more than capable of consistently telling the user "Be careful, you're modifying a temporary when it's probably not what you meant". --- An alternate proposal that just came to mind: allowing the user to pass rvalues to ref arguments with the following syntax: y = doubleMyValue(cast(ref)10); This syntax would avoid creating ambiguous situations where the compiler thinks you're passing it a getter's return as a temporary when you're trying to pass the attribute that the getter maps to. Eg, the following code: y = doubleMyValue(pt.x); would still fail the same way it currently does when Point.x() is a getter.
Feb 01 2019
On Wednesday, 30 January 2019 at 18:29:37 UTC, Manu wrote:On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:It appears to say it does: fun(my_short); // implicit type conversions (ie, short->int promotion) You should clarify that ;)The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error). We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.
Jan 30 2019
On Wed, Jan 30, 2019 at 6:40 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On Wednesday, 30 January 2019 at 18:29:37 UTC, Manu wrote:Yes, as said above, read `short(10)`. I can understand the confusion that it may look like a variable when taken out of context; but listed beneath the heading immediately above which says: "This inconvenience extends broadly to every manner of **rvalue** passed to functions" It didn't occur to me the reader might interpret the clearly stated list of cases of rvalues passed to functions to include arguments that are not rvalues. The name was just chosen to indicate the argument is a short, perhaps an enum, or any expression that is a short... I could have used `short(10)`, but apparently I didn't think of it at the time. Is this the basis for the claims of "a hole you could drive a truck through"? Again, a request for clarification, and a couldn't-possibly-be-more-trivial revision may resolve this.On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:It appears to say it does: fun(my_short); // implicit type conversions (ie, short->int promotion) You should clarify that ;)The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error). We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.
Jan 30 2019
On 1/30/19 10:05 PM, Manu wrote:On Wed, Jan 30, 2019 at 6:40 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:I think changing it to `short(10)` helps the argument that you didn't intend it to mean conversions from lvalues, but I'd recommend still spelling out that they are forbidden. Leaving the reader to infer intent is not as good as clarifying intent directly. The whole rvalue vs. lvalue thing is confusing to me, because I assumed an lvalue converted to a different type changes it to an rvalue. I think of it like an implicit function that returns that new value. -SteveYou should clarify that ;)Yes, as said above, read `short(10)`. I can understand the confusion that it may look like a variable when taken out of context; but listed beneath the heading immediately above which says: "This inconvenience extends broadly to every manner of **rvalue** passed to functions" It didn't occur to me the reader might interpret the clearly stated list of cases of rvalues passed to functions to include arguments that are not rvalues. The name was just chosen to indicate the argument is a short, perhaps an enum, or any expression that is a short... I could have used `short(10)`, but apparently I didn't think of it at the time. Is this the basis for the claims of "a hole you could drive a truck through"? Again, a request for clarification, and a couldn't-possibly-be-more-trivial revision may resolve this.
Jan 30 2019
On Wed, Jan 30, 2019 at 7:35 PM Steven Schveighoffer via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:On 1/30/19 10:05 PM, Manu wrote:I mean, the heading of the DIP is "ref T accepts r-values", the whole abstract talks about nothing but rvalues, the header of the confusing block couldn't say 'rvalues' more clearly... I didn't consider that it was possible to confuse this as anything other than an rvalue DIP... but yes, I can certainly spell it out.On Wed, Jan 30, 2019 at 6:40 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:I think changing it to `short(10)` helps the argument that you didn't intend it to mean conversions from lvalues, but I'd recommend still spelling out that they are forbidden.You should clarify that ;)Yes, as said above, read `short(10)`. I can understand the confusion that it may look like a variable when taken out of context; but listed beneath the heading immediately above which says: "This inconvenience extends broadly to every manner of **rvalue** passed to functions" It didn't occur to me the reader might interpret the clearly stated list of cases of rvalues passed to functions to include arguments that are not rvalues. The name was just chosen to indicate the argument is a short, perhaps an enum, or any expression that is a short... I could have used `short(10)`, but apparently I didn't think of it at the time. Is this the basis for the claims of "a hole you could drive a truck through"? Again, a request for clarification, and a couldn't-possibly-be-more-trivial revision may resolve this.Leaving the reader to infer intent is not as good as clarifying intent directly. The whole rvalue vs. lvalue thing is confusing to me, because I assumed an lvalue converted to a different type changes it to an rvalue. I think of it like an implicit function that returns that new value.Obviously all of this is true, but I didn't think of it that way; didn't realise there was a point of confusion, and nobody during the community reviews appeared to raise confusion either. I'll obviously revise it, except that it's rejected and moved to the rejected folder. For reference, the key point that justifies its mention in the first place is a little further down: "It is important that T be defined as the parameter type, and not auto (ie, the argument type), because it will allow implicit conversions to occur naturally, with identical behavior as when the parameter is not ref." It was important to consider mis-matching types (implicit conversions), because there is detail in the rules that allows them to work properly and make the call uniform with the same function if it passed by-val.
Jan 30 2019
On 1/30/19 10:05 PM, Manu wrote:On Wed, Jan 30, 2019 at 6:40 PM Nicholas Wilson via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:Affirmative. With the restriction that the expression passed into the function must be an rvalue to start with, by Walter's and my understanding, the proposed semantics would work and be helpful.On Wednesday, 30 January 2019 at 18:29:37 UTC, Manu wrote:Yes, as said above, read `short(10)`. I can understand the confusion that it may look like a variable when taken out of context; but listed beneath the heading immediately above which says: "This inconvenience extends broadly to every manner of **rvalue** passed to functions" It didn't occur to me the reader might interpret the clearly stated list of cases of rvalues passed to functions to include arguments that are not rvalues. The name was just chosen to indicate the argument is a short, perhaps an enum, or any expression that is a short... I could have used `short(10)`, but apparently I didn't think of it at the time. Is this the basis for the claims of "a hole you could drive a truck through"?On Wed, Jan 30, 2019 at 9:20 AM Neia Neutuladh via Digitalmars-d-announce <digitalmars-d-announce puremagic.com> wrote:It appears to say it does: fun(my_short); // implicit type conversions (ie, short->int promotion) You should clarify that ;)The result of a CastExpression is an rvalue. An implicit cast is a compiler-inserted CastExpression. Therefore all lvalues with a potential implicit cast are rvalues.But there's no existing language rule that attempts to perform an implicit cast where an lvalue is supplied to a ref arg...? Why is the cast being attempted? 'p' is an lvalue, and whatever that does should remain exactly as is (ie, emits a compile error). We could perhaps allow this for `const` args, but that feels like separate follow-up work to me, and substantially lesser value. This DIP doesn't want to change anything about lvalues.Again, a request for clarification, and a couldn't-possibly-be-more-trivial revision may resolve this.Negative. It must be clear that the reason of this misunderstanding is squarely due to the DIP itself. It has multiple problems of informality and vague language that have worked together to cause said misunderstanding. (It is great it's just that, a misunderstanding; I have been worried people would believe such an awful semantics was considered just fine. That explains but does not justify my use of unkind language.) The DIP must convince the reader, and in a way the reader does not "owe" the DIP. For good reason, they call the research theme chosen by a doctoral candidate a "charge"; the root of "dissertation" is Latin for "debate"; and the final doctoral examination is plainly called a "defense". The whole thing is structured like a criminal investigation :o). Of course we don't want to be as harsh as academics could get, but we don't want to transform DIP acceptance into a farmers market bargaining process. So the code with my_short was open to interpretation. Cool. In a thorough submission, however, there would have been many places that clear that up: * Use of a distinct notation (non-code non-text font for metalanguage, i.e. general expressions); * Description of the typechecking process, with examples of code that passes and code that fails; * A clarification that lowering proceeds not against all expressions, but only against rvalues; * Several places in text in which it is explained that rvalues resulted from implicit conversions are not eligible; * etc. etc. etc. So if we rejected the DIP, we didn't do so on account of one word that can be so easily changed; we did so on account of a DIP that as a whole failed to clarify what it purports to do (and equally importantly, to not do). The purpose of us all is to move things forward, and in that spirit allow me to put forward a short list of matters that a revised proposal should do, at a minimum: * Walter has posted about a few issues with various parts of the proposal. Those should be addressed. * The "Reference" section does good to mention the issues, but the litany of forum discussions has no value besides "there have been repeated discussion in community forums of the topic", and refer to a list in an bibliography. Placing them in the "Reference" section suggests the reader that they need to read the forum debates in order to understand the DIP, which isn't and shouldn't be the case. * An "Existing Work" section discussing C++ (and possibly Rust) is a must. Studious neglect of what other languages do and what problems they have does not serve us well. I think Walter could help with that. * The "Rationale" section currently focuses only on issues caused by the current rule. It should have three parts: - Open with a brief description of the current rule and why it is that way. Here we have the advantage that confusing conversions are disallowed. - Then continue with "However, the binding rule also causes a variety of undue limitations whereby valid and useful code is rejected". Insert examples. - Then continue with "If the rule got relaxed so as to only allow certain bindings but not all, we could allow the sensible cases illustrated above, yet still disallow the problematic cases." Boom! The reader is already loving it. * Subsection "Why are we here?" is not necessary. Its first part is folded in the "Rationale", and its second part is actually clearly an argument that is part of the rationale. IMHO the "pipeline" argument is not necessary, or can be moved in an "Examples of use" section toward the end. * "Description" needs a complete rewrite. Here typing rules, lowering, exception safety, should be explained in ultimate detail using metalanguage (not just a few simplistic examples). It should be mechanically clear how an arbitrary expression containing function calls can be lowered into preexisting D code. Again I very much recommend lowering expressions to expressions instead of statements to statements, but anything clear and thorough will do. * Also the description should clarify what happens in certain odd cases, such as alias this, class inheritance, properties that return lvalues vs. rvalues using the same syntax, and probably more. An important aspect of the proposal is "rvalues look different than lvalues, so the user won't get confused". * "Temporary destruction" is unnecessary. * "Function calls as arguments" us unnecessary if "Description" describes the feature completely. * "Interaction with return ref" is probably unnecessary as a subsection but it would be nice to have as an explanatory example after the general lowering has been introduced. * "Overload resolution" - see Walter's comments. * Eliminate imprecise language "It has been noted that..." (by whom? when? reference?) If no reference, fine - spend a sentence to create that note! * "Why not XXX?" should be massaged into a "Workarounds" section that follows the "Rationale" section, or is a subsection of it. * "Key use cases" is good, and more examples should be only better. Hope this helps! The outlook is definitely better after this misunderstanding has been cleared. Andrei
Jan 30 2019
On Monday, 28 January 2019 at 17:23:51 UTC, Andrei Alexandrescu wrote:* Regarding the argument "why not make this an iterative process where concerns are raised and incrementally addressed?" We modeled the DIP process after similar processes - conference papers, journal papers, proposals in other languages. There is a proposal by one or more responsibles, perfected by a community review, and submitted for review. This encourages building a strong proposal - as strong as can be - prior to submission. Washing that down to a negotiation between the proposers and the reviewers leads to a "worst acceptable proposal" state of affairs in which proposers are incentivized to submit the least-effort proposal, reactively change it as issues are raised by reviewers.Fair enough.
Jan 29 2019
I'm on the reviewers side here. To be honest I never liked this DIP and maybe I'll sound dumb but I think this is a case where this could bring more problem than anything. The way I see this would be more like a syntax sugar to create temporary variable for ref parameters and that's it. But what I fail to see is why can't the programmer solve this themselves instead of relying on a new feature that would cause more harm? With overload some could do: void f(int i){ f(i); } void f(ref int i){ ++i; writeln(i); } void main(){ int i = 0; f(10); f(i); } prints: 11 1 The "f" function will work with ref or literal (rvalues). But this will be controlled by the programmer the way they want it. Donald.
Jan 29 2019
On Wednesday, 30 January 2019 at 00:25:17 UTC, Don wrote:But what I fail to see is why can't the programmer solve this themselves instead of relying on a new feature that would cause more harm?Donald....Did you even read the arguments in the dip? This has been discuss quite a lot in the forums, it even gives you links to them.
Jan 29 2019
On Wednesday, 30 January 2019 at 03:01:36 UTC, 12345swordy wrote:On Wednesday, 30 January 2019 at 00:25:17 UTC, Don wrote:Well, I read the DIP and the whole forum discussion back in the day, and again I think this will create more harm than benefits the way it was proposed. And starting from the beginning of this DIP - Rationale example: "void fun(int x); fun(10); // <-- this is how users expect to call a typical function But when ref is present: void fun(ref int x); fun(10); // <-- compile error; not an lvalue!! Necessitating the workaround: int temp = 10; fun(temp); This inconvenience extends broadly to every manner of rvalue passed to functions, including:" So the solution in the way I understood is pretty much a syntax sugar, creating temporary variable with destruction. But the concept is weird, because originally your function signature has a "ref parameter" and we're just creating a workaround expanding it to handle rvalues. I would prefer to handle it myself with overloading instead of being presented with new language feature creating different scenarios for something that's not the case right now. Otherwise D will be pretty much like C++ and in this case why bother with it? Donald.But what I fail to see is why can't the programmer solve this themselves instead of relying on a new feature that would cause more harm?Donald....Did you even read the arguments in the dip? This has been discuss quite a lot in the forums, it even gives you links to them.
Jan 29 2019
On Wednesday, 30 January 2019 at 04:34:46 UTC, Don wrote:On Wednesday, 30 January 2019 at 03:01:36 UTC, 12345swordy wrote:I do not accept gut feeling as a valid objection here. The current workarounds is shown to be painful as shown in the dip and in the discussions that it currently link. That *the* motivation here. I am familiar with the author here, he is very involved with the C++<->D compatibility side of things. He knows the pain from first hand experience. -AlexOn Wednesday, 30 January 2019 at 00:25:17 UTC, Don wrote:Well, I read the DIP and the whole forum discussion back in the day, and again I think this will create more harm than benefits the way it was proposed. Donald.But what I fail to see is why can't the programmer solve this themselves instead of relying on a new feature that would cause more harm?Donald....Did you even read the arguments in the dip? This has been discuss quite a lot in the forums, it even gives you links to them.
Jan 30 2019
On Wednesday, 30 January 2019 at 13:58:38 UTC, 12345swordy wrote:I do not accept gut feeling as a valid objection here. The current workarounds is shown to be painful as shown in the dip and in the discussions that it currently link. That *the* motivation here.Like I said previously I am on the reviews side and that's it. By the way I don't like your tone when you say: "I do not accept gut feeling as a valid objection here". I don't think you would like if I say that your opinion is biased because you know the author either, so don't go that way, because it's not only me against this DIP.I am familiar with the author here, he is very involved with the C++<->D compatibility side of things. He knows the pain from first hand experience.Alright we're talking about a change that have been on hold for almost 10 years, if it was simple it would already been done. In this thread we saw some other concerns been emerged. Finally I only know the author by his postings in this forum, and I don't have anything personally against him. Donald.
Jan 30 2019
On Wednesday, 30 January 2019 at 16:47:48 UTC, Don wrote:On Wednesday, 30 January 2019 at 13:58:38 UTC, 12345swordy wrote:In terms of what exactly? Walter had stated they do not rejected the dip in principle. You apparently *do* rejected it in principle, from judging your posts here.I do not accept gut feeling as a valid objection here. The current workarounds is shown to be painful as shown in the dip and in the discussions that it currently link. That *the* motivation here.Like I said previously I am on the reviews side and that's it.By the way I don't like your tone when you say: "I do not accept gut feeling as a valid objection here".If you stated that you think it a bad/good idea without explaining why you think it. That is what I call "gut feeling"Alright we're talking about a change that have been on hold for almost 10 years, if it was simple it would already been done.The current dip system didn't exist 10 years prior. I wouldn't say that things are already done due to them being simple, as there are quite number of "simple" features that wasn't implemented already (Looking at you tuples). -Alex
Jan 30 2019
On Thursday, 24 January 2019 at 07:18:58 UTC, Mike Parker wrote:Walter and Andrei have declined to accept DIP 1016, "ref T accepts r-values", on the grounds that it has two fundamental flaws that would open holes in the language.fun(10) ==> { T __temp0 = void; fun(__temp0 := 10); }the rewrite is from an expression to a statement, rendering it invalid.if the first constructor throws an exception, all remaining values will be destroyed in the void state as they never have the chance to become initialized.They say that with the current semantics, this function only operates on long values as it should. With the proposed semantics, the call will accept all shared integral types.I think this solves all of the above: fun(10); ==> (int __temp0){ (return)? fun( __temp0 ); }(10); -The expression/statement issue is solved by the closure -The initialization issue created by the ":=" approach is not present here -For this rewrite, 'T' is the type of the rvalue argument, not the type of the function parameter. This prevents undesired implicit conversions.
Feb 04 2019
On Monday, 4 February 2019 at 17:09:25 UTC, bitwise wrote:I think this solves all of the above: fun(10); ==> (int __temp0){ (return)? fun( __temp0 ); }(10); -The expression/statement issue is solved by the closure -The initialization issue created by the ":=" approach is not present here -For this rewrite, 'T' is the type of the rvalue argument, not the type of the function parameter. This prevents undesired implicit conversions.I don't understand this. What does "(return)?" mean? Is this valid D syntax? What do I miss?
Feb 04 2019
On Monday, 4 February 2019 at 18:32:56 UTC, Dominikus Dittes Scherkl wrote:I don't understand this. What does "(return)?" mean? Is this valid D syntax? What do I miss?I meant for that to be interpreted like a Regular expression, denoting conditional presence of the return statement. I'm not sure what the proper notation would be to express such a thing.
Feb 04 2019
On Monday, 4 February 2019 at 18:35:37 UTC, bitwise wrote:On Monday, 4 February 2019 at 18:32:56 UTC, Dominikus Dittes Scherkl wrote:It's actually fine to leave the `return` there unconditionally--you're allowed to return an expression of type `void` from a function. Example: https://run.dlang.io/is/tnSGN4I don't understand this. What does "(return)?" mean? Is this valid D syntax? What do I miss?I meant for that to be interpreted like a Regular expression, denoting conditional presence of the return statement. I'm not sure what the proper notation would be to express such a thing.
Feb 04 2019
On Monday, 4 February 2019 at 20:08:39 UTC, Paul Backus wrote:On Monday, 4 February 2019 at 18:35:37 UTC, bitwise wrote:Even better ;) No one else seems particularly excited about the rewrite though - what am I missing? I suppose a lambda cost significantly more, but I don't think the lambda should make it through the optimizer for this case though.On Monday, 4 February 2019 at 18:32:56 UTC, Dominikus Dittes Scherkl wrote:It's actually fine to leave the `return` there unconditionally--you're allowed to return an expression of type `void` from a function. Example: https://run.dlang.io/is/tnSGN4I don't understand this. What does "(return)?" mean? Is this valid D syntax? What do I miss?I meant for that to be interpreted like a Regular expression, denoting conditional presence of the return statement. I'm not sure what the proper notation would be to express such a thing.
Feb 08 2019
On Friday, 8 February 2019 at 16:00:58 UTC, bitwise wrote:On Monday, 4 February 2019 at 20:08:39 UTC, Paul Backus wrote:Immediately called lamdas are always inlined.On Monday, 4 February 2019 at 18:35:37 UTC, bitwise wrote:Even better ;) No one else seems particularly excited about the rewrite though - what am I missing? I suppose a lambda cost significantly more, but I don't think the lambda should make it through the optimizer for this case though.[...]It's actually fine to leave the `return` there unconditionally--you're allowed to return an expression of type `void` from a function. Example: https://run.dlang.io/is/tnSGN4
Feb 08 2019
On Friday, 8 February 2019 at 23:02:34 UTC, Nicholas Wilson wrote:Immediately called lamdas are always inlined.``` extern(C) void main() { int a = (() => 1)(); } ``` dmd -inline -O -release -betterC asm: ``` main: push RBP mov RBP,RSP call qword ptr pure nothrow nogc safe int onlineapp.main().__lambda1() GOTPCREL[RIP] xor EAX,EAX pop RBP ret ``` https://run.dlang.io/is/lZW9B6 Still a lambda call :/
Feb 08 2019
On Fri, Feb 08, 2019 at 11:34:47PM +0000, Dennis via Digitalmars-d-announce wrote:On Friday, 8 February 2019 at 23:02:34 UTC, Nicholas Wilson wrote:Does LDC/GDC inline it? I no longer trust dmd for codegen quality. :-/ T -- Customer support: the art of getting your clients to pay for your own incompetence.Immediately called lamdas are always inlined.``` extern(C) void main() { int a = (() => 1)(); } ``` dmd -inline -O -release -betterC asm: ``` main: push RBP mov RBP,RSP call qword ptr pure nothrow nogc safe int onlineapp.main().__lambda1() GOTPCREL[RIP] xor EAX,EAX pop RBP ret ``` https://run.dlang.io/is/lZW9B6 Still a lambda call :/
Feb 08 2019
On Fri, Feb 08, 2019 at 03:42:51PM -0800, H. S. Teoh via Digitalmars-d-announce wrote:On Fri, Feb 08, 2019 at 11:34:47PM +0000, Dennis via Digitalmars-d-announce wrote:[...]On Friday, 8 February 2019 at 23:02:34 UTC, Nicholas Wilson wrote:Immediately called lamdas are always inlined.``` extern(C) void main() { int a = (() => 1)(); } ```Does LDC/GDC inline it? I no longer trust dmd for codegen quality. :-/[...] Just checked: LDC does inline it. In fact, LDC compiles the whole thing out and just has `ret` for main(). :-D Forcing LDC not to elide the whole thing by inserting a writeln(a) call reveals that the lambda is indeed inlined. Yep, the moral of the story is, if codegen quality is important to you, use ldc (and presumably gdc too) rather than dmd. T -- Freedom of speech: the whole world has no right *not* to hear my spouting off!
Feb 08 2019
On Friday, 8 February 2019 at 23:58:49 UTC, H. S. Teoh wrote:Yep, the moral of the story is, if codegen quality is important to you, use ldc (and presumably gdc too) rather than dmd.That's definitely true, but that leaves the question whether lowering rvalue references to lambdas is acceptable. There's the 'dmd for fast builds, gdc/ldc for fast code' motto, but if your debug builds of your game make it run at 15 fps it becomes unusable. I don't want the gap between dmd and compilers with modern back-ends to widen.
Feb 08 2019
On Sat, Feb 09, 2019 at 12:04:20AM +0000, Dennis via Digitalmars-d-announce wrote:On Friday, 8 February 2019 at 23:58:49 UTC, H. S. Teoh wrote:TBH, I've been finding that ldc compilation times aren't all that bad compared to dmd. It's definitely slightly slower, but it's not anywhere near the gap between, say, dmd and g++. Recently I've been quite tempted to replace dmd with ldc as my main D compiler, esp. now that ldc releases are essentially on par with dmd releases in terms of release schedule of a particular language version. The slowdown in compilation times isn't enough to offset the benefits, as long as you're not compiling with, say, -O3 which *would* make the ldc optimizer run slower (but with the huge benefit of significantly better codegen -- I've seen performance improvements of up to ~200% with ldc -O3 vs. dmd -O -inline). And template-heavy code is slow across all D compilers anyway, so the relatively small compilation time difference between dmd and ldc doesn't really matter that much anymore once you have a sufficiently large codebase with heavy template use. T -- What doesn't kill me makes me stranger.Yep, the moral of the story is, if codegen quality is important to you, use ldc (and presumably gdc too) rather than dmd.That's definitely true, but that leaves the question whether lowering rvalue references to lambdas is acceptable. There's the 'dmd for fast builds, gdc/ldc for fast code' motto, but if your debug builds of your game make it run at 15 fps it becomes unusable. I don't want the gap between dmd and compilers with modern back-ends to widen.
Feb 08 2019
On Saturday, 9 February 2019 at 00:04:20 UTC, Dennis wrote:On Friday, 8 February 2019 at 23:58:49 UTC, H. S. Teoh wrote:Since the user doesn't explicitly place the lambda in their code, wouldn't it be justifiable for the compiler to take it back out again at a later step in compilation, even in debug mode?Yep, the moral of the story is, if codegen quality is important to you, use ldc (and presumably gdc too) rather than dmd.That's definitely true, but that leaves the question whether lowering rvalue references to lambdas is acceptable. There's the 'dmd for fast builds, gdc/ldc for fast code' motto, but if your debug builds of your game make it run at 15 fps it becomes unusable. I don't want the gap between dmd and compilers with modern back-ends to widen.
Feb 08 2019
On Sat, Feb 09, 2019 at 01:08:55AM +0000, bitwise via Digitalmars-d-announce wrote:On Saturday, 9 February 2019 at 00:04:20 UTC, Dennis wrote:Using lowering to lambdas as a way of defining semantics is not the same thing as actually using lambdas to implement a feature in the compiler! While it can be convenient to do the latter as a first stab, I'd expect that the optimizer could make use of special knowledge available in the compiler to implement this more efficiently. Since the compiler will always use a fixed pattern for the lowering, the backend could detect this pattern and optimize accordingly. Or the compiler implementation could lower it directly to something more efficient in the first place. T -- If you look at a thing nine hundred and ninety-nine times, you are perfectly safe; if you look at it the thousandth time, you are in frightful danger of seeing it for the first time. -- G. K. ChestertonOn Friday, 8 February 2019 at 23:58:49 UTC, H. S. Teoh wrote:Since the user doesn't explicitly place the lambda in their code, wouldn't it be justifiable for the compiler to take it back out again at a later step in compilation, even in debug mode?Yep, the moral of the story is, if codegen quality is important to you, use ldc (and presumably gdc too) rather than dmd.That's definitely true, but that leaves the question whether lowering rvalue references to lambdas is acceptable. There's the 'dmd for fast builds, gdc/ldc for fast code' motto, but if your debug builds of your game make it run at 15 fps it becomes unusable. I don't want the gap between dmd and compilers with modern back-ends to widen.
Feb 08 2019
On Saturday, 9 February 2019 at 01:31:05 UTC, H. S. Teoh wrote:Using lowering to lambdas as a way of defining semantics is not the same thing as actually using lambdas to implement a feature in the compiler! While it can be convenient to do the latter as a first stab, I'd expect that the optimizer could make use of special knowledge available in the compiler to implement this more efficiently. Since the compiler will always use a fixed pattern for the lowering, the backend could detect this pattern and optimize accordingly. Or the compiler implementation could lower it directly to something more efficient in the first place. TThe lambda even correctly handles " disable this(this);", I like it! struct One { disable this(this); } void fun(ref One one) { } One gun() { return One.init; } void main() { One one; one.fun(); (One __temp0){ return fun( __temp0 ); }(gun()); // OK }
Feb 10 2019