August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgkbn8$4pr$1@digitaldaemon.com... > In article <cgkaql$4gq$1@digitaldaemon.com>, Walter says... > > >I don't understand why a thrown exception with an appropriate message is such an anathema. They're good things, they tell you in no uncertain terms > >that there's a bug in the program, and where that bug is. > > You're completely missing the point. Your statement above is obviously true, and > no-one is disagreeing with you. All you're doing is stating the obvious. But > it's irrelevant, so I can only assume that you're just not /getting/ what we're > complaining about. > > THIS SHOULD NOT COMPILE: > > # int f(int n) > # { > # if (n == 1) return 1; > # } > > I want the error reported /at compile time/, not at run-time. I want the compiler to tell me "not all execution paths return a value" /at compile time/. > And if the way to make the compile-error go away is to change the code to: > > # int f(int n) > # { > # if (n == 1) return 1; > # noreturn; > # } > That is exactly what i would like too. Except i would call it unreachable; :) But i could live with noreturn also. Throwing some UnreachableCodeReachedException sure is better then an assert that doesn't even exist in release and we get undefined behaviour. > Then what is wrong with that? Furthermore, it should be a compile-time error to > place any code after "return", "noreturn", "throw", "goto" or "break", unless > there's a label or a right-brace. The error message should be "Unreachable code > at file xxxx line xxxx". > > > >The noreturn > >keyword would be redundant - a closing } without a return exp; for a value > >returning function implicitly specifies that it's an error to fall off the > >end. All the language does is make such errors impossible to overlook. If the assert message is "NoReturnException file foo.d line 1234", isn't that > >plenty? > > The error message is not a problem (although it should be present in release > builds, not just debug builds). The problem is that DMD compiles code which > should not compile. > > Arcane Jill > > |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | Walter wrote:
> "Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message
> news:cgk46j$209$3@digitaldaemon.com...
>
>>"Walter" <newshound@digitalmars.com> wrote in message
>
> news:cgk28d$3i$1@digitaldaemon.com...
>
>>>But the compiler requiring a superfluous return statement is not finding
>
> a
>
>>>bug nor is it helping find a bug. It is, often enough, hiding a bug.
>
> I've
>
>>>often had to insert such superfluous returns to satisfy various C++
>>>compilers.
>>
>>I've never had to insert one. I've always been able to sensibly reorder
>
> the code such that the return was both natural
>
>>and valid.
>
>
> I've had to insert them for things like:
>
> int func()
> {
> while (1)
> { ...
> if (condition) return value;
> ...
> }
> }
Microsoft's C# compiler handles this easily enough, but fails if the while condition is anything remotely complicated. For instance, this fails to compile:
public int thingie() {
int q = 0;
// while (true) { // OK
while (q == q) { // Not OK
Console.WriteLine("q={0}", q);
if (q == 4) return q;
q++;
}
}
This is more than good enough for me. If DMD were to do the same, its users would be forced to stick an "assert(false);" at the end to make the compiler happy, but I think that's fine because it is a bit more than just shutting the compiler up. The explicit annotation is useful to both people and the compiler, and, while the resulting execution flow is identical, the author is forced to be aware of the potential for a fall-off return.
There is certainly the possibility that this will be forced on a programmer even when it is clear as day to him that it can never happen, but everything is clear as day when writing the code. It is, after all, the easiest part. Besides, this is exactly what the assert statement is meant for. :)
-- andy
|
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andy Friesen | In article <cgktoo$d5c$1@digitaldaemon.com>, Andy Friesen says... >If DMD were to do the same, its users would be forced to stick an "assert(false);" at the end Almost. But # assert(false) is only equivalent to # debug # { # throw newAssertException(); # } so there is still a release-build control-path which falls straight through. This also needs to be explicitly blocked. Matthew's "noreturn" idea (or perhaps "unreachable", as has also been suggested) should be executed in both debug and non-debug builds, and so is preferable to "assert". Jill |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | In article <cgkaql$4gq$1@digitaldaemon.com>, Walter says... > >I don't understand why a thrown exception with an appropriate message is such an anathema. They're good things, they tell you in no uncertain terms that there's a bug in the program, and where that bug is. The noreturn keyword would be redundant - a closing } without a return exp; for a value returning function implicitly specifies that it's an error to fall off the end. All the language does is make such errors impossible to overlook. If the assert message is "NoReturnException file foo.d line 1234", isn't that plenty? I think it is. Though I'd prefer an assert to an exception in debug builds because it would simplify debugging of the problem. And if it already works this way then I have no complaints. Sean |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | Arcane Jill wrote:
> In article <cgktoo$d5c$1@digitaldaemon.com>, Andy Friesen says...
>
>>If DMD were to do the same, its
>>users would be forced to stick an "assert(false);" at the end
>
> Almost. But
>
> # assert(false)
>
> is only equivalent to
>
> # debug
> # {
> # throw newAssertException();
> # }
>
> so there is still a release-build control-path which falls straight through. This also needs to be explicitly blocked. Matthew's "noreturn" idea (or perhaps "unreachable", as has also been suggested) should be executed in both debug and non-debug builds, and so is preferable to "assert".
>
> Jill
Ok, so here's how I understand the debate so far:
- The compiler would catch every instance where there could be a mssing
return code path, or barring that, a large amount of effort is expended to
make it as accurate as possible.
- The programmer could always find a way to not miss any potential return
paths, and make them all meaningful, as well as not have to spend hours
rearranging code to do so. Barring that, the programmer can now throw an
exception at the bottom of a function and make the exception verbage as
meaningful as they want.
Then the 'noreturn' idea was introduced:
- The compiler would check for 'noreturn' _or_ 'return ...' at the end of
all code paths, making the compiler's job a bit more complicated still.
- The programmer could always find a way to not miss any potential return
paths, and make all paths meaningful, as well as not have to spend hours to
do so. Now they would also have the choice to learn about, remember and use
the non-descript 'noreturn' instruction instead of just typing a little
more to throw an exception that they could make as meaningful as they want.
At this point, I don't see the value of 'noreturn' over an implicit exception, because 'noreturn' wouldn't be any more expressive but would require extra compiler and programmer support.
IMHO, the problem with expending a bunch of effort for a really accurate compiler is that it may well still miss the hard cases that the programmer missed in the first place (because they were hard) since it cannot be perfect. Plus the effort was expended that could have been used to make the tool better in more meaningful places, not to mention slowing down the compile speed, etc.
Besides, the semantic analysis parts of C++ compilers have the advantage of many man-years and dollars worth of development under their belt.
Sounds to me like we're getting little or no value for adding complexity to the language and the compiler if we start adding new "exception shortcut keywords" or diligantly try to make the compiler conform to an explicit return path spec.
I think Walter is half-way to a solution now.. Halfway being checking obvious cases at compile time and doing implicit asserts for non-release builds, the other half would be implicit exceptions for release builds, _IF_ they don't screw up performance, which I think may be attainable w/o a lot of effort.
- Dave
|
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave | In article <cgl7d2$fnm$1@digitaldaemon.com>, Dave says... >- The compiler would catch every instance where there could be a mssing return code path, or barring that, a large amount of effort is expended to make it as accurate as possible. It's not actually a large amount of effort. Basically, wherever you have an if(), you have to assume that there will be a path of execution for the true case, and a path for the false case. Similarly, whenever you have a switch(), you have to assume that there is a path of execution for every case, including default. Likewise, when you have a while() or a for(), you have to assume that there is a path of execution through the body, and a path which skips the body. And, you have to be aware that code which follows "return", "goto", "throw", etc., is unreachable. That's pretty much it. This approach may generate false positives, which is why Walter doesn't like it. It would mean that the compiler would say "Not all execution paths return" when a human might know otherwise. It depends whether you consider that more of a problem than the current alternative. >- The compiler would check for 'noreturn' _or_ 'return ...' at the end of all code paths, making the compiler's job a bit more complicated still. I can't argue with that. But in fact the compiler should (in my opinion, but not Walter's) count "throw" as equivalent to "return", in that it can be used to exit the function. "noreturn" is not very descriptive, I agree, but "unreachable" has been suggested as an alternative. As you say, it would just be syntactic sugar for "throw" with some suitable exception. >At this point, I don't see the value of 'noreturn' over an implicit exception, because 'noreturn' wouldn't be any more expressive but would require extra compiler and programmer support. True. Again, it's just syntactic sugar, and it isn't really important. "throw" works for me just as well. Primarily it's Walter's "false negatives are better than false positives" philosophy with which I and others disagree. >IMHO, the problem with expending a bunch of effort for a really accurate compiler is that it may well still miss the hard cases that the programmer missed in the first place (because they were hard) since it cannot be perfect. Really accurate is hard, yes. But if you can live with false positives, you can live with a less accurate analysis. As noted above, the simple analysis is not hard. >the other half would be implicit exceptions for release builds, >_IF_ they don't screw up performance, which I think may be attainable w/o a >lot of effort. There is no way that an unreachable throw statement can hurt performance. By definition, if it's unreachable, it will never be executed. Only if there's a bug in the code can execution ever get to that throw statement - and if there's a bug, a throw is not a bad thing. Arcane Jill |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | Arcane Jill wrote:
> In article <cgktoo$d5c$1@digitaldaemon.com>, Andy Friesen says...
>
>
>>If DMD were to do the same, its users would be forced to stick an "assert(false);" at the end
>
>
> Almost. But
>
> # assert(false)
>
> is only equivalent to
>
> # debug
> # {
> # throw newAssertException();
> # }
>
> so there is still a release-build control-path which falls straight through.
> This also needs to be explicitly blocked. Matthew's "noreturn" idea (or perhaps
> "unreachable", as has also been suggested) should be executed in both debug and
> non-debug builds, and so is preferable to "assert".
Maybe, as a special case, assert(false) should stick around even in release mode. If it should never execute, it's not exactly in a position to cause much runtime overhead. :)
Plus it saves us the hassle of setting a keyword aside for such a narrow purpose.
-- andy
|
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgkbn8$4pr$1@digitaldaemon.com... > You're completely missing the point. Your statement above is obviously true, and > no-one is disagreeing with you. All you're doing is stating the obvious. But > it's irrelevant, so I can only assume that you're just not /getting/ what we're > complaining about. > > THIS SHOULD NOT COMPILE: > > # int f(int n) > # { > # if (n == 1) return 1; > # } > > I want the error reported /at compile time/, not at run-time. I want the compiler to tell me "not all execution paths return a value" /at compile time/. But it isn't always an error to write such code. Always diagnosing it as an error means that you have to insert superfluous return statements in that will never get executed. I've already pointed out why this is undesirable. |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arcane Jill | Arcane Jill wrote: > In article <cgl7d2$fnm$1@digitaldaemon.com>, Dave says... > >>- The compiler would catch every instance where there could be a mssing return code path, or barring that, a large amount of effort is expended to make it as accurate as possible. > > It's not actually a large amount of effort. Basically, wherever you have an if(), you have to assume that there will be a path of execution for the true case, and a path for the false case. Similarly, whenever you have a Perhaps it isn't a huge effort, but I suspect that adding another check like this to the recursive decent logic could complicate the compiler to a significant degree, because now the compiler has to keep track of more information for potentially many other recursions into other logical branches in the same function. I guess I just don't know for sure except to say that other much more mature compilers still miss, so I suspect it isn't totally straight forward. > > True. Again, it's just syntactic sugar, and it isn't really important. "throw" works for me just as well. Primarily it's Walter's "false negatives are better than false positives" philosophy with which I and others disagree. > Personally, I've always felt that bad data is worse than no data... Ah hell, I can see both points here. > There is no way that an unreachable throw statement can hurt performance. By definition, if it's unreachable, it will never be executed. Only if there's a bug in the code can execution ever get to that throw statement - and if there's a bug, a throw is not a bad thing. I agree - I don't think it will either, but I threw in the big IF to get the point across that it needs to be implemented efficiently ;) I held out the possibility that it could potentially cause problems. For example, if an implementation might coax the compiler into reserving scarce resources (like registers) for the call to the Exception class Ctor, and then there are the 'code distance' issues that extra code may exacerbate, etc.. I think everything except the extra bloat could be handled w/o causing adverse effects though. > > Arcane Jill |
August 26, 2004 Re: AssertError on missing return? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <newshound@digitalmars.com> wrote in message news:cglfk6$mqk$1@digitaldaemon.com... > > "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgkbn8$4pr$1@digitaldaemon.com... > > You're completely missing the point. Your statement above is obviously > true, and > > no-one is disagreeing with you. All you're doing is stating the obvious. > But > > it's irrelevant, so I can only assume that you're just not /getting/ what > we're > > complaining about. > > > > THIS SHOULD NOT COMPILE: > > > > # int f(int n) > > # { > > # if (n == 1) return 1; > > # } > > > > I want the error reported /at compile time/, not at run-time. I want the compiler to tell me "not all execution paths return a value" /at compile > time/. > > But it isn't always an error to write such code. Always diagnosing it as an > error means that you have to insert superfluous return statements in that will never get executed. I've already pointed out why this is undesirable. > But most of the time it is. Then add noreturn statement that will fit nicelly in the design by contarct story. In a code like this: int f(int n) { if (n == 1) return 1; noreturn; } That noreturn statement would be a contract with wich i am saying: this part of code can't/(must not) be reached. It would also be useful to see this in someone code, it would clearly document the coders intention. For example if i coded int f(int n) { if(n>0)return 1; if(n<0)return -1; noreturn; //=== throw new Exception("Unreachable code reached in f, line ..."); } And looking at this code someone would imidiatelly know my intention that n is either > 0 or <0, and that may even help someone spot a bug for example that n can be 0. With noreturn there are no superfluous returns (wich are ofcourse as undesirable as missing returns) :) |
Copyright © 1999-2021 by the D Language Foundation