www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Discarded return values

reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
Is there a way to get a compile error when returning a temporary 
from a function and then not assigning it to a variable or 
passing it to a different function? E.g:

struct S {
     int[] a;
     void morph() {}
}

 warnOnDiscard
S foo() {
     return S([1,2,3]);
}

unittest {
     auto a = foo(); // Fine, assigned to variable.
     bar(foo());     // Fine, passed to other function.
     foo();          // Error: return value is discarded.
     foo().morph();  // Error: return value is discarded.
}

I know that pure functions give errors when their results are 
discarded, but in this case foo() might not be pure.
--
   Simen
Jan 30 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, January 30, 2018 10:49:54 Simen Kjærås via Digitalmars-d-learn 
wrote:
 Is there a way to get a compile error when returning a temporary
 from a function and then not assigning it to a variable or
 passing it to a different function? E.g:

 struct S {
      int[] a;
      void morph() {}
 }

  warnOnDiscard
 S foo() {
      return S([1,2,3]);
 }

 unittest {
      auto a = foo(); // Fine, assigned to variable.
      bar(foo());     // Fine, passed to other function.
      foo();          // Error: return value is discarded.
      foo().morph();  // Error: return value is discarded.
 }

 I know that pure functions give errors when their results are
 discarded, but in this case foo() might not be pure.
Well, the first problem is that you simply can't warn or give an error about that if the function isn't pure, because if it isn't pure, then it could be doing work, and it might be perfectly okay for the return value to be ignored. So, while it might make sense for some functions, it wouldn't make sense for others, and unless it never makes sense to ignore the return value, the compiler can't reasonably warn about it. Having an attribute that tells the compiler that it's not okay to ignore the return value for that specific function could solve that problem, because it would give the programmer a way to tell the compiler that that particular function is not doing work such that it's reasonable to ignore the return value, but that brings up the second problem which is that no such attribute exists and that there is no way to create attributes to tell the compiler to warn about something. UDAs are simply there to be used by code introspection done by user code. The compiler itself doesn't do anything interesting with them. So, in order to get something like that, a DIP would be required. IIRC, the Weka guys wanted to be able to have attributes tell the compiler stuff so that it could yell at the programmer when appropriate, so I think that there is some interest in this area, but I have no idea how such a thing would be implemented. New built-in attributes that warn about specific stuff could certainly be added, but in each case, there would have to be a solid enough argument as to why it was worth adding that further complication to the language. I have no idea what the chances of being accepted would be for a DIP specifically for an attribute to warn if the return value is ignored, but you can certainly create such a DIP if you feel strongly enough about it and feel that you can argue it well. - Jonathan M Davis
Jan 30 2018
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 30 January 2018 at 11:36:25 UTC, Jonathan M Davis 
wrote:
 [nope]
Thanks. It's what I thought, though not what I wanted.
 IIRC, the Weka guys wanted to be able to have attributes tell 
 the compiler stuff so that it could yell at the programmer when 
 appropriate, so I think that there is some interest in this 
 area, but I have no idea how such a thing would be implemented. 
 New built-in attributes that warn about specific stuff could 
 certainly be added, but in each case, there would have to be a 
 solid enough argument as to why it was worth adding that 
 further complication to the language. I have no idea what the 
 chances of being accepted would be for a DIP specifically for 
 an attribute to warn if the return value is ignored, but you 
 can certainly create such a DIP if you feel strongly enough 
 about it and feel that you can argue it well.
It's been on my mind for years, so it probably will happen eventually. Can't promise this is the year, though. -- Simen
Jan 30 2018
prev sibling parent reply bauss <jj_1337 live.dk> writes:
On Tuesday, 30 January 2018 at 10:49:54 UTC, Simen Kjærås wrote:
 Is there a way to get a compile error when returning a 
 temporary from a function and then not assigning it to a 
 variable or passing it to a different function? E.g:

 struct S {
     int[] a;
     void morph() {}
 }

  warnOnDiscard
 S foo() {
     return S([1,2,3]);
 }

 unittest {
     auto a = foo(); // Fine, assigned to variable.
     bar(foo());     // Fine, passed to other function.
     foo();          // Error: return value is discarded.
     foo().morph();  // Error: return value is discarded.
 }

 I know that pure functions give errors when their results are 
 discarded, but in this case foo() might not be pure.
 --
   Simen
On top of what's said. It would be very complex to implement properly. For example the following should fail, but will be very hard for the compiler to actually track. struct S { int[] a; void morph() {} } warnOnDiscard S foo() { return S([1,2,3]); } void d(S s) { } unittest { auto a = foo(); // This should fail, because a is never used. // This should also fail, because b is never used actually used afterwards. auto b = foo() b.morph(); // This should fail, because d does not use c. // A simply analysis of d will not work, you'll have to track all passed values to d and whether they themselves should be tracked with the attribute. auto c = foo(); d(c); // Same as above, should fail too since d doesn't use the return value d(foo()); } The above is going to be very complex to implement in the compiler. Sure a simple check if it has just been assigned to a variable would work fine, but in the reality people would most likely just do something like this then: auto unused = foo(); And there ... we "hacked" our way around the attribute. So for it to work it has to track every single return value from a function that implements the attribute and that is borderline impossible to get correct.
Jan 30 2018
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 30 January 2018 at 14:01:00 UTC, bauss wrote:
 unittest {
     auto a = foo();          // This should fail, because a is 
 never used.
No it shouldn't. It is assigned to a variable, as the constraint said.
     // This should also fail, because b is never used actually 
 used afterwards.
     auto b = foo()
     b.morph();
No it shouldn't. It is assigned to a variable, as the constraint said.
    // This should fail, because d does not use c.
    // A simply analysis of d will not work, you'll have to 
 track all passed values to d and whether they themselves should 
 be tracked with the attribute.
    auto c = foo();
    d(c);
No it shouldn't. It is assigned to a variable, as the constraint said.
   // Same as above, should fail too since d doesn't use the 
 return value
   d(foo());
No it shouldn't. It is passed to a function, as the constraint said.
 }

 The above is going to be very complex to implement in the 
 compiler.

 Sure a simple check if it has just been assigned to a variable 
 would work fine, but in the reality people would most likely 
 just do something like this then:

 auto unused = foo();

 And there ... we "hacked" our way around the attribute.
Yes, that's the point. Now it's visible that you're discarding the result. The point isn't actually to make sure people keep the variable around - that'd be silly - it's to inform the programmer that potentially important information is discarded, and to stop the programmer from calling mutating functions on a temporary return value where it will have no effect. For pure functions, this gives a warning: auto fun() pure { return 3; } unittest { fun(); // calling without side effects discards return value } Does that in any way whatsoever stop me from assigning the return value to a variable and then ignoring it? No. But doing so would make me an idiot. The compiler suggests I cast it to void if I really intended to discard the result. The reason for this is cast(void) is relatively easy to grep for, and sticks out like a sore thumb when looking through the code.
 So for it to work it has to track every single return value 
 from a function that implements the attribute and that is 
 borderline impossible to get correct.
No it's not. It's *perfectly* impossible the way your scheme works. What does this function do to its parameters? extern(C) void foo(S s); In other words, your idea of perfectly tracking how a value is used cannot be implemented at all. Trying to equate that with what I'm asking is disingenuous. -- Simen
Jan 30 2018