Topic: Proposal: Change the specified behavior when control
Author: chris beck <render787@gmail.com>
Date: Tue, 2 Feb 2016 16:15:03 -0800 (PST)
Raw View
------=_Part_7320_1551576515.1454458503436
Content-Type: multipart/alternative;
boundary="----=_Part_7321_2103638992.1454458503436"
------=_Part_7321_2103638992.1454458503436
Content-Type: text/plain; charset=UTF-8
In the C++14 standard, the behavior when control flow reaches the end of a
non-void function is described as follows:
[stmt.return][6.6.3 The `return` statement]
... Flowing off the end of a function is equivalent to a return with no
value; this results in undefined
behavior in a value-returning function.
I propose that the standard should be amended as follows:
... Flowing off the end of a function is equivalent to a return with no
value; if this occurs in a function whose
return type is not `void`, `std::terminate` is called.
The reason for this is to make it easier to catch this undefined behavior
by requiring the program to fail fast.
In practice, it *seems* that gcc and clang generally do something like
return an indeterminate value when they fall off
the end of a non-void function, and tracking down such bugs can be very
difficult and time-consuming.
In C++ code written in a modern style, this seems to be one of the easiest
ways that an uninitialized value can occur in
real code.
Most programmers deal with the situation by diligently paying attention to
compiler warnings -- gcc will issue a warning
when it can see that control flow can reach the end of a non-void function.
However, this is not an excellent solution.
It only works when gcc can actually prove that the end of the function is
reachable, and this depends on the
optimization level used, and on the optimizations that succeed. If the
function is sufficiently complicated that gcc
can't be sure the end is reachable, gcc won't issue a warning even if the
end can be reached, to avoid giving a
"false-positive" warning, which would limit the utility of the warning.
Determining for sure if the end is reachable is
equivalent to the halting problem, so this approach is inherently limited.
By simply requiring the compiler to emit a call to `std::terminate`
(instead of potentially returning an undefined
value when this happens), we can catch the problem at run-time 100% of the
time without having to solve the halting
problem. Moreover, in many real cases the compiler can prove that the "end"
is not reachable and that the function does
properly return, and then it may eliminate the `std::terminate` call.
The main arguments that I anticipate against this proposal are
1) Potentially emitting extra calls to `std::terminate` may bloat code,
which without this change wouldn't need it, and
would work fine.
My belief is that the overhead would be low -- I would anticipate that
"small" functions can usually be proved to
return correctly, and then the `std::terminate` call is optimized out. For
large functions, an extra call at the end
represents only a marginal increase. However I don't have any data.
2) The C standard specifies this as undefined behavior -- C++ should do so
also for compatibility. Potentially, there
may be programs which technically have undefined behavior by way of
returning an indeterminate value in this way, but in
fact the indeterminate value is then ignored in the caller and so it
doesn't cause problems and runs fine. Such programs
would now be terminated instead, breaking previously "working" code.
My hope would be that more programs will be fixed by catching the
indeterminate values before they cause havoc, than
would be broken in this manner -- likely, even the programs which break due
to this change would benefit from being
refactored to avoid this problem. Anyways, it seems that the standard
committee recently has been willing to make
breaking changes that improve maintainability in the long term, for
instance, with the changes regarding throwing
exceptions from destructors which became standard in C++11.
So perhaps the standards committee has an appetite for this also.
3.) The impact of this change may vary widely from compiler to compiler.
Some compilers with very mature dead-code
elimination may be able to tolerate a change like this with few spurious
`std::terminate` calls resulting in their code,
but others whose optimization tech is engineered differently may struggle
to eliminate those calls -- requiring much
work from developers of those compilers to avoid this overhead.
It's difficult for one individual to survey the entire body of C++
compilers and the optimization techniques they use,
I am only actually familiar with a few compilers. My impression is that
dead-code elimination is one of the most
important optimizations and that all modern compilers do this very
aggressively, but if you know a popular compiler that
you think might struggle with eliminating function calls injected at the
end of functions like I proposed in a large
program, I would be interested to investigate and perhaps run some tests.
I would very much appreciate any thoughts or feedback regarding this idea.
If there have been previous proposals of
this which have been rejected, I would appreciate a reference, I was not
able to find such.
Best Regards,
Chris Beck
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-proposals/.
------=_Part_7321_2103638992.1454458503436
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">In the C++14 standard, the behavior when control flow reac=
hes the end of a non-void function is described as follows:<br><br>[stmt.re=
turn][6.6.3 The `return` statement]<br>=C2=A0=C2=A0=C2=A0 ... Flowing off t=
he end of a function is equivalent to a return with no value; this results =
in undefined<br>behavior in a value-returning function.<br><br>I propose th=
at the standard should be amended as follows:<br><br>=C2=A0=C2=A0=C2=A0 ...=
Flowing off the end of a function is equivalent to a return with no value;=
if this occurs in a function whose<br>return type is not `void`, `std::ter=
minate` is called.<br><br>The reason for this is to make it easier to catch=
this undefined behavior by requiring the program to fail fast.<br>In pract=
ice, it *seems* that gcc and clang generally do something like return an in=
determinate value when they fall off<br>the end of a non-void function, and=
tracking down such bugs can be very difficult and time-consuming.<br>In C+=
+ code written in a modern style, this seems to be one of the easiest ways =
that an uninitialized value can occur in<br>real code.<br><br>Most programm=
ers deal with the situation by diligently paying attention to compiler warn=
ings -- gcc will issue a warning<br>when it can see that control flow can r=
each the end of a non-void function. However, this is not an excellent solu=
tion.<br>It only works when gcc can actually prove that the end of the func=
tion is reachable, and this depends on the<br>optimization level used, and =
on the optimizations that succeed. If the function is sufficiently complica=
ted that gcc<br>can't be sure the end is reachable, gcc won't issue=
a warning even if the end can be reached, to avoid giving a<br>"false=
-positive" warning, which would limit the utility of the warning. Dete=
rmining for sure if the end is reachable is<br>equivalent to the halting pr=
oblem, so this approach is inherently limited.<br><br>By simply requiring t=
he compiler to emit a call to `std::terminate` (instead of potentially retu=
rning an undefined<br>value when this happens), we can catch the problem at=
run-time 100% of the time without having to solve the halting<br>problem. =
Moreover, in many real cases the compiler can prove that the "end"=
; is not reachable and that the function does<br>properly return, and then =
it may eliminate the `std::terminate` call.<br><br>The main arguments that =
I anticipate against this proposal are<br>1) Potentially emitting extra cal=
ls to `std::terminate` may bloat code, which without this change wouldn'=
;t need it, and<br>would work fine.<br><br>=C2=A0 My belief is that the ove=
rhead would be low -- I would anticipate that "small" functions c=
an usually be proved to<br>return correctly, and then the `std::terminate` =
call is optimized out. For large functions, an extra call at the end<br>rep=
resents only a marginal increase. However I don't have any data.<br><br=
>2) The C standard specifies this as undefined behavior -- C++ should do so=
also for compatibility. Potentially, there<br>may be programs which techni=
cally have undefined behavior by way of returning an indeterminate value in=
this way, but in<br>fact the indeterminate value is then ignored in the ca=
ller and so it doesn't cause problems and runs fine. Such programs<br>w=
ould now be terminated instead, breaking previously "working" cod=
e.<br><br>=C2=A0 My hope would be that more programs will be fixed by catch=
ing the indeterminate values before they cause havoc, than<br>would be brok=
en in this manner -- likely, even the programs which break due to this chan=
ge would benefit from being<br>refactored to avoid this problem. Anyways, i=
t seems that the standard committee recently has been willing to make<br>br=
eaking changes that improve maintainability in the long term, for instance,=
with the changes regarding throwing<br>exceptions from destructors which b=
ecame standard in C++11.<br>So perhaps the standards committee has an appet=
ite for this also.<br><br>3.) The impact of this change may vary widely fro=
m compiler to compiler. Some compilers with very mature dead-code<br>elimin=
ation may be able to tolerate a change like this with few spurious `std::te=
rminate` calls resulting in their code,<br>but others whose optimization te=
ch is engineered differently may struggle to eliminate those calls -- requi=
ring much<br>work from developers of those compilers to avoid this overhead=
..<br><br>=C2=A0 It's difficult for one individual to survey the entire =
body of C++ compilers and the optimization techniques they use,<br>I am onl=
y actually familiar with a few compilers. My impression is that dead-code e=
limination is one of the most<br>important optimizations and that all moder=
n compilers do this very aggressively, but if you know a popular compiler t=
hat<br>you think might struggle with eliminating function calls injected at=
the end of functions like I proposed in a large<br>program, I would be int=
erested to investigate and perhaps run some tests.<br><br><br><br>I would v=
ery much appreciate any thoughts or feedback regarding this idea. If there =
have been previous proposals of<br>this which have been rejected, I would a=
ppreciate a reference, I was not able to find such.<br><br>Best Regards,<br=
>Chris Beck</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
Visit this group at <a href=3D"https://groups.google.com/a/isocpp.org/group=
/std-proposals/">https://groups.google.com/a/isocpp.org/group/std-proposals=
/</a>.<br />
------=_Part_7321_2103638992.1454458503436--
------=_Part_7320_1551576515.1454458503436--
.