August 26, 2004
"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
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
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
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
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
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
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
"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
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
"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) :)