Jump to page: 1 2
Thread overview
Not returning a value when you're supposed to
Jul 20, 2004
Arcane Jill
Jul 20, 2004
Derek Parnell
Jul 20, 2004
Arcane Jill
Jul 20, 2004
Walter
Jul 20, 2004
Ivan Senji
Jul 20, 2004
Walter
Jul 20, 2004
Ivan Senji
Jul 20, 2004
Walter
OT humor (was: Not returning a value....)
Jul 21, 2004
Arcane Jill
Jul 24, 2004
Ivan Senji
Re: OT humor
Jul 24, 2004
J C Calvarese
Jul 20, 2004
Walter
Jul 20, 2004
Ivan Senji
Jul 20, 2004
Walter
Jul 20, 2004
Stewart Gordon
Jul 21, 2004
J Anderson
Jul 21, 2004
Stewart Gordon
Jul 24, 2004
J Anderson
Jul 24, 2004
Arcane Jill
July 20, 2004
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
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
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
"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
"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
"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
"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
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
"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
"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'.


« First   ‹ Prev
1 2