Warning to newbies

P

Phil Carmody

Richard Heathfield said:
Ah, some sense. For once, I can agree with you. It's silly. I see no
point in enforcing a return statement in a function with void return
type, but in functions that are declared as returning a value, surely
it isn't asking too much of the programmer to require a value to be
returned (that is, for the lack of a return statement to be a
constraint violation).


I've encountered many an embedded situation which maps quite
closely onto this kind of thing:

int main(/*...*/)
{
while(1) { do_stuff(); }
}

Which I think is forgivable.

Phil
 
W

Walter Banks

An interesting problem is for functions that are coded in such a way
as to make reaching the end of the function impossible. That may
happen in the case of a loop which will be exited from via a return
inside the loop, or be calling some functions (abort(), for example),
that will not return.

Neither case is easy (or in fact necessarily possible at all, since
they break down to the halting problem in the general case) for a
compiler to detect. That leaves the somewhat unpleasant requirement
of putting a dummy return statement at the end of such a function to
suppress the diagnostic (and many compilers do, of course, warn about
that). Probably a better approach would be a constraint specification
("__should_never_get_here__") that could be inserted by the programmer
that the compiler can understand, and toss some kind of exception if
it does happen.

There is an approach that we use in our compilers to detect just this
kind of problem. We use the control flow graph and start at the beginning
of the function and every path should be terminated by an appropriate
return.

This will detect cases with multiple returns or where a return is
from the middle of a loop and not require a return at the physical
end of the function.

Regards,

w..
 
R

Richard Tobin

Phil Carmody said:
I've encountered many an embedded situation which maps quite
closely onto this kind of thing:

int main(/*...*/)
{
while(1) { do_stuff(); }
}

Which I think is forgivable.

One might argue that in that case it should be possible to declare
main as void. On the other hand, one should be able to write

int fun(void)
{
...
if(something_bad)
abort(); /* or exit(whatever); */
else
{
...
return something;
}
}

without adding a return statement after the abort(). And of course
one can construct cases where the impossibility of reaching a missing
return is undecidable. I have a vague recollection that the Java spec
requires the compiler to recognise unreachable code in and only in
certain specified cases.

-- Richard
 
S

Seebs

Sorry, Seebs, but that's not an interesting special case. It's a dumb
special case. :)

Heh.

I think that one may have been just about justified, just because it's been
such a pernicious and common boundary condition, and because we *do* know
what the caller makes of the return value to main. It makes less sense for
cases where the implications of "return 0" are not defined by the spec.

-s
 
K

Keith Thompson

One might argue that in that case it should be possible to declare
main as void. On the other hand, one should be able to write

int fun(void)
{
...
if(something_bad)
abort(); /* or exit(whatever); */
else
{
...
return something;
}
}

without adding a return statement after the abort(). And of course
one can construct cases where the impossibility of reaching a missing
return is undecidable. I have a vague recollection that the Java spec
requires the compiler to recognise unreachable code in and only in
certain specified cases.

Ah, that's a good point. If we're going to try to forbid reaching the
closing "}" of a non-void function, we'll have to recognize things
other than return statements: abort() calls, exit() calls, probably
raise() calls, probably something involving setjmp and longjmp that
I'm not going to think about, and any implementation-defined or
programmer-defined constructs that terminate execution.

int fun(void) {
if (something) {
some_function();
}
else {
return 0;
}
}

/* in a different translation unit */

void some_function(void) {
abort();
}

Solving the problem in general, and avoiding all spurious diagnostics,
is probably impractical.

So the question is, what kind of partial solution can we come up with?
(Leaving everything as it is is probably a viable partial solution.)
 
S

Seebs

One might argue that in that case it should be possible to declare
main as void. On the other hand, one should be able to write

int fun(void)
{
...
if(something_bad)
abort(); /* or exit(whatever); */
else
{
...
return something;
}
}
without adding a return statement after the abort().
Hmm.

And of course
one can construct cases where the impossibility of reaching a missing
return is undecidable.

int fun(void)
{
assert(0);
}

Hmm.

You could just declare that the existence of a code path which does not
execute a return statement with a value is a constraint violation... Of
course, compilers couldn't always tell, but if they couldn't tell, they could
emit a diagnostic and compile anyway.

But I think that would be worse. Probably.

-s
 
W

Walter Banks

Keith said:
One possibility would be to require, in any non-void function, a
return statement along all possible execution paths, so that it can be
proven at compile time that execution will never reach the closing
"}". For this, no run-time checking would be required.

It would require defining "all possible execution paths", preferably
in a fairly straightforward way so that compilers aren't required to
be *too* clever.

For example this:

int foo(void) {
if (something) {
return 0;
}
}

would be a constraint violation, whereas this:

void foo(void) {
if (something) {
while (1) {
twiddle(thumbs);
}
}
else {
return 0;
}
}

would not. This would require more control flow analysis than C
compilers have traditionally been required to do (but no more than
they typically do in practice).

Another possibility would be something like what Ada does. For the
equivalent of a non-void function, Ada requires a return statement
*somewhere* in the body of the function, even if it's never executed;
this is a compile-time requirement. It raises a run-time exception
if execution reaches the end of the function. C could require the
equivalent of a call to abort(). A compiler could omit the abort()
call from the generated code if it can prove that it will never be
reached. A big problem with this is that abort(), or even exit(),
isn't required for freestanding implementations.

We use control flow data to trace the execution path in a function and
make sure that returns on any leaf returns and value or not as the
function is declared. Failure to find an appropriate return results in a
fatal error. Very similar to your suggestion

w..
 
W

Walter Banks

Keith said:
Ah, that's a good point. If we're going to try to forbid reaching the
closing "}" of a non-void function, we'll have to recognize things
other than return statements: abort() calls, exit() calls, probably
raise() calls, probably something involving setjmp and longjmp that

Walking a control flow map of the application resolves most of
these. Detecting a missing return can be done in the compiler,
much easier than determining if a loop will exit for example.

w..
 
W

Walter Banks

Seebs said:
int fun(void)
{
assert(0);
}

This can be handled similar to tail end recursion so the return from the
assert is actually the fun return. (saving call return space at the same time)


w..
 
P

Peter Nilsson

Richard Heathfield said:
spinoza1111 wrote:
... I invite people to read a selection of, say, 10-20
articles you've posted to this group, chosen entirely
at random; to read the same number of articles I've
posted to this group, also chosen at random; and then
to make their own minds up.

If they haven't, and they happen to randomly select 40
articles from threads like this, then they may well
conclude that you're both jackasses who contribute
nothing useful to the group!
I have no doubts whatsoever about the outcome.

If reputation concerns you so much, then you should
realise the harm you do to that reputation getting
sucked in to these pointless discussions.
I can even take a fair guess at which four or five
subscribers to this group would manage to get the
answer wrong.

Given that your posts to spinoza at times make up
near to half your posts to clc, perhaps you should
be concerned at the number who might 'manage to get
the answer wrong'. I'd say it would be considerably
more than four or five.

If being seen to win over spinoza is a level of
sufficient quality for you, that's your choice. But
don't expect too many others to be supportive, let
alone grateful, that you've lowered the bar for
yourself that far.
 
K

Keith Thompson

Walter Banks said:
Walking a control flow map of the application resolves most of
these. Detecting a missing return can be done in the compiler,
much easier than determining if a loop will exit for example.

I don't see how that would help, unless you're talking about mapping
the *entire* application. If we're talking about a new requirement
that would apply to all conforming compilers (and that's what I'm
talking about anyway), I'd say all the analysis needs to be doable on
a single function at a time. Letting the compiler know that abort()
and exit() don't return might be reasonable, but then there's
an asymmetry between language-defined and implementation-defined
functions.

Of course compilers and linkers are always free to do whatever
additional analysis they like, but

If we're going to require a diagnostic for this:

int foo(void) {
if (something) {
return 0;
}
fputs("Oops\n", stderr);
/* error: control reaches end of non-void function */
}

then I don't think we can avoid requiring a diagnostic for this:

int foo(void) {
if (something) {
return 0;
}
system_specific_abort_program();
/* error: control reaches end of non-void function */
}

If we did go that way, then programmers would have to add a dummy
"return 0;" that they know will never be reached.

I'm beginning to think that this might not be such a good idea
after all. Existing compilers and other tools can already issue
warnings when control might reach the end of a non-void function,
and can take advantage of system-specific knowledge while doing so.
It would be nice if *all* compilers did so, but I'm not sure the
benefit outweighs both the cost it imposes on all implementations
and the difficulty of hammering out the exact requirements.
 
S

spinoza1111

spinoza1111wrote:


Clearly you and I are not ever going to agree about this. I invite
people to read a selection of, say, 10-20 articles you've posted to this
group, chosen entirely at random; to read the same number of articles
I've posted to this group, also chosen at random; and then to make their
own minds up. I have no doubts whatsoever about the outcome. I can even
take a fair guess at which four or five subscribers to this group would
manage to get the answer wrong.

"Reading things at random" is the problem. It misses cause and effect,
and, as I've said, your actions have to be seen in order.

Whenever someone more qualified than you enters, you find some trivial
objection and then start in on that person, encouraging others to
respond including sadists who unlike you are here purely to abuse
others (like Colonel Harlan Sanders and Rosenau). Your hope is that
the greater "simplicity" (dismissal of complexity) in your posts will
appeal to the man on the street in the same way the Nazis simplified
their message.

However, in a court of law, it will be established that you're here,
perhaps paid to be here, as a disruptive influence who keeps people
from discussing C and related topics.
 
C

Colonel Harlan Sanders

Yes, a cross-dressed loner in a basement,
In bra, panties, and a Hitler Hat,
Joining an ONLINE mob. Get it, slob?

Yes, I get it. I get that you actually believe this.
You assert ridiculous imaginings like this in the same way as you
state "facts" to support any other statement that you hold to be true.

While my underwear is to you and other readers clearly unknowable, you
are sure of exactly what I'm wearing.

Moving on to the statement about "mobs"; that is both knowable and
false, as it supposedly describes actions in this group that are on
the record. By "mob" you seem to mean that more than one person holds
a view contrary to your own; thus they are part of a conspiracy/the
regs/a mob/Hitler Youth/ as the mood takes you. This definition of
"mob" is unique.

I notice that you have written several other posts vilifying me, pages
and pages -- in verse, in response to my earlier post pointing out
how unlikely it was that Schildt was one of the "lurkers who support
me in email" as the old Usenet trope goes.

That pretty much confirms that you can't argue the facts and just
blowing smoke to try to divert attention from that.

How convenient it is for you that Schildt only speaks to you. He can't
contradict you, as for instance Navia did when he rebelled against
your championing him and using his defense as a pretext for attacking
Heathfield last year.

A recent example, we can observe the birth of a new conspiracy theory:

You see, IF fly by wire was the
cause of the Air France crash of 2009, or if drive by wire caused the
accelerator pedal jam on the Toyota, this could well mean that the
avionics or car software was written in C, and one of C's numerous
issues caused the problems.

After several earlier references to these several events, you have
brought them all together into one pattern, simply asserting without
the slightest shred of evidence that the problems all were all due to
them being coded in C (a "fact" you seem to have deduced by ESP).

Every time you return to this you state it less as a supposition and
more as a fact. After a while you will have convinced yourself it's
true and added it to your collection of entirely self-generated facts
that you litter your arguments with to the bewilderment of any reader.

And you actually think that your discovery of such conspiracies, in
small and large scale, wherever you look, is a sign of your
perspicacity, not insanity.

Since I'm not getting paid by the hour to analyse you, I should move
on to more productive uses of my time.

Give my regards to Schildt, Nash and all your other famous, but
silent, patrons.
 
S

spinoza1111

The problem was one of computer science, but Kiki, filled with
resentment if not hate, kept whining that it was off topic because C
Sharp could handle the problem with ease.
Yes, C# can indeed handle the problem. However, it's not exactly all
that
"efficient" because it needs to do a full blown copy-on-write and use
garbage collection to ensure that everything works as expected. In other
words, I cannot operate on data-structures directly like I can using
assembly language. FWIW, this is exactly how Sun implemented
`AtomicStampedReference'.[...]
Can't you just let Moore's Law handle "efficiency"?

That's a fair point Edward, however I am highly interested in exploring
techniques that allow software to have excellent scalability, throughput and
performance characteristics on multi-processor systems. If the algorithm
does not scale well, then it's basically dead in the water wrt upcoming
many-core systems.

IMHO, it's not really enough anymore to simply think in terms of `faster
clock rate's = faster software'. Instead, you need to think about how your
software can possibly scale up and adapt to a new system that might have 5
to 10 times the number of processors that you are normally accustomed to
working with. For instance, imagine a software product that runs great on a
4-core system. Then one it's users purchases a 64-core system and installs
said software and does not see any speedup whatsoever. Well, the call to
customer service might go something like:

"I just purchased one of those new multi-core systems, and your application
suite seems to execute critical tasks a little bit slower than usual! Why is
that? Do I have to set some special configuration options? Please help..."

That's right, a poorly written multi-threaded application could actually
perform worse in a multi-core environment. I believe a company would have an
advertising and promotion advantage if it were able to say:

"Our company has recently accomplished the steps that are necessary in order
to create high-end software that is able to efficiently address the
concurrency revolution that is currently underway in the programming
community."

IMHO, software developers should try and learn how to properly address the
"Rise of the Multi-Core Machines!" in order to create quality highly
scaleable products that the consumers deserve. FWIW, here is an article by
Herb Sutter that just might be of interest to you:

http://www.gotw.ca/publications/concurrency-ddj.htm
Sure, not everything can be interpreted since this would multiply all
times by a constant or even a variable number of times because when
things are interpreted, something happens for each instruction. But as
I have shown, C sharp is compiled at the last minute and not
interpreted.
In my view, garbage collection is such a great thing that we shouldn't
be sad that it happens.

I think that garbage collection is an extraordinary convenient tool indeed
and, IMVHO, it's definitely worthwhile to try and reduce the
"burden/pressure" on it in order to help make it's job "easier". So, I think
that some forms of manual memory management can indeed be beneficial in a
fully garbage collected environment. For instance, something as simple as an
object cache can potentially help out things out:

Imagine a simple container in which you push/pop references to objects. Now,
on every push operation you allocate an internal node to hold the reference
and insert it into the collection. On every pop you remove a node a return
the reference. No need to explicitly free any internal nodes because the GC
just handles all if it for you; very nice! However, this can begin to build
up nodes that "do not necessarily have to exist". Think of pushing and
popping 10,000 nodes. Now, those 10,000 nodes might still exist even though
the collection is completely empty. Then you push and pop 10,000 more. Now
there might be 20,000 nodes floating around waiting for the GC to kick in
and clean everything. This is due to the non-deterministic nature of a
general purpose GC.

Okay, so what would happen if you used a simple node cache? The push
operation would check the cache and only allocate new nodes if it was empty.
The pop operation just sticks popped nodes into said cache if the number of
nodes it currently contains is below a certain threshold, say 50,000 nodes.
In this scenario, when you push/pop 10,000 nodes and then push/pop 10,000
more you are guaranteed to only have 10,000 nodes in existence. Therefore, I
kind of think that the GC would be fairly happy that you actually took the
time to take some of the pressure off of it. Yes, this is definitely a form
of manual memory management which is more complicated and error prone, but
it does have it's perks...   ;^)

Is that making any sense Edward?
However, I have NEVER programmed OSen or embedded systems for real
money, although I've written a number of compilers. I understand that
absolute performance goals may apply. I do object to treating
performance speed as an unalloyed good when the customer doesn't ask
for it.

I think that one class of software than can benefit from heavy optimizations
could be database servers. If a company upgrades their server farms with
higher end systems that have many more cores inside of them and your
database software does not scale up, well, then they are probably going to
be extremely pissed of. I would not want to receive that phone call; yikes!

:^o

Interesting comments. Thank you. OK, it sounds like software will be
in ten years something that only highly qualified postdocs will write
anew, and this directly contradicts what I said elsewhere: that
software is evolving in the direction of airline piloting.

If software becomes brain surgery then serious barriers to entry need
to be enforced by law.

Highly critical intellects will be needed to say "the emperor has no
clothes" since so much concurrent software seems to work as in your
ABA example. Ben Bacarisse is the only example here and I sense his
intellect is atrophying on the job.

The idea of a formal proof of software correctness, or Dijkstra's
idea, that we should develop the proof and code simultaneously, may
come into its own, as may Dijsktra's memory; we badly need a solid
scientific biography of Dijkstra for the general reader, since so few
people outside the field know of his contributions.

Reliability in these future systems may be so important that it may be
a terminating offense to try to "help" any system function such as
garbage collection with your "own" features, since "own" features have
been in the past a rich source of bugs.

The reality has always been the cruel reality of the shaven Marching
Slaves in the 1984 Apple "Super Bowl" commercial. In the future, we
may all have to get PhDs to program while at the same time cultivating
critical intellects, while at the same time not using those critical
intellects to criticise management or politicians.

Indeed, I've lived and worked in this future. It was called Shenzen.
 
N

Nick

Walter Banks said:
Walking a control flow map of the application resolves most of
these. Detecting a missing return can be done in the compiler,
much easier than determining if a loop will exit for example.

Only if you've got the source of any pre-compiled libraries to hand.
GCC provides some of this as a bonus feature - it will tell you
"warning, control reaches end of a non-void function". But to avoid
spurious warnings you have to use a compiler extension:
__attribute__ ((noreturn))
on any code that terminates (so presumably all the internal definitions
of abort() etc, as well as your own functions that call that and
similar).
 
S

spinoza1111

Yes, I get it. I get that you actually believe this.

You got it wrong, pard, come on, it ain't hard,
It's called hyperbole, as in
"When y'all said 'oh promise me' I thought ya spoke hyperbole".
There ya go, cowboy, word power, don't get sour
You might learn somethin before your final hour.
(But I wouldn't bet on it).
You assert ridiculous imaginings like this in the same way as you
state "facts" to support any other statement that you hold to be true.

While my underwear is to you and other readers clearly unknowable, you
are sure of exactly what I'm wearing.

No, sot.
I'd really really rather
Not.
Moving on to the statement about "mobs"; that is both knowable and
false, as it supposedly describes actions in this group that are on
the record. By "mob" you seem to mean that more than one person holds
a view contrary to your own; thus they are part of a conspiracy/the
regs/a mob/Hitler Youth/ as the mood takes you. This definition of
"mob" is unique.

You don't hold views that are in any way meaningful:
You're not here to post code or to discuss C.
Instead you're here to place on view the shit of which you are full
By finding whom the regs target and then releasing pee.
This is to be the member of the raging mob:
This is the Way, the Truth, and the Life of the slob.

I notice that you have written several other posts vilifying me, pages
and pages --  in verse,  in response to my earlier post pointing out
how unlikely it was that Schildt was one of the "lurkers who support
me in email" as the old Usenet trope goes.

Yes, indeed I have whil'd away the weary hour
By responding in metre to your offensive posts and sour,
Which demonstrates clearly to men of good taste
That I am practically of a different species than you,
Who art a toad croaking upon toxic waste.
That pretty much confirms that you can't argue the facts and just
blowing smoke to try to divert attention from that.

Pray tell me what facts have you dealt in?
You live in a world of images and fantasy
Well locked in your sordid little bin
Seeing nothing speaking nothing because you can't see.
How convenient it is for you that Schildt only speaks to you. He can't
contradict you, as for instance Navia did when he rebelled against
your championing him and using his defense as a pretext for attacking
Heathfield last year.

The bully in his loneliness and fear
Tries psychological projection out his rear
The Good ignore his farts
And cultivate the useful arts.

A recent example, we can observe the birth of a new conspiracy theory:



After several earlier references to these several events, you have
brought them all together into one pattern, simply asserting without
the slightest shred of evidence that the problems all were all due to
them being coded in C (a "fact" you seem to have deduced by ESP).

Learn to read, your teacher would plead,
Find where the author uses "may":
He doesn't claim the avionics use C
He says instead he does not know.
It's as plain as your toe.
Every time you return to this you state it less as a supposition and
more as a fact. After a while you will have convinced yourself it's
true and added it to your collection of entirely self-generated facts
that you litter your arguments with to the bewilderment of any reader.

Looks to me like the facts are sourced, with a citation
To Langewiesche's short little book
Whereas your ravings are the rumination
Of a mad man in his pathetic little nook.

And you actually think that your discovery of such conspiracies, in
small and large scale,  wherever you look, is a sign of your
perspicacity, not insanity.

It has been my experience, from Hong Kong to Chicago,
That the uneducated sort of geezer and kid,
Think of settled interpretations and facts found in books, from long
ago,
As rumors and conspiracies to make them look stupid.
Since I'm not getting paid by the hour to analyse you, I should move
on to more productive uses of my time.

Such as applying for a job, appropriate for your mental age
Somewhere around the minimum wage.
 
S

spinoza1111

I've encountered many an embedded situation which maps quite
closely onto this kind of thing:

int main(/*...*/)
{
  while(1) { do_stuff(); }

}

Which I think is forgivable.

Not really. The only way to shut down such an appliance is brutally,
by turning the power off.

If the appliance has memory or state, then the only way it can restart
is cold boot.

But more and more, we need appliances to remember stuff.
 
M

Michael Foukarakis

One might argue that in that case it should be possible to declare
main as void.  On the other hand, one should be able to write

  int fun(void)
  {
    ...
    if(something_bad)
      abort();    /* or exit(whatever); */
    else
    {
      ...
      return something;
    }
  }

without adding a return statement after the abort().  And of course
one can construct cases where the impossibility of reaching a missing
return is undecidable.  I have a vague recollection that the Java spec
requires the compiler to recognise unreachable code in and only in
certain specified cases.

It's an embedded device - failure means it has to either be checked by
an appropriate specialist or replaced. Not taking into account what
has happened because it abort()'ed. Maybe it caused someone's death.
 
R

Richard Tobin

int main(/*...*/)
{
while(1) { do_stuff(); }

}

Which I think is forgivable.
[/QUOTE]
Not really. The only way to shut down such an appliance is brutally,
by turning the power off.

No, many programs have just this form, but shut down cleanly when
requested to. Look up the exit() function.

-- Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,251
Latest member
41Ki

Latest Threads

Top