www.digitalmars.com         C & C++   DMDScript  

D - missing return

reply "Pavel Minayev" <evilone omen.ru> writes:
A suggestion: in debug versions, throw a "missing return" exception
at the very end of all functions - much like you did with switch
statement. This would aid to catch such bugs at run-time, if they
were missed at compile-time - like it happens often:

    int foo(int n)
    {
        if (n > 0xffffffff)
            return 1;
    }

This piece of code compiles without errors... =)
Feb 24 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
This code should not compile without errors...at the very least, it
should have a warning.  Frankly, my general thought is that having paths
that don't return should be an error, not a warning.  I know, it makes
calls to exec() and such ugly, but not that bad...

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Feb 24 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C79C017.D84FCBC3 deming-os.org...

 This code should not compile without errors...at the very least, it
 should have a warning.  Frankly, my general thought is that having paths
 that don't return should be an error, not a warning.  I know, it makes
 calls to exec() and such ugly, but not that bad...
Yes, but compiler cannot catch all the situations where return is missing in one of the branches. Raising an exception at the end of function in debug version would uncover some of those hidden bugs.
Feb 24 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a5cg76$10qn$1 digitaldaemon.com...
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3C79C017.D84FCBC3 deming-os.org...

 This code should not compile without errors...at the very least, it
 should have a warning.  Frankly, my general thought is that having paths
 that don't return should be an error, not a warning.  I know, it makes
 calls to exec() and such ugly, but not that bad...
Yes, but compiler cannot catch all the situations where return is missing in one of the branches. Raising an exception at the end of function in debug version would uncover some of those hidden bugs.
It is a good idea. Some functions don't return, and it's irritating to have to put in dummy returns to satisfy the compiler.
Feb 24 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a5cp04$15bt$2 digitaldaemon.com...

 It is a good idea. Some functions don't return, and it's irritating to
have
 to put in dummy returns to satisfy the compiler.
The compiler should still try to detect missing returns at compile-time. However, my position is that compiler should _never_ raise an error in the code that is actually right. It's very hard to write such an analyzer that does such a thing, but any algorithm actually used should never assume things like "every function must have a return in its block" (which earlier Borland C++ did).
Feb 25 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:a5cp04$15bt$2 digitaldaemon.com...

 It is a good idea. Some functions don't return, and it's irritating to
have
 to put in dummy returns to satisfy the compiler.
The compiler should still try to detect missing returns at compile-time. However, my position is that compiler should _never_ raise an error in the code that is actually right. It's very hard to write such an analyzer that does such a thing, but any algorithm actually used should never assume things like "every function must have a return in its block" (which earlier Borland C++ did).
This is a fair opinion, but I think that such code is remarkably rare. IMHO, it's easier to add false returns with a "this is here to make the compiler happy comments" than to try to track down missing returns. Of course, your throw-exception-on-lack-of-return has merit, but it seems unnecessary when programmers are already to compile-time reports. Compile-time reports, while they have some "false positives", also are completely thorough...there is no guarantee that you will run down all of the possible paths in your debug build. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A32FB.4BB1CF2C deming-os.org...

 This is a fair opinion, but I think that such code is remarkably rare.
 IMHO, it's easier to add false returns with a "this is here to make the
 compiler happy comments" than to try to track down missing returns.
The problem is, how do you strictly define this rule (about missing returns)? If different compilers interpret it differently, there's a risk of creating an absolutely valid D code that turns out to be non-portable because other compilers cannot properly check that all branches have returns in them, and assume there's a missing one... Also, not all compilers are able to optimize away false returns simply because they don't know these are false.
Feb 25 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3C7A32FB.4BB1CF2C deming-os.org...

 This is a fair opinion, but I think that such code is remarkably rare.
 IMHO, it's easier to add false returns with a "this is here to make the
 compiler happy comments" than to try to track down missing returns.
The problem is, how do you strictly define this rule (about missing returns)? If different compilers interpret it differently, there's a risk of creating an absolutely valid D code that turns out to be non-portable because other compilers cannot properly check that all branches have returns in them, and assume there's a missing one...
I guess I'm not understanding what you're getting at here. Can you explain or give example code? It seems (at first glance) that it would be trivial to see if all code paths return a value...
 Also, not all compilers are able to optimize away false returns
 simply because they don't know these are false.
Ok, I can see optimization issues here... -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
next sibling parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A6D64.853E54E8 deming-os.org...
 Pavel Minayev wrote:

 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3C7A32FB.4BB1CF2C deming-os.org...

 This is a fair opinion, but I think that such code is remarkably rare.
 IMHO, it's easier to add false returns with a "this is here to make
the
 compiler happy comments" than to try to track down missing returns.
The problem is, how do you strictly define this rule (about missing returns)? If different compilers interpret it differently, there's a risk of creating an absolutely valid D code that turns out to be non-portable because other compilers cannot properly check that all branches have returns in them, and assume there's a missing one...
I guess I'm not understanding what you're getting at here. Can you
explain
 or give example code?  It seems (at first glance) that it would be trivial
to
 see if all code paths return a value...
Look at this: int missing_return(int code) { switch(code) { case -1: return 10; case 0: return 20; case 1: return 30; } } Now, the programmer has omitted a "default:" case because he is confident that this function will only be called with -1, 0, or 1 - and let's assume he's actually right this time. :-) But the compiler doesn't know; it thinks there's another possible code path, that falls out of the switch and finds a valued-return missing. Now, if the programmer's debugging, there might be a faulty call to this function. So inserting code to throw an exception if the end of the function is reached would be better than simply returning an unknown value. You get the same effect by just inserting a debug-conditional statement that throws. But it might be nice if the compiler did it, so the programmer doesn't have to remember it all the time. -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Feb 25 2002
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Richard Krehbiel wrote:

 int missing_return(int code)
 {
    switch(code)
    {
    case -1:
       return 10;
    case 0:
       return 20;
    case 1:
       return 30;
    }
 }
A compiler can easily detect a missing default case and issue the error. In fact, this is a good error checking mechanism: throw Exception("unexpected value passed to missing_return()"); Of course, it would be even better to use an in-condition stating the valid range of the variable!
 Now, the programmer has omitted a "default:" case because he is confident
 that this function will only be called with -1, 0, or 1 - and let's assume
 he's actually right this time.  :-)  But the compiler doesn't know; it
 thinks there's another possible code path, that falls out of the switch and
 finds a valued-return missing.
If there was an explicit in-case stating the range, then there *is* a return value for the "invalid" cases - it is to throw an exception from the in-block. Thus, a D compiler doesn't face this problem (though a C compiler does).
 Now, if the programmer's debugging, there might be a faulty call to this
 function.  So inserting code to throw an exception if the end of the
 function is reached would be better than simply returning an unknown value.

 You get the same effect by just inserting a debug-conditional statement that
 throws.  But it might be nice if the compiler did it, so the programmer
 doesn't have to remember it all the time.
Makes sense. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
prev sibling next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A6D64.853E54E8 deming-os.org...
 I guess I'm not understanding what you're getting at here.  Can you
explain
 or give example code?  It seems (at first glance) that it would be trivial
to
 see if all code paths return a value...
int foo(int i) { if (i) return bar(); else exit(0); } Putting in a dummy return after the exit(0) will satisfy the compiler demanding a return, but is a clarity/maintenance problem because the person reading the code will be misled by the existence of code that is never intended to be executed.
Feb 25 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:

 Putting in a dummy return after the exit(0) will satisfy the compiler
 demanding a return, but is a clarity/maintenance problem because the person
 reading the code will be misled by the existence of code that is never
 intended to be executed.
Right, understood. But then again, can exit() fail? I don't think that exit() can, but others (like exec()) can. Personally, I prefer to force the explicit (unused) return (provided that it is well commented). However, I understand that others have other opinions, and I think it's time I bow out. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:a5drkm$1nbf$2 digitaldaemon.com...
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3C7A6D64.853E54E8 deming-os.org...
 I guess I'm not understanding what you're getting at here.  Can you
explain
 or give example code?  It seems (at first glance) that it would be
trivial
 to
 see if all code paths return a value...
int foo(int i) { if (i) return bar(); else exit(0); } Putting in a dummy return after the exit(0) will satisfy the compiler demanding a return, but is a clarity/maintenance problem because the
person
 reading the code will be misled by the existence of code that is never
 intended to be executed.
One can argue that the function should be rewritten as so: int foo(int i) { if (i) return bar(); exit(0); } One could also argue that exit(0) is dangerous and should be avoided; use throw instead. If we could flag functions with some attribute (like "never returns") the compiler would be able to understand what's going on above. Sean
Feb 25 2002
parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <spalmer iname.com> wrote in message
news:a5e1la$1q7p$1 digitaldaemon.com...
 If we could flag functions with some attribute (like "never returns") the
 compiler would be able to understand what's going on above.
True, but I like Pavel's idea of having the compiler insert a hidden throw there. To me, it solves the problem rather neatly!
Feb 25 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A6D64.853E54E8 deming-os.org...

 I guess I'm not understanding what you're getting at here.  Can you
explain
 or give example code?  It seems (at first glance) that it would be trivial
to
 see if all code paths return a value...
int foo() { if (something) return 1; else return 2; } int bar() { int n = foo(); if (n == 1) return 3; else if (n == 2) return 4; } As you can see, we know that foo() can only return 1 or 2 - so our version of bar is safe. However, the compiler might have a problem checking this.
Feb 25 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

     int foo()
     {
         if (something)
             return 1;
         else
             return 2;
     }

     int bar()
     {
         int n = foo();
         if (n == 1)
             return 3;
         else if (n == 2)
             return 4;
     }

 As you can see, we know that foo() can only return 1 or 2 - so our version
 of bar is safe. However, the compiler might have a problem checking this.
Specify an out-condition then? -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Russ Lewis wrote:

 Specify an out-condition then?
d'oh! I just said I'd bow out...well, at the very least, I won't offer any more opinions, if that's possible :) -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Feb 25 2002
prev sibling next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A79B2.6BAF3DEF deming-os.org...

 As you can see, we know that foo() can only return 1 or 2 - so our
version
 of bar is safe. However, the compiler might have a problem checking
this.
 Specify an out-condition then?
Out-condition is not necessary a constant expression. In fact, there might be a rather complex block checking validity of return value... But in our case it could be as simple as: out (result) { assert(result == a || result == b); // where a turns up being 1, and b is 2 }
Feb 25 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A79B2.6BAF3DEF deming-os.org...
 Specify an out-condition then?
Won't be reliable because the return value will be garbage that might randomly look like valid data.
Feb 25 2002
prev sibling parent "OddesE" <OddesE_XYZ hotmail.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3C7A32FB.4BB1CF2C deming-os.org...
 Pavel Minayev wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:a5cp04$15bt$2 digitaldaemon.com...

 It is a good idea. Some functions don't return, and it's irritating to
have
 to put in dummy returns to satisfy the compiler.
The compiler should still try to detect missing returns at compile-time. However, my position is that compiler should _never_ raise an error in the code that is actually right.
Agreed. A computer shouldn't tell me I am wrong when I am not, that is soooo irritating... *Word spell checker nightmares coming back to me* :)
 It's very hard to write such an analyzer that does such a
 thing, but any algorithm actually used should never assume
 things like "every function must have a return in its block"
 (which earlier Borland C++ did).
This is a fair opinion, but I think that such code is remarkably rare. IMHO, it's easier to add false returns with a "this is here to make the compiler happy comments" than to try to track down missing returns.
Nah...That just seems to me like a very ugly hack! How about warnings instead of errors though?
 Of course, your throw-exception-on-lack-of-return has merit, but it seems
 unnecessary when programmers are already to compile-time reports.
 Compile-time reports, while they have some "false positives", also are
 completely thorough...there is no guarantee that you will run down all of
 the possible paths in your debug build.
Agreed, that is a problem. So why not combine them? Issue a warning which you may ignore if you so wish, and in the debug build, throw exceptions on code that should return but doesn't.This should catch almost all cases of forgetting to return in some control paths, while not forcing changes in the code just to satisfie the compiler. Optionally, compilers could have an option to treat warnings as errors, like some C/C++ compilers do.
 --
 The Villagers are Online! http://villagersonline.com

 .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
 .[ (a version.of(English).(precise.more)) is(possible) ]
 ?[ you want.to(help(develop(it))) ]
-- Stijn OddesE_XYZ hotmail.com http://OddesE.cjb.net __________________________________________ Remove _XYZ from my address when replying by mail
Feb 25 2002