www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 3008] New: Members of non-lvalues can be assigned to.

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008

           Summary: Members of non-lvalues can be assigned to.
           Product: D
           Version: 2.030
          Platform: PC
        OS/Version: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: chadjoan gmail.com


struct S { int a = 0; }
S foo() { S s; return s; }

void main()
{
    foo.a++;
    foo().a++;
    foo.a = 42;
}

This compiles but is nonsensical.  It is impossible to modify foo.a, yet the
compiler lets you try anyway.  

Tried it with DMD 2.030 on Linux.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 18 2009
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008


BCS <shro8822 vandals.uidaho.edu> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |shro8822 vandals.uidaho.edu





---
The problem is not that ++/+= work on lvalues (they don't). The problem is that
the members of a struct returned from a function are lvalues.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 18 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008


Steven Schveighoffer <schveiguy yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy yahoo.com





07:01:51 PDT ---
Consider this actually sensical case:

struct S
{
  int *a;
}

S foo() {...}

*foo.a = 3;

Clearly, you don't want structs to be non-modifyiable in all cases.  If S has
member functions which modify both values of S and values S references, then
you should able to call those functions also.  It might be very difficult for
the compiler to distinguish all these cases.

I'd say the case you give is non-sensical, but still valid code.

This is also non-sensical code, in the same light, and I don't think it should
be a compiler error:

void foo()
{
   int x;
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 19 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008


Jarrett Billingsley <jarrett.billingsley gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jarrett.billingsley gmail.c
                   |                            |om





2009-07-28 18:09:12 PDT ---

 Consider this actually sensical case:
 
 struct S
 {
   int *a;
 }
 
 S foo() {...}
 
 *foo.a = 3;
 
 Clearly, you don't want structs to be non-modifyiable in all cases.  If S has
 member functions which modify both values of S and values S references, then
 you should able to call those functions also.  It might be very difficult for
 the compiler to distinguish all these cases.
Actually the compiler wouldn't have to do anything special here. If the return of foo() were an rvalue, then *foo().a could still be an lvalue, not because of any special rules, but because of how the dereference operator works. A more thorough explanation: DMD will diagnose no-op statements such as: x + y; Since such code is dead and possibly a typo (say you accidentally shifted your assignment operator there). Code like: struct S { int a; } S foo() { return S(5); } foo().a++; Should be nonsensical for the same reason. foo() is an rvalue, and rvalue.field yields an rvalue. Similarly, in your example, "foo().a = new int;" would be nonsensical, since again, rvalue.field yields an rvalue. But as soon as you use the dereference operator, foo().a becomes an lvalue. (In fact I think K&R describe * and & in such terms (turning lvalues into rvalues and vice versa).) -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 28 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






10:11:26 PDT ---

 Actually the compiler wouldn't have to do anything special here.  If the return
 of foo() were an rvalue, then *foo().a could still be an lvalue, not because of
 any special rules, but because of how the dereference operator works.
Yes, that would be helpful. I think you are right that it can be determined in simple cases, but for sure there will be cases that the compiler cannot diagnose, such as: int _global; struct S { int _x; version(noop) void x(int n) { _x = n;} else void x(int n) { _global = n;} } struct S2 { S foo() { return S(5);} } void main() { S2 s2; s2.foo.x = 5; } How does the compiler know when compiling with noop that the s2.foo.x = 5 doesn't do anything? Especially if the module containing main is using a di file to define S and S2. The result is, I don't think the compiler can diganose the complex cases, and most of the time, the cases are complex. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 29 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008







 
 I think you are right that it can be determined in simple cases, but for sure
 there will be cases that the compiler cannot diagnose, such as:
 
 int _global;
 
 struct S
 {
   int _x;
   version(noop)
     void x(int n) { _x = n;}
   else
     void x(int n) { _global = n;}
 }
 
 struct S2
 {
   S foo() { return S(5);}
 }
 
 void main()
 {
   S2 s2;
   s2.foo.x = 5;
 }
 
 How does the compiler know when compiling with noop that the s2.foo.x = 5
 doesn't do anything?  Especially if the module containing main is using a di
 file to define S and S2.
 
 The result is, I don't think the compiler can diganose the complex cases, and
 most of the time, the cases are complex.
It's easy for the compiler to know that "s2.foo.x = 5" does nothing. When compiling with noop, the "void x(int n) { _global = n;}" version just does not get compiled. Period. When "s2.foo.x = 5;" is being analysed, the compiler will walk the syntax tree for "void x(int n) { _x = n;}" and discover that s2.foo is an rvalue. This is what it already does, minus the "discover that s2.foo is an rvalue" part. Of course, s2.foo().x(5) violates the principle at play here. At this point, the whole "version(noop)" thing is just fluff, and here is the meat of the matter. In this example, "s2.foo.x = 5;" does actually do something and is reasonable code. However, naively forbidding an rvalue on the lhs of an assign expression will make that code fail to compile. I don't feel that code like this is terribly common or that much better than the alternatives, so it is probably worth losing some corner cases like these for the sake of preventing nasty bugs. .di files change absolutely nothing. They are .d files that just happen to be mostly definitions because dmd generated that way. There is nothing in the spec saying that they even need to exist. More importantly, Walter was intentional about omitting them from the spec. "D interface files bear some analogous similarities to C++ header files. But they are not required in the way that C++ header files are, and they are not part of the D language. They are a feature of the compiler, and serve only as an optimization of the build process." http://www.digitalmars.com/d/2.0/dmd-linux.html#interface_files -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






On the newsgroup KennyTM~ pointed out that opDot() also suffers from this
problem:

struct S {
  int s;
}

class X {
  S opDot() { S temp; temp.s = 6; return temp; }
}

X z = new X;
assert(z.s == 6);
z.s = 3;
assert(z.s == 6);

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






07:19:24 PDT ---


 Of course, s2.foo().x(5) violates the principle at play here.  At this point,
 the whole "version(noop)" thing is just fluff, and here is the meat of the
 matter.  In this example, "s2.foo.x = 5;" does actually do something and is
 reasonable code.  However, naively forbidding an rvalue on the lhs of an assign
 expression will make that code fail to compile.  I don't feel that code like
 this is terribly common or that much better than the alternatives, so it is
 probably worth losing some corner cases like these for the sake of preventing
 nasty bugs.  
You are killing the entire feature of making user-defined "builtin" types, such as a custom pointer type. Since those would undoubtedly be structs, and therefore returned as rvalues, you could not use them for anything without first creating lvalues out of them. If we are to have such constructs, they should be on par with native pointers. At the very least, we should have a way to mark such structs as "allow rvalue operations." I would be ok with that. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






---

 
 It's easy for the compiler to know that "s2.foo.x = 5" does nothing.  When
 compiling with noop, the "void x(int n) { _global = n;}" version just does not
 get compiled.  Period.  When "s2.foo.x = 5;" is being analysed, the compiler
 will walk the syntax tree for "void x(int n) { _x = n;}" and discover that
 s2.foo is an rvalue.  This is what it already does, minus the "discover that
 s2.foo is an rvalue" part.
This assumes that the body of x is available for analysis and is simple enough to be analysed. The first will not be true of any C function, any function that calls a C function (etc.), any closed source lib or any function that calls into a closed source lib (etc.). The second will not be true in general because it can devolve into the halting problem. To keep things consistent, the rules used have to be defined in the spec, practicely ruling out powerful heuristics and computation logic systems, and have to act the same regardless of accessability to source, strongly ruling out anything that uses inter-procedural analysis (Walter has several times stated that semantic rules that require analysis of non local code are not going to happen).
 .di files change absolutely nothing.
Technically, yes. .di files don't actually /add/ any problems here because the problems at issue can be caused by a .d file as well.
 They are .d files that just happen to be
 mostly definitions because dmd generated that way.  There is nothing in the
 spec saying that they even need to exist.
Any implementation that doesn't give the same functionality as .di files (no-source symbol decelerations) effectively eliminates the ability to have closed source D libraries. Therefor, the feature causing problems effectively is part of the spec. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008








 
 Of course, s2.foo().x(5) violates the principle at play here.  At this point,
 the whole "version(noop)" thing is just fluff, and here is the meat of the
 matter.  In this example, "s2.foo.x = 5;" does actually do something and is
 reasonable code.  However, naively forbidding an rvalue on the lhs of an assign
 expression will make that code fail to compile.  I don't feel that code like
 this is terribly common or that much better than the alternatives, so it is
 probably worth losing some corner cases like these for the sake of preventing
 nasty bugs.  
You are killing the entire feature of making user-defined "builtin" types, such as a custom pointer type. Since those would undoubtedly be structs, and therefore returned as rvalues, you could not use them for anything without first creating lvalues out of them. If we are to have such constructs, they should be on par with native pointers. At the very least, we should have a way to mark such structs as "allow rvalue operations." I would be ok with that.
You make it sound like we wouldn't be able to use structs anymore! Not the case. struct S { int _x; version(noop) void x(int n) { _x = n;} else void x(int n) { _global = n;} } struct S2 { S foo() { return S(5);} } void main() { S2 s2; s2.foo.x = 5; // not good. int bar = s2.foo.x + 42; // fine, rvalue is not mutated, only read. auto s = s2.foo; s.x = 5; // fine S s1; s1.x = 5; // fine s1._x; // fine, it was declared public. } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008








 
 It's easy for the compiler to know that "s2.foo.x = 5" does nothing.  When
 compiling with noop, the "void x(int n) { _global = n;}" version just does not
 get compiled.  Period.  When "s2.foo.x = 5;" is being analysed, the compiler
 will walk the syntax tree for "void x(int n) { _x = n;}" and discover that
 s2.foo is an rvalue.  This is what it already does, minus the "discover that
 s2.foo is an rvalue" part.
This assumes that the body of x is available for analysis and is simple enough to be analysed. The first will not be true of any C function, any function that calls a C function (etc.), any closed source lib or any function that calls into a closed source lib (etc.). The second will not be true in general because it can devolve into the halting problem. To keep things consistent, the rules used have to be defined in the spec, practicely ruling out powerful heuristics and computation logic systems, and have to act the same regardless of accessability to source, strongly ruling out anything that uses inter-procedural analysis (Walter has several times stated that semantic rules that require analysis of non local code are not going to happen).
Oh my, I made it sound like the body of the thing would be analysed. Poor wording. No that's not the case, I was mostly just referring to the declaration. The only thing that matters there is that it looks at foo's return type, finds an rvalue, and thinks "hmmm, that might mess up someone's day." My point was that it doesn't matter whether it looks at the noop version or the non-noop version, either way it finds the same rvalue in the return and decides to the negative.
 .di files change absolutely nothing.
Technically, yes. .di files don't actually /add/ any problems here because the problems at issue can be caused by a .d file as well.
 They are .d files that just happen to be
 mostly definitions because dmd generated that way.  There is nothing in the
 spec saying that they even need to exist.
Any implementation that doesn't give the same functionality as .di files (no-source symbol decelerations) effectively eliminates the ability to have closed source D libraries. Therefor, the feature causing problems effectively is part of the spec.
.di files and bodyless function declarations, these are two different things. While I'm at a loss to find bodyless function declarations defined anywhere in the spec, they do seem to be /used/ in the spec: http://www.digitalmars.com/d/2.0/function.html#parameters Of course, I was not by any means doing a thorough search. I only checked the obvious tabs "Functions" and "Declarations". (The spec seems hard to search :/ ... needs more targeted search feature and liberal application of metadata.) -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






---



 
 This assumes that the body of x is available for analysis and is simple 
 enough to be analysed. 
Oh my, I made it sound like the body of the thing would be analysed. Poor wording. No that's not the case, I was mostly just referring to the declaration. The only thing that matters there is that it looks at foo's return type, finds an rvalue, and thinks "hmmm, that might mess up someone's day." My point was that it doesn't matter whether it looks at the noop version or the non-noop version, either way it finds the same rvalue in the return and decides to the negative.
OK then that's another (potentially worse) problem because I can think of several cases where I want to return a struct (by value) and invoke functions on it. Based on your rule, I'd always have to store it in a local to do that. I don't see that flying. The only cases I can see being banned is member variable assignment on a struct return.
 Any implementation that doesn't give the same functionality as .di files
 (no-source symbol decelerations) effectively eliminates the ability to have
 closed source D libraries. Therefor, the feature causing problems 
 effectively is part of the spec.
.di files and bodyless function declarations, these are two different things.
Well, as I pointed out, one or the other is needed so, either way, there side effect have to be dealt with. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






11:12:25 PDT ---

 You make it sound like we wouldn't be able to use structs anymore!
 
 Not the case.  
What I mean is this: original: class C { void foo(); } struct S { C getValue() {...} } void main() { S s; s.getValue.foo(); // should be allowed, getValue does not return an rvalue } new version (assume C is the same): struct MyNiftyPointerTo(T) { ... } struct S { MyNiftyPointerTo!(C) getValue() {...} } void main() { S s; s.getValue.foo(); // oops, compiler says MyNiftyPointerTo!(C) is an rvalue because it's a struct. } What I'm saying is you have no ability to make wrapper or extended builtin types because they are now always treated as rvalues even though their semantics are entirely lvalue-ish. Maybe MyNiftyPointerTo!(C) connects to a remote object on a server, and invokes methods there. You're saying this isn't a valuable enough extension to the type system for D to allow it? -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008







 
 OK then that's another (potentially worse) problem because I can think of
 several cases where I want to return a struct (by value) and invoke functions
 on it. Based on your rule, I'd always have to store it in a local to do that. I
 don't see that flying.
 
 The only cases I can see being banned is member variable assignment on a struct
 return.
 
That'd be a start. The problem then is that variable assignment is equivalent to function calls with 1 parameter due to the omitable parentheses rule. Thus if we banned member variable assignment on struct returns, this happens: struct Foo { int x; int y(int val) { return x = val; } } Foo foo() { Foo f; return f; } void main() { foo.x = 5; // compile error, member assignment on returned struct foo.y = 5; // compiles just fine, function invoked on returned struct // (but is a bug waiting to happen) } So yes, getting rid of the former is a step in the right direction. That latter is still quite undesirable.
 
 .di files and bodyless function declarations, these are two different things. 
Well, as I pointed out, one or the other is needed so, either way, there side effect have to be dealt with.
Why does it matter for rvalues though? We aren't analysing the function body, just the declaration, and that's something the compiler has to do anyways to ensure type safety. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






---


 That'd be a start.
 
 The problem then is that variable assignment is equivalent to function calls
 with 1 parameter due to the omitable parentheses rule.  
 
 Thus if we banned member variable assignment on struct returns, this happens:
 
 struct Foo
 {
     int x;
     int y(int val) { return x = val; }
 }
 
 Foo foo()
 {
     Foo f;
     return f;
 }
 
 void main()
 {
     foo.x = 5; // compile error, member assignment on returned struct
     foo.y = 5; // compiles just fine, function invoked on returned struct
                //   (but is a bug waiting to happen)
 }
While I can see where you are coming from, I have no problem at all with that.
 
 Well, as I pointed out, one or the other is needed so, either way, there 
 side
 effect have to be dealt with.
Why does it matter for rvalues though? We aren't analysing the function body, just the declaration, and that's something the compiler has to do anyways to ensure type safety.
See above. Without analyzing the function bodies, Applying all this to functions will also ban things I'm not willing to give up. As an example: should this be alowed: struct S { void M(int arg) { ... } ... } S fn() { ... } fn().M = 5; how about (the equivalent): fn().M(5); how about if I rename it: struct OutputHandle { void Output(int arg) { ... } ... } OutputHandle GetProcessOutput() { ... } GetProcessOutput().Output(5); -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008








 You make it sound like we wouldn't be able to use structs anymore!
 
 Not the case.  
What I mean is this: original: class C { void foo(); } struct S { C getValue() {...} } void main() { S s; s.getValue.foo(); // should be allowed, getValue does not return an rvalue } new version (assume C is the same): struct MyNiftyPointerTo(T) { ... } struct S { MyNiftyPointerTo!(C) getValue() {...} } void main() { S s; s.getValue.foo(); // oops, compiler says MyNiftyPointerTo!(C) is an rvalue because it's a struct. }
Incorrect. s is an lvalue (it isn't being returned) s.getValue is an lvalue (pointer to some C's data, AKA a reference type) s.getValue.foo() function call on an lvalue. fine. s.getValue returns a reference type (the class C). It is the same thing as dereferencing a pointer, only it's hidden by saying that C is a "reference" type. The only thing I'm unsure of is how hard it is to get the compiler to recognize this.
 What I'm saying is you have no ability to make wrapper or extended builtin
 types because they are now always treated as rvalues even though their
 semantics are entirely lvalue-ish.  Maybe MyNiftyPointerTo!(C) connects to a
 remote object on a server, and invokes methods there.  You're saying this isn't
 a valuable enough extension to the type system for D to allow it?
It's not going away because MyNiftyPointerTo!(C).getValue is an lvalue. But I'm going to answer the question anyways. Even if this were a problem, I'd say no. Just type the extra line of code. Being a little less lazy here will pay off. You are a programmer. Unless you are mentally handicapped or had an unfortunate accident that crippled your hands, you type REALLY FAST. Even on a bad day full of nasty symbols that require you to hold shift repeatedly you still type really fast! You probably don't debug complicated code nearly as quickly. So you can have a feature that will save you a fraction of a second here and there by requiring less typing, or you can have an absence of a feature that will save you a few hours here and there by preventing the existence of bugs. Take your pick. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008







 
 See above. Without analyzing the function bodies, Applying all this to
 functions will also ban things I'm not willing to give up.
 
 As an example: should this be alowed:
 
 struct S { void M(int arg) { ... } ... }
 S fn() { ... }
 fn().M = 5;
 
 how about (the equivalent):
 
 fn().M(5);
 
 how about if I rename it:
 
 struct OutputHandle { void Output(int arg) { ... } ... }
 OutputHandle GetProcessOutput() { ... }
 GetProcessOutput().Output(5);
ref returns. They are awesome. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






---



 
 See above. Without analyzing the function bodies, Applying all this to
 functions will also ban things I'm not willing to give up.
 
 As an example: should this be alowed:
 
 struct S { void M(int arg) { ... } ... }
 S fn() { ... }
 fn().M = 5;
 
 how about (the equivalent):
 
 fn().M(5);
 
 how about if I rename it:
 
 struct OutputHandle { void Output(int arg) { ... } ... }
 OutputHandle GetProcessOutput() { ... }
 GetProcessOutput().Output(5);
ref returns. They are awesome.
You assume that the above is a bug and what I really wanted was to return a reference. Take another look while assuming that I actual do want return by value and the return by reference would be a bug. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






13:40:56 PDT ---

 Incorrect.
 
 s is an lvalue (it isn't being returned)
 s.getValue is an lvalue (pointer to some C's data, AKA a reference type)
*struct* MyNiftyPointerTo(T) ... MyNiftyPointerTo!(C) getValue() Read that carefully about 5 times getValue returns a struct. getValue returns an rvalue. Please recognize this before commenting again.
 It's not going away because MyNiftyPointerTo!(C).getValue is an lvalue.
MyNiftyPointerTo!(C) does not have a function called getValue, it is the return type from getValue
 But I'm going to answer the question anyways ...
Incidentally, I can type quite fast and I have never EVER wanted to save keystrokes when it sacrifices clarity. My protests have nothing to do with saving keystrokes, they have to do with generic code -- writing code that works whether something is a builtin or a custom wrapper type. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008







 
 You assume that the above is a bug and what I really wanted was to return a
 reference. Take another look while assuming that I actual do want return by
 value and the return by reference would be a bug.
I suppose what caught me is that it's easily possibly to write GetProcessOutput() such that a ref return isn't a bug. Just make sure the reference doesn't point to an OutputHandle that's already in use. I feel like having a value return there is unnecessary. What I will give here though is that forbidding such function calls would prevent the use of the caller's stack frame as a source of memory storage, but only while using 1 liners to make the call. It's complicated by the fact that the function writer has to make the verbosity/efficiency tradeoff for the caller. Then again... ref OutputHandle GetProcessOutput( OutputHandle* buffer = null ) {...} So it's possible to allow the caller to optionally provide stack space if they want to, and still return ref. If efficiency isn't an issue, it's still a one-liner on the call side. I'll admit that in the given circumstances this is not as clean. Being able to call off of rvalues allows both efficiency and brevity in this case. I'd still rather have something that is a bit more verbose in some cases but nearly impossible to unintentionally create bugs with. It's not like actual functionality is being removed. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008








 Incorrect.
 
 s is an lvalue (it isn't being returned)
 s.getValue is an lvalue (pointer to some C's data, AKA a reference type)
*struct* MyNiftyPointerTo(T) ... MyNiftyPointerTo!(C) getValue() Read that carefully about 5 times getValue returns a struct. getValue returns an rvalue. Please recognize this before commenting again.
 It's not going away because MyNiftyPointerTo!(C).getValue is an lvalue.
MyNiftyPointerTo!(C) does not have a function called getValue, it is the return type from getValue
 But I'm going to answer the question anyways ...
Incidentally, I can type quite fast and I have never EVER wanted to save keystrokes when it sacrifices clarity. My protests have nothing to do with saving keystrokes, they have to do with generic code -- writing code that works whether something is a builtin or a custom wrapper type.

 Incorrect.
 
 s is an lvalue (it isn't being returned)
 s.getValue is an lvalue (pointer to some C's data, AKA a reference type)
*struct* MyNiftyPointerTo(T) ... MyNiftyPointerTo!(C) getValue() Read that carefully about 5 times getValue returns a struct. getValue returns an rvalue. Please recognize this before commenting again.
 It's not going away because MyNiftyPointerTo!(C).getValue is an lvalue.
MyNiftyPointerTo!(C) does not have a function called getValue, it is the return type from getValue
Ah, sorry. I was confused because the meaning of getValue changed and foo was never defined in MyNiftyPointerTo!(T).
 But I'm going to answer the question anyways ...
Incidentally, I can type quite fast and I have never EVER wanted to save keystrokes when it sacrifices clarity. My protests have nothing to do with saving keystrokes, they have to do with generic code -- writing code that works whether something is a builtin or a custom wrapper type.
So MyNiftyPointerTo!(T) may grab all of the compile-time reflection info from T and forward all of its functions and such. Then it becomes a reference to some T, but not a reference in a sense the compiler knows about. I think I can see now where you're going with this. I suppose to really make everyone happy, there would need to be some way to annotate struct definitions and say, "Hey, this thing can be mutated as an rvalue, it's all under control." That probably doesn't make Walter happy though :( Still, without any major change to the language, we can at least forbid the -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






---


 
 You assume that the above is a bug and what I really wanted was to return a
 reference. Take another look while assuming that I actual do want return by
 value and the return by reference would be a bug.
I suppose what caught me is that it's easily possibly to write GetProcessOutput() such that a ref return isn't a bug. Just make sure the reference doesn't point to an OutputHandle that's already in use. I feel like having a value return there is unnecessary.
In most cases you are probably correct but, while I can't enumerate a specific example, I can imagine a cases where the value return us wanted for it's semantics rather than for memory usage reasons. For instance something in the direction of smart pointers or keeping track of distinct handles to a common resource. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 30 2009
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3008






06:11:34 PDT ---


 It's not going away because MyNiftyPointerTo!(C).getValue is an lvalue.
MyNiftyPointerTo!(C) does not have a function called getValue, it is the return type from getValue
Ah, sorry. I was confused because the meaning of getValue changed and foo was never defined in MyNiftyPointerTo!(T).
Yes, I probably could have made that clearer, but the type was supposed to be a functional replacement for C in that it hooks all calls to the underlying type.
 
 But I'm going to answer the question anyways ...
Incidentally, I can type quite fast and I have never EVER wanted to save keystrokes when it sacrifices clarity. My protests have nothing to do with saving keystrokes, they have to do with generic code -- writing code that works whether something is a builtin or a custom wrapper type.
So MyNiftyPointerTo!(T) may grab all of the compile-time reflection info from T and forward all of its functions and such. Then it becomes a reference to some T, but not a reference in a sense the compiler knows about.
Yes. That is a good way of explaining it.
 I think I can see now where you're going with this.  
 
 I suppose to really make everyone happy, there would need to be some way to
 annotate struct definitions and say, "Hey, this thing can be mutated as an
 rvalue, it's all under control."  That probably doesn't make Walter happy
 though :(
Due to the nature of the problem, I don't think there's any way to solve it, as it reduces to the halting problem as BCS says. And limiting it makes the C++). Without this ability, it will be a sticking point for people who don't want to convert :) I think we can pick better battles. However, annotating such structs might be OK. I am unsure how often this annoyance would surface, so I'm unsure how much annotation is required. If you have to annotate more than 25% of structs just to get them to work correctly, then I don't think it's worth it.
 
 Still, without any major change to the language, we can at least forbid the

Yes, I think that should solve quite a few mistakes that people often make, such as the Widget/Rectangle error. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 31 2009