digitalmars.D - Why do struct literals count as lvalues?
- Trass3r (12/12) Aug 18 2011 struct A {}
- kennytm (5/18) Aug 18 2011 The difference -- you've answered yourself in the title ;).
- Jonathan M Davis (10/33) Aug 18 2011 Yeah. I don't understand why a struct literal would be an lvalue. It's a...
- Trass3r (3/9) Aug 18 2011 I don't understand it either.
- Ali =?iso-8859-1?q?=C7ehreli?= (13/24) Aug 18 2011 (I suspect that I am confusing issues here; still... :) )
- Jonathan M Davis (8/18) Aug 18 2011 It doesn't even make sense with const ref IMHO - not unless you're going...
- bearophile (15/18) Aug 18 2011 I have not followed fully this thread, so I am not sure what you are tal...
- Jonathan M Davis (10/36) Aug 18 2011 That may be, but it doesn't refer to an actual variable, so ref makes no...
- Steven Schveighoffer (4/26) Aug 19 2011 I believe auto ref is slated to fix this problem.
- Timon Gehr (18/50) Aug 19 2011 auto ref currently treats struct literals as lvalues too. (therefore, as...
- kenji hara (6/60) Aug 19 2011 As far as I know, Andrei's original 'auto ref' means always receive
- Timon Gehr (4/72) Aug 19 2011 Oh, interesting. So why were they implemented with different semantics
- kenji hara (15/18) Aug 19 2011 In my opinion, that might work like C++ const T&, that allows
- kennytm (8/30) Aug 19 2011 Here I propose that we go a step further and abolish the notion of rvalu...
- Timon Gehr (5/35) Aug 19 2011 And then of course you could do:
- kennytm (5/47) Aug 19 2011 Actually that assignment would just become an expensive no-op because it
- Timon Gehr (2/49) Aug 19 2011 Nah, no-ops should be a compile error :o).
- Trass3r (2/12) Aug 19 2011 +1, but const should be required.
struct A {} static A bar() { return A(); } void foo(ref A a) {} void main() { foo(A()); // works foo(bar()); // doesn't } Where's the difference?
Aug 18 2011
Trass3r <un known.com> wrote:struct A {} static A bar() { return A(); } void foo(ref A a) {} void main() { foo(A()); // works foo(bar()); // doesn't } Where's the difference?The difference -- you've answered yourself in the title ;). Reason why struct literals are lvalues -- because Walter and others believe this is valid. Check the discussion in bugzilla issues 5178 and 5889. BTW, C99's compound literals also give lvalues.
Aug 18 2011
On Thursday, August 18, 2011 12:49 kennytm wrote:Trass3r <un known.com> wrote:Yeah. I don't understand why a struct literal would be an lvalue. It's a temporary. What possible value does it have? It's not a variable. It doesn't refer to a variable. Why would you be able to assign to anything which is not a variable (or indirectly refers to one - e.g. with ref)? The result is incredibly confusing - especially when you mix const ref into the mix. I grant you that changing it now would break code - especially given http://d.puremagic.com/issues/show_bug.cgi?id=3659 - but it serves no real purpose except to confuse as far as I can see. - Jonathan M Davisstruct A {} static A bar() { return A(); } void foo(ref A a) {} void main() { foo(A()); // works foo(bar()); // doesn't } Where's the difference?The difference -- you've answered yourself in the title ;). Reason why struct literals are lvalues -- because Walter and others believe this is valid. Check the discussion in bugzilla issues 5178 and 5889.
Aug 18 2011
Am 18.08.2011, 22:19 Uhr, schrieb Jonathan M Davis <jmdavisProg gmx.com>:Yeah. I don't understand why a struct literal would be an lvalue. It's a temporary. What possible value does it have? It's not a variable. It doesn't refer to a variable. Why would you be able to assign to anything which is not a variable (or indirectly refers to one - e.g. with ref)?I don't understand it either. It only makes sense with const ref.
Aug 18 2011
On Thu, 18 Aug 2011 22:33:47 +0200, Trass3r wrote:Am 18.08.2011, 22:19 Uhr, schrieb Jonathan M Davis <jmdavisProg gmx.com>:(I suspect that I am confusing issues here; still... :) ) C++11 added rvalue references. They are useful at least for move semantics. (Andrei had worked hard to find an non-intrusive solution but had to settle with an intrusive one: http://drdobbs.com/184403855 ) I remember reading about this in the TDPL. 7.1.3.5 is on topic. Here is an excerpt from page 251: "All anonymous rvalues are moved, not copied. A call to this(this) is never inserted when the source is an anonymous rvalue ..." Good. And when we want to move a named variable, there is std.algorithm.move: kun(move(w)); AliYeah. I don't understand why a struct literal would be an lvalue. It's a temporary. What possible value does it have? It's not a variable. It doesn't refer to a variable. Why would you be able to assign to anything which is not a variable (or indirectly refers to one - e.g. with ref)?I don't understand it either. It only makes sense with const ref.
Aug 18 2011
On Thursday, August 18, 2011 13:33 Trass3r wrote:Am 18.08.2011, 22:19 Uhr, schrieb Jonathan M Davis <jmdavisProg gmx.com>:It doesn't even make sense with const ref IMHO - not unless you're going to allow const ref to be bound to temporaries in general. I see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function. It just confuses things. And in a sense, a newly constructed object _is_ returned by a function, it's just that it's the constructor rather than a normal function. - Jonathan M DavisYeah. I don't understand why a struct literal would be an lvalue. It's a temporary. What possible value does it have? It's not a variable. It doesn't refer to a variable. Why would you be able to assign to anything which is not a variable (or indirectly refers to one - e.g. with ref)?I don't understand it either. It only makes sense with const ref.
Aug 18 2011
Jonathan M Davis:I see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy. Bye, bearophile
Aug 18 2011
On Thursday, August 18, 2011 15:56 bearophile wrote:Jonathan M Davis:That may be, but it doesn't refer to an actual variable, so ref makes no sense IMHO, and regardless, the return value of a function is just as hairy. So, with the current situation foo(Vect([1, 2, 3, 4])); works, but foo(func()); doesn't (assuming that func returns ' Vec). That makes no sense to me. Why treat one differently than the other? It's just confusing. - Jonathan M DavisI see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy.
Aug 18 2011
On Thu, 18 Aug 2011 18:56:01 -0400, bearophile <bearophileHUGS lycos.com> wrote:Jonathan M Davis:I believe auto ref is slated to fix this problem. -SteveI see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy.
Aug 19 2011
On 08/19/2011 02:16 PM, Steven Schveighoffer wrote:On Thu, 18 Aug 2011 18:56:01 -0400, bearophile <bearophileHUGS lycos.com> wrote:auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Jonathan M Davis:I believe auto ref is slated to fix this problem. -SteveI see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy.
Aug 19 2011
2011/8/19 Timon Gehr <timon.gehr gmx.ch>:On 08/19/2011 02:16 PM, Steven Schveighoffer wrote:As far as I know, Andrei's original 'auto ref' means always receive argument *by reference* either it is lvalue or rvalue, and it works with non template function. But, unfortunately, it doesn't exist in current D. Kenji HaraOn Thu, 18 Aug 2011 18:56:01 -0400, bearophile <bearophileHUGS lycos.com> wrote:auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Jonathan M Davis:I believe auto ref is slated to fix this problem. -SteveI see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy.
Aug 19 2011
On 08/19/2011 03:04 PM, kenji hara wrote:2011/8/19 Timon Gehr<timon.gehr gmx.ch>:Oh, interesting. So why were they implemented with different semantics then? Also, how would they work in a non-templated function? Would they just be conservatively treated as rvalues in the function body?On 08/19/2011 02:16 PM, Steven Schveighoffer wrote:As far as I know, Andrei's original 'auto ref' means always receive argument *by reference* either it is lvalue or rvalue, and it works with non template function. But, unfortunately, it doesn't exist in current D. Kenji HaraOn Thu, 18 Aug 2011 18:56:01 -0400, bearophile <bearophileHUGS lycos.com> wrote:auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Jonathan M Davis:I believe auto ref is slated to fix this problem. -SteveI see _zero_ reason to treat a newly constructed object as any different from one which is returned from a function.I have not followed fully this thread, so I am not sure what you are talking about, so sorry if I am misunderstanding the whole topic. I assume you want to disallow code like: struct Vect { float[4] data = 0.0; // lot of operator overloading here, all arguments are by ref } void foo(ref Vect f) {} void main() { foo(Vect([1,2,3,4])); } Such code is common. In foo() and in all the Vect operators ref is required for performance. This code is quite handy. Forcing me to define and assign a Vect to a temporary variable in main is not handy. Think about writing expressions that use Vects with operator overloading, that use ref everywhere. If you can't pass and give Vects defined inside the expressions the code becomes quite more hairy.
Aug 19 2011
2011/8/19 Timon Gehr <timon.gehr gmx.ch>:Oh, interesting. So why were they implemented with different semantics then?I don't know the background, sorry.Also, how would they work in a non-templated function?In my opinion, that might work like C++ const T&, that allows receiving a rvalue argument.Would they just be conservatively treated as rvalues in the function body?Maybe it doesn't. In D, a variable is always treated as lvalue, even if it is a parameter. The follows are my opinion: D might be designed as which ref parameter receives only lvalue, and non-ref parameter receives only rvalue. Today ref storage class also means that the parameter binds corresponding argument by reference, but it is not a language design, but that is performance decision. It seems to me that ref storage class is designed for automatic move semantics. According to my think, struct literal should be rvalue. That is temporary, and we want to move it automatically. Kenji Hara
Aug 19 2011
Timon Gehr <timon.gehr gmx.ch> wrote:auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Here I propose that we go a step further and abolish the notion of rvalue and lvalue entirely, and let the compiler insert necessary temporary variable when needed, so that we can finally write things like uint w; memcpy(w, &5.0f, w.sizeof); 13u = w; </joking>
Aug 19 2011
On 08/19/2011 03:46 PM, kennytm wrote:Timon Gehr<timon.gehr gmx.ch> wrote:And then of course you could do: 5=6; assert(5==6); It hasn't been working since the good ol' days of fortran =(.auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Here I propose that we go a step further and abolish the notion of rvalue and lvalue entirely, and let the compiler insert necessary temporary variable when needed, so that we can finally write things like uint w; memcpy(w,&5.0f, w.sizeof); 13u = w; </joking>
Aug 19 2011
Timon Gehr <timon.gehr gmx.ch> wrote:On 08/19/2011 03:46 PM, kennytm wrote:Actually that assignment would just become an expensive no-op because it will be rewritten into (int __lvalue1243 = 5, __lvalue1243) = 6; So the assert would still assert.Timon Gehr<timon.gehr gmx.ch> wrote:And then of course you could do: 5=6; assert(5==6); It hasn't been working since the good ol' days of fortran =(.auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Here I propose that we go a step further and abolish the notion of rvalue and lvalue entirely, and let the compiler insert necessary temporary variable when needed, so that we can finally write things like uint w; memcpy(w,&5.0f, w.sizeof); 13u = w; </joking>
Aug 19 2011
On 08/19/2011 04:33 PM, kennytm wrote:Timon Gehr<timon.gehr gmx.ch> wrote:Nah, no-ops should be a compile error :o).On 08/19/2011 03:46 PM, kennytm wrote:Actually that assignment would just become an expensive no-op because it will be rewritten into (int __lvalue1243 = 5, __lvalue1243) = 6; So the assert would still assert.Timon Gehr<timon.gehr gmx.ch> wrote:And then of course you could do: 5=6; assert(5==6); It hasn't been working since the good ol' days of fortran =(.auto ref currently treats struct literals as lvalues too. (therefore, as ref). And passing by value would be considerably less efficient for large structs. As I understand it, the only issue with allowing literals and function results as lvalues is that generic code cannot detect if it is working with a temporary or not. I don't know if this would be useful in D though. each code segment of the form: void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.Here I propose that we go a step further and abolish the notion of rvalue and lvalue entirely, and let the compiler insert necessary temporary variable when needed, so that we can finally write things like uint w; memcpy(w,&5.0f, w.sizeof); 13u = w; </joking>
Aug 19 2011
Am 19.08.2011, 14:50 Uhr, schrieb Timon Gehr <timon.gehr gmx.ch>:void foo(ref S); ... foo(S(...)); is equivalent to one explicitly declaring the temporary: {auto __temp=S(...); foo(__temp);} The difference is that the first is more pleasant to write. If temporaries would become rvalues everyone would always have to write the second form manually. So imho it is just a syntax sugar issue. I'd actually argue that ref-passing should work for arbitrary function results too.+1, but const should be required.
Aug 19 2011