August 25, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cghqva$211l$1@digitaldaemon.com...
> In article <cghkuj$1uni$1@digitaldaemon.com>, Walter says...
>
> >I don't like putting in meaningless return statements in code
> >that will never be executed and that will confuse the maintenance
> >programmer, just to get the compiler to stop issuing an error message.
>
> I'd rather have it that way round that the current way round. False
positives
> (unnecessary returns) are better than false negatives (failing to reach a
return
> at all) in this situation. Consider:
>
> #    int f(int n)
> #    in
> #    {
> #        assert(n > 3 && n < 10);
> #    }
> #    body
> #    {
> #        if (n >= 4 && n < 8) return 3;
> #        if (n >= 8 && n < 10) return 4;
> #        // The compiler will insist on an unnecessary return here.
> #    }
>
> So the compiler demands a superfluous return. Big deal. I can live with
that.
> Better a statement which won't get executed (and therefore won't affect
the
> running of my program in any way at all) than a run-time assert in a debug
build
> and a crash in a release build.

I disagree strongly. When I am maintaining/reviewing code, every statement must be justified. If I see a superfluous "return 0;", I'll look at all the callers to make sure they can handle a 0 value. If they can't, it isn't clear at all that they don't need to. I like to be able to look at code and know that "this cannot happen".

And if the superfluous return *does* get executed because of a different bug, now you've got a garbage return value that's seeped into your data, possibly corrupting things in unpredictable ways. I cannot see how this is superior. With the assert, you get an *unambiguous indication* of a bug in your program. I pick an unambiguous and repeatable indication of an error every time over a bad value sneaking unobserved into my data.

> >But I do want to find out if that meaningless return statement ever does happen. There's no way to do it 100% without a runtime check,
>
> Yes. No-one's arguing with that. I just don't think it that important. I'd rather have a compiler which claimed that good code is bad, than a
compiler
> which claimed that bad code is good. I've had crashes because of this
situation,
> and I'd rather not have any more.

Putting in a superfluous return isn't going to help you find those kind of bugs. It will just paper it over.


August 25, 2004
"J C Calvarese" <jcc7@cox.net> wrote in message news:cgi8mr$26f7$1@digitaldaemon.com...
> I was just hoping that we could have a more descriptive runtime error
than:
> Error: AssertError Failure bug.d(7)
>
> Maybe that can't be done. I just thought it was a possibility.
>
> (It only took me a few minutes to figure out what the problem was when I
ran
> into  it, but it could have taken a lot longer if I had made more changes
or had
> a larger program.)

I agree the message isn't the best, but at least it had the file and line number <g>.


August 25, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgi05f$234t$1@digitaldaemon.com...
> In article <cght30$21u3$1@digitaldaemon.com>, Matthew says...
>
> >unquantifiable and unpredictable throwing of exceptions at any time after
the
> >deployment of my code.

They are not any more unpredictable than any other bugs in your program - and at least you have a chance of finding this bug before release. The superfluous return has proveably *less* chance of being found before release.

> It's /much/ worse than that if you compile with the "-release" flag. These
are
> asserts, not exceptions. They vanish in a release build, leaving you with /seriously/ undefined behavior.

Yet executing a superfluous return that returns a value that was never intended to be returned is an easier to find bug? I don't see how. I'd *much* rather have the assert.

The principle here is that, as much as practical, program bugs should result in the program stopping immediately upon detection and issuing some appropriate error message. Trying to soldier on with some arbitrary return value will just cause your program to fail somewhere else in a far more obscure manner.

This discussion reminds me of an old joke about assembler programming:

    ... various assembler instructions ...
    HALT        ; stop program
    HALT        ; if skidding
    HALT        ; needed if program was running really, really fast



August 25, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgi05f$234t$1@digitaldaemon.com...
> In article <cght30$21u3$1@digitaldaemon.com>, Matthew says...
>
> >unquantifiable and unpredictable throwing of exceptions at any time after the deployment of my code.
>
> It's /much/ worse than that if you compile with the "-release" flag. These are asserts, not exceptions. They vanish in a release build, leaving you with /seriously/ undefined behavior.

Bah! This just sucks then. Count me a seconder to your objections.


August 25, 2004
On Wed, 25 Aug 2004 21:27:32 +1000, Matthew <admin.hat@stlsoft.dot.org> wrote:
> "Walter" <newshound@digitalmars.com> wrote in message news:cghkuj$1uni$1@digitaldaemon.com...
>>
>> "Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message
>> news:cgh9ms$1ohc$1@digitaldaemon.com...
>> > If D's simpler than C++ to implement, and C++ compilers can do this 
>> pretty
>> much in their sleep, then why should D not?
>>
>> I've never seen a C++ compiler get it completely right, and there's no way
>> that they can. I don't like putting in meaningless return statements in code
>> that will never be executed and that will confuse the maintenance
>> programmer, just to get the compiler to stop issuing an error message.
>>
>> But I do want to find out if that meaningless return statement ever does
>> happen. There's no way to do it 100% without a runtime check, hence that's
>> what the language does.
>
> Well it's the "completely" that's the issue. I don't need 100%. In the code I write, I've maybe come across the C++
> compiler not spot a missing return only a handful of types in 12 years.
>
> Conversely, I've had false positives perhaps 5 or six times a year. Frankly, I'd rather have that than missing a return.
> I'd far rather have the slight inconvenience of having to workaround a false positive 5 times a year than the
> unquantifiable and unpredictable throwing of exceptions at any time after the deployment of my code.

> Frankly, this is
> another of those situations where your penchant for compiler simplicity and the influence of your own coding style are
> given precedence over measures of robustness and scalability to, IMO, the long-term detriment of the language.

This is your opinion Matthew, you're entitled to it. I think you're being unfair, Walter has described his position as being _in_ favour of robustness and scalability, you simply disagree that his approach is robust and/or scalable, which is also an opinion you're entitled to.

My opinion is that a middle ground could be found that would hopefully please everyone.

I think if possible the compiler should catch what it can be 100% certain of at the compile stage, this step can and will improve as Walter finds examples he can catch, code that passes that step should throw an assertion at runtime in a 'debug' build as it currently does. The current behaviour of a 'release' build is ok, I believe.

Regan

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
August 25, 2004
"Walter" <newshound@digitalmars.com> wrote in message news:cgirt4$2fvp$1@digitaldaemon.com...
> The principle here is that, as much as practical, program bugs should
result
> in the program stopping immediately upon detection and issuing some appropriate error message. Trying to soldier on with some arbitrary return value will just cause your program to fail somewhere else in a far more obscure manner.

Let me expand on that a bit, because I feel strongly about this. The idea in developing robust code is to flush out bugs by making the program with the bug in it fail quickly and obviously. Inadvertantly executing statements that were never intended to be executed will likely hide bugs, making them hard to detect and harder to track down.

In addition, having an assert for the missing return means that, if you run the test suite and no assert happens, you KNOW that the missing return is not executed. But if you've installed a superfluous return there, then there is NO WAY to know that the return was not executed.

If you're concerned that your test suite is inadequate and your release code is therefore still buggy, and removing all the runtime checks like array bounds checking and missing return asserts will thereby cause erratic behavior, your best course of action is to leave the checks in in the shipping code. Trying to pretend the bugs aren't there by putting in superfluous code is not going to help make the released program more reliable and less buggy.

As a further point, some QA processes involve instrumenting code to verify that every code path was executed by the test suite. Being forced to put in superfluous, unreachable statements throws a monkey wrench into that methodology. (One of my first major projects used such a tool, it was also probably the least buggy code I've ever done as a result. Perhaps I should add this capability to D!)


August 26, 2004
On Mon, 23 Aug 2004 19:41:34 -0500, J C Calvarese wrote:

> I think I found a bug. I could be wrong. ;)
> 
> bit b;
> 
> int main()
> {
>      if (b)
>          return 0;
> }
> 
> Note the lack of a return statement when b is false (which is always).
> 
> It compiles fine. At runtime, an error occurs...
> Error: AssertError Failure bug.d(7)
> 
> I understand it can be difficult/undesirable for the compiler to catch a problem like this during compile. If the lack of a return is cause of the AssertError, I hope it could at least be renamed to NoReturnError or something.

Yes, a better message would be nice. Maybe "'return' expected but none was executed.". And I hope this wouldn't disappear in a "-release" build either.

But meanwhile ...

Should the principle for a compiler be that it tries to detect all potential errors, within practical limits, that could/would otherwise be detected at run time? I don't know the answer to this, so it is not rhetorical.

<anecodote>
Recently, in some code of mine, I came across an complex 'if/else if'
statement, in which I realized the final 'else' condition would never be
met. I'd left it for ages without an 'else' clause, but last week, just for
a sense of completeness, I coded something like ...

   else
       Abort("LOGIC ERROR #1: You should never see this but the Handle is
not owned by owner");

I released the code and almost immediately I had users saying that they were getting this weird error message. It turned out that there were at least three different conditions that fell through to the abort code. They were all bugs in code quite distant from this 'else', but until I added this superfluous 'else' code, I never knew about the bugs. (They caused a slow resource leak).

So I fixed the bugs but still I've left in the abort code, just in case I've messed up elsewhere.

</anecodote>

-- 
Derek
Melbourne, Australia
26/Aug/04 10:14:38 AM
August 26, 2004
In article <cgj749$2kt7$1@digitaldaemon.com>, Walter says...
>
>"Walter" <newshound@digitalmars.com> wrote in message news:cgirt4$2fvp$1@digitaldaemon.com...
>> The principle here is that, as much as practical, program bugs should
>result
>> in the program stopping immediately upon detection and issuing some appropriate error message. Trying to soldier on with some arbitrary return value will just cause your program to fail somewhere else in a far more obscure manner.
>
>Let me expand on that a bit, because I feel strongly about this. The idea in developing robust code is to flush out bugs by making the program with the bug in it fail quickly and obviously. Inadvertantly executing statements that were never intended to be executed will likely hide bugs, making them hard to detect and harder to track down.
>
>In addition, having an assert for the missing return means that, if you run the test suite and no assert happens, you KNOW that the missing return is not executed. But if you've installed a superfluous return there, then there is NO WAY to know that the return was not executed.

I think the difference of opinion here has to do with expectations.  Your way seems to expect programmers to know exactly what they're doing and to be able to test their applications fairly thoroughly for errors.  The other way seems to expect that the programmer may make dumb mistakes and would like the compiler to offer more support in finding those mistakes, even though this may occasionally lead to a false sense of security.

>If you're concerned that your test suite is inadequate and your release code is therefore still buggy, and removing all the runtime checks like array bounds checking and missing return asserts will thereby cause erratic behavior, your best course of action is to leave the checks in in the shipping code. Trying to pretend the bugs aren't there by putting in superfluous code is not going to help make the released program more reliable and less buggy.

Overall, I think your approach is more in-line with the D rationale (imagine that ;).  I think it also implies that D is not a beginner's language despite its straightforward syntax, or perhaps merely that proper use of D really requires programming and testing practices not common to beginners.  In some respects this makes me wish I were a CS teacher as I would be interested in seeing how folks turned out if they were taught D as a first or second language.

>As a further point, some QA processes involve instrumenting code to verify that every code path was executed by the test suite. Being forced to put in superfluous, unreachable statements throws a monkey wrench into that methodology. (One of my first major projects used such a tool, it was also probably the least buggy code I've ever done as a result. Perhaps I should add this capability to D!)

If you could do it then please do so.  D's built-in support for DBC and unit testing are substantial reasons why I initially became interested in the language.  If D had even more features in the testing realm it could only strengthen this selling point.


Sean


August 26, 2004
"Walter" <newshound@digitalmars.com> wrote in message news:cgirt4$2fvp$1@digitaldaemon.com...
>
> "Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:cgi05f$234t$1@digitaldaemon.com...
> > In article <cght30$21u3$1@digitaldaemon.com>, Matthew says...
> >
> > >unquantifiable and unpredictable throwing of exceptions at any time after
> the
> > >deployment of my code.
>
> They are not any more unpredictable than any other bugs in your program - and at least you have a chance of finding this bug before release. The superfluous return has proveably *less* chance of being found before release.
>
> > It's /much/ worse than that if you compile with the "-release" flag. These
> are
> > asserts, not exceptions. They vanish in a release build, leaving you with /seriously/ undefined behavior.
>
> Yet executing a superfluous return that returns a value that was never intended to be returned is an easier to find bug? I don't see how. I'd *much* rather have the assert.
>
> The principle here is that, as much as practical, program bugs should result in the program stopping immediately upon detection and issuing some appropriate error message. Trying to soldier on with some arbitrary return value will just cause your program to fail somewhere else in a far more obscure manner.
>
> This discussion reminds me of an old joke about assembler programming:
>
>     ... various assembler instructions ...
>     HALT        ; stop program
>     HALT        ; if skidding
>     HALT        ; needed if program was running really, really fast

I completely agree that having the program fail if its in an invalid state is preferable to soldiering on. Absolutely completely agree with that.

But I also prefer *in every conceivable circumstance* to have the compile refuse to compile than insert a runtime check. To me these things are completely different, and one should not be mixed with the other. Since C++ is able to, in almost all cases, find it at compile time, then I fail to see why D shouldn't. I accept that neither C++ nor D can find every one, in which case I prefer the assert to arbitrary behaviour. But "we can't find 100% at compile time" is no justification for "we won't look for *any* at compile time".




August 26, 2004
In article <opsda9v8065a2sq9@digitalmars.com>, Regan Heath says...
>
>This is your opinion Matthew, you're entitled to it. I think you're being unfair, Walter has described his position as being _in_ favour of robustness and scalability, you simply disagree that his approach is robust and/or scalable, which is also an opinion you're entitled to.
>
>My opinion is that a middle ground could be found that would hopefully please everyone.
>
>I think if possible the compiler should catch what it can be 100% certain of at the compile stage, this step can and will improve as Walter finds examples he can catch, code that passes that step should throw an assertion at runtime in a 'debug' build as it currently does. The current behaviour of a 'release' build is ok, I believe.
>

I agree that this runtime check is nice to have for debug builds regardless of how good a compiler implementation gets at catching missing returns.

This may be one of those checks that could be inserted for release builds too, because if the software takes that path /once/ it dies immediately anyhow (unlike an array bounds check in a loop as an obvious example). That way there would be something for a user to report back.

Could inline code with a simple message including the mangled method/function name be used for release builds?? If so then unless I'm missing some register or stack allocation issue or somesuch, it likely won't cost any performance _directly_ related to the function call, but will cost in terms of bloat regardless. Just a suggestion..

- Dave

>
>Regan
>
>-- 
>Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/