Thread overview | |||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 20, 2004 Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
This, on the other hand, does give a run-time Assert Error, but should be a compile error because the flow of control leaves the function f without returning anything OR throwing an exception. # int f(int x) # { # if (x < 0) ++x; # else return 1; # } # # void main() # { # printf("%d\n", f(-1)); # } |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | On Tue, 20 Jul 2004 08:58:02 +0000 (UTC), Arcane Jill wrote: > This, on the other hand, does give a run-time Assert Error, but should be a compile error because the flow of control leaves the function f without returning anything OR throwing an exception. > > # int f(int x) > # { > # if (x < 0) ++x; > # else return 1; > # } > # > # void main() > # { > # printf("%d\n", f(-1)); > # } I suspect that the reason is that there is a limit to the amount of optimizing that the compiler (not language) is capable of. The current compiler has decided to wait until run-time to catch these types of errors rather than try at compile time. At compile time, it would have to simulate the flow of control whenever it knew the value of the parameters, and record the effect, then at the end of the compile, it would have to go back to see if all calls to each routine were able to be simulated and each call caused a routine to fail to return a value, then it could issue an error message. Probably in the realm of diminishing returns. Consider this type of tracing ... # int f(int x) # { # if (x < 0) ++x; # else return 1; # } # # int x; # void main() # { # x = setX(3); # printf("%d\n", f(x)); # } # # int setX(int y) # { # x = y; # return -2; # } A nasty bit of simulation work there! Now consider multiple modules... -- Derek Melbourne, Australia 20/Jul/04 7:02:48 PM |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Parnell | In article <cdinis$1a3o$1@digitaldaemon.com>, Derek Parnell says... >At compile time, it would have to simulate the flow of control whenever it knew the value of the parameters, and record the effect, then at the end of the compile, it would have to go back to see if all calls to each routine were able to be simulated and each call caused a routine to fail to return a value, then it could issue an error message. Flow of execution analysis is simpler than that. You just have to assume that EVERY branch of an if or switch will be taken at least once; that a for, foreach or while statement may be executed either zero times or at least once; and that nothing following a throw will ever be executed. Then you're sorted. (Compile-time ifs, like if(false), are an exception to that rule, but since they'll known at compile time, the compiler can figure those out anyway). Failing to make this relatively simple analysis results in the current bug - that it is possible to exit a function which requires a return value by running out of scope, instead of via a return statement. It should be noted further that the actual error message emitted by the compiler is: Error: AssertError Failure auto.d(8) I don't /have/ a file called "auto.d". Every assert failure demonstrates a bug. (Walter himself once stated "Arcane Jill, you rock" when I explained this to someone, and encouraged me to post my explanation to the Wiki, so I'm fairly confident he would agree with me on this one). So, whose bug is it? Have I violated the in contract of a DbC function? Nope. Have I written rubbish code? Yes - but why should that get me an assert error from within a file I don't even have? It's not exactly a useful error message. So whose bug is it? Certainly my code /does/ have a bug. The question is, is it (or should it be) legal D? If so, it's a user bug; if not, it's a DMD bug. Let's ask the expert. Walter, is this legal D or not? # int f(int x) # { # if (x<0) ++x; // This branch doesn't execute return # else return x; # } Arcane Jill |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cdimqq$19mc$1@digitaldaemon.com... > This, on the other hand, does give a run-time Assert Error, but should be a > compile error because the flow of control leaves the function f without returning anything OR throwing an exception. In the general case, it is impossible for the compiler to determine the actual flow of control. I generally dislike getting messages from other compilers about "no return statement" for a path through the function that will never happen. To fix it, then, I have to insert a dummy return statement: return some_dummy_value; // this statement will never be executed, but we put it here to get the compiler // to shut up } which is annoying. Putting in the assert, though, neatly solves the problem, because if it ever actually does go on that path, the error is flagged. |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cdit1q$1c15$1@digitaldaemon.com... > In article <cdinis$1a3o$1@digitaldaemon.com>, Derek Parnell says... > > >At compile time, it would have to simulate the flow of control whenever it > >knew the value of the parameters, and record the effect, then at the end of > >the compile, it would have to go back to see if all calls to each routine were able to be simulated and each call caused a routine to fail to return > >a value, then it could issue an error message. > Flow of execution analysis is simpler than that. You just have to assume that > EVERY branch of an if or switch will be taken at least once; that a for, foreach > or while statement may be executed either zero times or at least once; and that > nothing following a throw will ever be executed. Then you're sorted. > > (Compile-time ifs, like if(false), are an exception to that rule, but since > they'll known at compile time, the compiler can figure those out anyway). > > Failing to make this relatively simple analysis results in the current bug - > that it is possible to exit a function which requires a return value by running > out of scope, instead of via a return statement. > > It should be noted further that the actual error message emitted by the compiler > is: > > Error: AssertError Failure auto.d(8) > > I don't /have/ a file called "auto.d". > > Every assert failure demonstrates a bug. (Walter himself once stated "Arcane > Jill, you rock" when I explained this to someone, and encouraged me to post my > explanation to the Wiki, so I'm fairly confident he would agree with me on this > one). So, whose bug is it? Have I violated the in contract of a DbC function? > Nope. Have I written rubbish code? Yes - but why should that get me an assert > error from within a file I don't even have? It's not exactly a useful error > message. So whose bug is it? Certainly my code /does/ have a bug. The question > is, is it (or should it be) legal D? If so, it's a user bug; if not, it's a DMD > bug. > > Let's ask the expert. Walter, is this legal D or not? > > # int f(int x) > # { > # if (x<0) ++x; // This branch doesn't execute return > # else return x; > # } Yes, it's legal. The "auto.d", though, if the file is named something different, then that is a bug. But, this is why I want a complete, reproducible example, because when I do the obvious: ------------------------------------------------------- C:\cbx>type test.d int f(int x) { if (x<0) ++x; // This branch doesn't execute return else return x; } void main() { f(-1); } C:\cbx>dmd test \dm\bin\link test,,,user32+kernel32/noi; C:\cbx>test Error: AssertError Failure test.d(5) C:\cbx> ---------------------------------------------------------------- you can see that it prints the correct file name in the assert, so there's something crucial left out of the bug report. |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <newshound@digitalmars.com> wrote in message news:cdjhp8$1m1l$1@digitaldaemon.com... > > "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cdit1q$1c15$1@digitaldaemon.com... > > In article <cdinis$1a3o$1@digitaldaemon.com>, Derek Parnell says... > > > > >At compile time, it would have to simulate the flow of control whenever > it > > >knew the value of the parameters, and record the effect, then at the end > of > > >the compile, it would have to go back to see if all calls to each routine > > >were able to be simulated and each call caused a routine to fail to > return > > >a value, then it could issue an error message. > > Flow of execution analysis is simpler than that. You just have to assume > that > > EVERY branch of an if or switch will be taken at least once; that a for, > foreach > > or while statement may be executed either zero times or at least once; and > that > > nothing following a throw will ever be executed. Then you're sorted. > > > > (Compile-time ifs, like if(false), are an exception to that rule, but > since > > they'll known at compile time, the compiler can figure those out anyway). > > > > Failing to make this relatively simple analysis results in the current > bug - > > that it is possible to exit a function which requires a return value by > running > > out of scope, instead of via a return statement. > > > > It should be noted further that the actual error message emitted by the > compiler > > is: > > > > Error: AssertError Failure auto.d(8) > > > > I don't /have/ a file called "auto.d". > > > > Every assert failure demonstrates a bug. (Walter himself once stated > "Arcane > > Jill, you rock" when I explained this to someone, and encouraged me to > post my > > explanation to the Wiki, so I'm fairly confident he would agree with me on > this > > one). So, whose bug is it? Have I violated the in contract of a DbC > function? > > Nope. Have I written rubbish code? Yes - but why should that get me an > assert > > error from within a file I don't even have? It's not exactly a useful > error > > message. So whose bug is it? Certainly my code /does/ have a bug. The > question > > is, is it (or should it be) legal D? If so, it's a user bug; if not, it's > a DMD > > bug. > > > > Let's ask the expert. Walter, is this legal D or not? > > > > # int f(int x) > > # { > > # if (x<0) ++x; // This branch doesn't execute return > > # else return x; > > # } > > Yes, it's legal. The "auto.d", though, if the file is named something different, then that is a bug. But, this is why I want a complete, reproducible example, because when I do the obvious: So you are saying that it is legal for an "int" function not to return an int, but it has to have atleast one return even though it might be skiped as int this example: int func() { goto label; return 0; label: ; } > ------------------------------------------------------- > C:\cbx>type test.d > int f(int x) > { > if (x<0) ++x; // This branch doesn't execute return > else return x; > } > > void main() > { > f(-1); > } > > C:\cbx>dmd test > \dm\bin\link test,,,user32+kernel32/noi; > > C:\cbx>test > Error: AssertError Failure test.d(5) > > C:\cbx> > ---------------------------------------------------------------- > you can see that it prints the correct file name in the assert, so there's something crucial left out of the bug report. > > |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <newshound@digitalmars.com> wrote in message news:cdjgje$1lju$1@digitaldaemon.com... > > "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cdimqq$19mc$1@digitaldaemon.com... > > This, on the other hand, does give a run-time Assert Error, but should be > a > > compile error because the flow of control leaves the function f without returning anything OR throwing an exception. > > In the general case, it is impossible for the compiler to determine the actual flow of control. Don't be angry when i say this but: i don't believe you :) You are a compiler writer, but from what i know/remember when learning about compilers i know that a compiler can know about every possible flow (i remember we were drawing some diagrams :) so it could forse every flow to end with return or throw. > I generally dislike getting messages from other > compilers about "no return statement" for a path through the function that > will never happen. So you dislike dmd? It does just that, complains about "no return" even though it will never happen. > To fix it, then, I have to insert a dummy return statement: > > return some_dummy_value; // this statement will never be executed, > but we put it here to get the compiler > // to shut up > } > > which is annoying. > > Putting in the assert, though, neatly solves the problem, because if it ever > actually does go on that path, the error is flagged. ->Runtime error finding - could be compile time. |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | Walter wrote: <snip> > In the general case, it is impossible for the compiler to determine > the actual flow of control. I generally dislike getting messages from > other compilers about "no return statement" for a path through the > function that will never happen. To fix it, then, I have to insert a > dummy return statement: Which is actually more likely: a can't happen situation or a coding error? > return some_dummy_value; // this statement will never be executed, > but we put it here to get the compiler > // to shut up > } > > which is annoying. Or putting in an assert(false) at that point, which is better if you want to be able to catch the bug. > Putting in the assert, though, neatly solves the problem, because if > it ever actually does go on that path, the error is flagged. Requiring either a return or an assert(false) is even neater IMO, as it would then be easy to pinpoint the missing return, and would force the user to check whether it really is an error or a can't happen. Stewart. -- My e-mail is valid but not my primary mailbox, aside from its being the unfortunate victim of intensive mail-bombing at the moment. Please keep replies on the 'group where everyone may benefit. |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ivan Senji | "Ivan Senji" <ivan.senji@public.srce.hr> wrote in message news:cdjimt$1mfr$1@digitaldaemon.com... > So you are saying that it is legal for an "int" function not to return an int, but it has to have atleast one return even though it might be skiped as int this example: > > int func() > { > goto label; > return 0; > label: > ; > } It is illegal to drop off the end without a return and to execute such a path. |
July 20, 2004 Re: Not returning a value when you're supposed to | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ivan Senji | "Ivan Senji" <ivan.senji@public.srce.hr> wrote in message news:cdjimu$1mfr$2@digitaldaemon.com... > "Walter" <newshound@digitalmars.com> wrote in message news:cdjgje$1lju$1@digitaldaemon.com... > > > > "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cdimqq$19mc$1@digitaldaemon.com... > > > This, on the other hand, does give a run-time Assert Error, but should > be > > a > > > compile error because the flow of control leaves the function f without > > > returning anything OR throwing an exception. > > > > In the general case, it is impossible for the compiler to determine the actual flow of control. > > Don't be angry when i say this but: i don't believe you :) > You are a compiler writer, but from what i know/remember when learning > about compilers i know that a compiler can know about every possible > flow (i remember we were drawing some diagrams :) so it could forse > every flow to end with return or throw. One common case for this is: int func() { while (1) { if (...) return 0; } } which will get you an error from many of today's most modern compilers. It's possible for flow analysis to figure this out, but I don't want there to be some D compilers that accept it and some that issue an error. Compilation errors need to be consistent. There's also: extern int x; int func() { if (x == 1) return 0; else if (x == 3) return 2; } where flow analysis cannot determine that x will never be 28. BTW, in the first example, the optimizer *does* figure out that the fall-off return will never be executed, and so removes the assert as 'dead code'. |
Copyright © 1999-2021 by the D Language Foundation