Topic: some thoughts on static exceptions (p0709r0)
Author: tortoise741@gmail.com
Date: Thu, 13 Dec 2018 09:43:39 -0800 (PST)
Raw View
------=_Part_528_1558586904.1544723019583
Content-Type: multipart/alternative;
boundary="----=_Part_529_2013518403.1544723019584"
------=_Part_529_2013518403.1544723019584
Content-Type: text/plain; charset="UTF-8"
I've finally had a chance to review the
[p0709r0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf)
(and the related work on possible implementations).
I have a few questions/suggestions.
After doing some background research and rubberducking this has come out as
rather a long post.
Hopefully some of it useful.
1. how do we handle propagating exceptions?
This was touched upon in the thread
[here](https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/D65DeZK23kg)
void X()
{
try
{
Y()
}
catch(error e)
{
}
}
int Y() /*not declared throws?*/
{
return Z();
}
int Z() throws
{
}
*Do users need to define two versions of Y() one declared throws() and one
not?*
*Does the compiler need to create two versions of Y() internally?*
If it is a compile error for Y() to use "Z() throws" without itself being
declared "throws" then
you have an extra overhead in replacing dynamic exceptions.
If X() previously caught dynamic exceptions from Z().
Y() previously did not have to be aware of the contract between X and Z. It
could
choose to be ignorant of whether or not Z() throws an exception.
To convert to static exceptions it now has itself to be declared throws.
I can see this being an issue where there is a long chain of functions
between the
thrower and catcher.
I was thinking that the correct thing is that the compiler generates one
verison of Y()
that is effectively declared throws() but there are two problems with this:
Callers of Y() previously not using exceptions have to pay the cost.
If Y() is a library function it cannot be recompiled to use the new calling
convention.
This may seem like a false concern but consider callbacks.
// maybe in a library...
struct snafu
{
callback* q;
snafu(callback* q_):q(q) {}
int Y() /*not declared throws?*/
{
return (*q)();
}
};
int Z() throws {
}
void X() {
try {
Y(&Z); // Am I undefined behaviour?
}
catch(error e)
{
}
}
Should this compile or be undefined behaviour?
Must the callback be declared not throwing?
With dynamic exceptions this is not necessary. I can use a callable
regardless of whether or not it throws.
This suggests that static exceptions as currently specified cannot
replace dynamic exceptions in all cases.
Of course it is fair to argue that using callbacks is by definition a
dynamic context.
*So is there still a place for dynamic exceptions in the future or should
they be entirely deprecated?*
2. What are the semantics for void functions?
E.g.
void Z() throws {
}
I'm presuming this is legal.
The return channel was not previously used but now will be.
Assuming clever compilers could elide it in this case, this may add some
(small) overhead.
The implementation of:
void Y() {
// do stuff 1
Z();
// do stuff 2
}
Has to silently become:
void Y() {
// do stuff 1
retVal = Z();
// save retVal and error flag if required
// do stuff 2
// put retVal into return register or similar
// set error flag
}
3. ABI overhead is lower but not zero
Return values can be either on the stack or in a register.
The intention is that std::error is (at least on many processors) small
enough to fit into a register.
So there should be no stack overhead for a void function but there
is a register overhead if an implementation was not previously using a
return register.
There is also an overhead for the additional flag (or register) for
indicating whether
the return value is a return or an error.
A tightly constrained embedded system is one place where static exceptions
are targeted
as a palatable replacement for dynamic exceptions.
The total number and width of registers available may be constrained.
On a tightly constrained embedded system an overhead of two registers might
be significant.
Presumably there are systems where a single register (with an 8-bit word)
is insufficient to represent the error
code by itself.
4. Can we do better?
Perhaps a single error 'bit' could flag the compiler to look in a well
defined location
on the stack for the actual error code.
It could be in the stack frame of the static exception handler and
referenced by an
offset from the stack pointer.
The register overhead is less.
However, though it seems like Y() doesn't need to know whether Z() throws
something needs to tell Z() where to put the error code.
So all this gives is the option to shift the register overhead from the
output side of Z() to the input.
That might be help with some extreme register shuffling perhaps.
How wide does a register need to be to represent the error code?
Could we optimise the use of a single register return so that for example
the
high order bit contains the error flag and if set rest of the register is
the error code.
On the other hand perhaps Z() can look in a known (set at link time)
location for the error location
just like the few other handlers we have.
This has to be per thread, perhaps even per coroutine context.
I've read "Draft 2 of D1095/N2259 C _Either(A, B) proposal paper"
and I see it gives examples or using the carry or zero flags.
I also read http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2289.pdf which
is even better.
This look like a good way forwards.
https://www.reddit.com/r/rust/comments/8wlphf/d1095r0n2xxx_draft_3_a_c_either_metatype_so_rust/
"Currently WG14 is feeling that my proposal is nowhere near radical enough.
They're actually currently talking about breaking binary compatibility by
making all C functions implicitly return a union type, which is very
radical for C."
@Niall: This is interesting. Could you expand on it?
I would have expected a lot of resistance.
The opinion of people like Linus Torvalds (i.e trying to maximise low-level
performance in C) on this would be interesting.
I also found:
https://groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/BnNRLtFXJMA/G3gIZ3FGBgAJ;context-place=forum/std-proposals
Some of the talk has been of "zero overhead" exceptions.
Surely setting a flag is not quite zero.
Propogating the flag and the error through functions which might want to
use the flag
or the return register themselves is even bigger.
5. Has anyone considered something like a "conditions system" for C++?
In Common LISP for example a function raises a "condition" rather than an
exception.
There is no stack unwinding.
Instead there are handlers which are active in the current context (think
set_new_handler() but not OOM)
A handler may accept or ignore a condition allowing the next handler in the
chain to try it
(like a catch block).
A handler can abort, ignore or restart the function from designated restart
points.
So Common LISP effectively supports resumable exceptions which were ruled
out of C++ as dubious/dangerous
a long time ago. Regardless of that the rest of the Scheme (pun intended)
may have merits.
To do this in C++ at present we would have to supply handlers as callbacks.
That is rather than:
Y()
{
try {
X()
}
catch(A) {
handler1()
}
catch(B) {
handler2()
}
}
We currently might write something like:
X'(handler& handler)
{
// stuff X did
if (A) handler.handle(A) //calls handler1
if (B) handler.handle(B) //calls handler2
}
Y()
{
X(Handler().setAHandler(handler1).setAHandler(handler2));
}
An interesting thing about this pattern is that stack unwinding (one of the
current costs) is (sort of) optional
Could language level support for something like this complement or compete
with static exceptions?
Language support might include translating "try" to this kind of
implementation so keep error information
out of band as we desire.
6. Contracts and OOM errors can be exceptional
Tl;Dr; panics should have handlers
We use design by contract and currently require assertions to throw an
AssertionFailure exception so that:
1. we can catch them in the right place and report the bug appropriately
2. don't have to terminate
For example we have a multi-thread fault tolerant scheduler that executes
tasks.
A bug in one task must not kill the scheduler.
The scheduler must continue to execute other tasks.
Tasks are not expected to fail. Therefore we treat failure as exceptional
rather than just a kind of error that has to be handled.
An assertion indicates a bug. The bug must be reported.
We log all exceptions the same way.
We log assertion failures as bugs. Internal errors which only a programmer
can fix.
A pseudo log file for the scheduler might look something like:
timestamp: task X: foobar: error: file not found <-- task failed due to
an error
timestamp: task X: task aborted
timestamp: task Y: error: threw something this it what it means <-- task
failed by throwing an exception
timestamp: task Y: task aborted
timestamp: task Z: error: internal error: assertion failure Y != X+1:
report bug!
timestamp: task Z: task aborted
timestamp: task BIG: task started
timestamp: task Little: task started
timestamp: task Little: error: internal error: not enough memory <--
allocation failed for this task (it could recover)
timestamp: task Little: task aborted
timestamp: task BIG: error: internal error: not enough memory <--
allocation failed for this task (it probably couldn't recover)
timestamp: task BIG: task aborted
timestamp: task Little: task retrying
timestamp: task Little: task finished
Aborting a task is exceptional.
Logging is a responsibility of the scheduler not the task.
Memory exhaustion may be recoverable.
The BIG task doesn't have sufficient memory to proceed but another task
might after we recover the memory it used.
Or maybe there are too many things running at once. Maybe we have to scale
down to performing tasks in serial.
The discussion suggests adding special kinds of "panic" for some things
which were previously exceptions.
I like that we have contract violation handlers in C++20 and that we can
throw provided the function violated
is not noexcept.
I like that we have the same for OOM via std::new_handler though I think I
would do this with a pool allocator myself.
Its possible we want that to be a choice even for corruption.
I'm not sure how many types of corruption can be detected reliably.
If we take stack overflow as an example. That should kill the thread but
perhaps not the application.
*So it should be "std::terminate_thread()" not std::terminate()?*
We can't require your programs never fail but we can permit attempts (as
long as they are not 100% futile)
at recovery and graceful failure.
Regards,
Bruce
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/8fbad2b5-d934-4471-a63c-b4c505ef8a8d%40isocpp.org.
------=_Part_529_2013518403.1544723019584
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">I've finally had a chance to review the [p0709r0](http=
://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf) <br>(and t=
he related work on possible implementations).<br><br><div>I have a few ques=
tions/suggestions.</div><div>After doing some background research and rubbe=
rducking this has come out as rather a long post.<br>Hopefully some of it u=
seful.</div><br><br>1. how do we handle propagating exceptions?<br>This was=
touched upon in the thread [here](https://groups.google.com/a/isocpp.org/f=
orum/#!topic/sg14/D65DeZK23kg)<br><br>=C2=A0=C2=A0=C2=A0 void X()<br>=C2=A0=
=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try <br>=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0 Y()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 catch(error e)<br>=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 }<br><br>=C2=A0=C2=A0=C2=A0 int Y() /*=
not declared throws?*/ <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 return Z();<br>=C2=A0=C2=A0=C2=A0 }<br><br>=C2=A0=C2=A0=C2=A0 =
int Z() throws <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0 }<br><br><br>=
*Do users need to define two versions of Y() one declared throws() and one =
not?*<br>*Does the compiler need to create two versions of Y() internally?*=
<br><br>If it is a compile error for Y() to use "Z() throws" with=
out itself being declared "throws" then<br>you have an extra over=
head in replacing dynamic exceptions.<br>If X() previously caught dynamic e=
xceptions from Z(). <br>Y() previously did not have to be aware of the cont=
ract between X and Z. It could<br>choose to be ignorant of whether or not Z=
() throws an exception.<br>To convert to static exceptions it now has itsel=
f to be declared throws.<br>I can see this being an issue where there is a =
long chain of functions between the<br>thrower and catcher.<br><br>I was th=
inking that the correct thing is that the compiler generates one verison of=
Y()<br>that is effectively declared throws() but there are two problems wi=
th this:<br><br>Callers of Y() previously not using exceptions have to pay =
the cost.<br>If Y() is a library function it cannot be recompiled to use th=
e new calling convention.<br><br>This may seem like a false concern but con=
sider callbacks.<br><br>=C2=A0=C2=A0=C2=A0 // maybe in a library...<br>=C2=
=A0=C2=A0=C2=A0 struct snafu<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 callback* q;<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0 snafu(callback* q_):q(q) {}<br>=C2=A0=C2=A0=C2=A0 <br=
>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 int Y() /*not declared throws?*/ <br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0 return (*q)();<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 }<br>=C2=A0=C2=A0=C2=A0 };<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0=
int Z() throws {<br>=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=
=C2=A0=C2=A0 void X() {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Y(&Z); // Am I u=
ndefined behaviour?<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0 catch(error e)<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=
}<br><br>Should this compile or be undefined behaviour?<br>Must the callba=
ck be declared not throwing?<br>With dynamic exceptions this is not necessa=
ry. I can use a callable regardless of whether or not it throws.<br><br>Thi=
s suggests that static exceptions as currently specified cannot <br>replace=
dynamic exceptions in all cases.<br><br>Of course it is fair to argue that=
using callbacks is by definition a dynamic context.<br>*So is there still =
a place for dynamic exceptions in the future or should they be entirely dep=
recated?*<br><br>2. What are the semantics for void functions?<br>E.g.<br><=
br>void Z() throws {<br>}<br><br>I'm presuming this is legal.<br>The re=
turn channel was not previously used but now will be.<br>Assuming clever co=
mpilers could elide it in this case, this may add some (small) overhead.<br=
><br>The implementation of:<br><br>void Y() {<br>=C2=A0 // do stuff 1<br>=
=C2=A0 Z();<br>=C2=A0 // do stuff 2<br>}<br><br>Has to silently become:<br>=
<br>void Y() {<br>=C2=A0 // do stuff 1<br><br>=C2=A0 retVal =3D Z();<br><br=
>=C2=A0 // save retVal and error flag if required<br><br>=C2=A0 // do stuff=
2<br><br>=C2=A0 // put retVal into return register or similar<br>=C2=A0 //=
set error flag<br>}<br><br><br>3. ABI overhead is lower but not zero<br><b=
r>Return values can be either on the stack or in a register.<br>The intenti=
on is that std::error is (at least on many processors) small enough to fit =
into a register.<br>So there should be no stack overhead for a void functio=
n but there<br>is a register overhead if an implementation was not previous=
ly using a return register.<br>There is also an overhead for the additional=
flag (or register) for indicating whether<br>the return value is a return =
or an error.<br><br>A tightly constrained embedded system is one place wher=
e static exceptions are targeted<br>as a palatable replacement for dynamic =
exceptions.<br>The total number and width of registers available may be con=
strained.<br>On a tightly constrained embedded system an overhead of two re=
gisters might be significant.<br><br>Presumably there are systems where a s=
ingle register (with an 8-bit word) is insufficient to represent the error<=
br>code by itself.<br><br>4. Can we do better?<br><br>Perhaps a single erro=
r 'bit' could flag the compiler to look in a well defined location<=
br>on the stack for the actual error code. <br>It could be in the stack fra=
me of the static exception handler and referenced by an<br>offset from the =
stack pointer.<br><br>The register overhead is less.<br>However, though it =
seems like Y() doesn't need to know whether Z() throws <br>something ne=
eds to tell Z() where to put the error code.<br>So all this gives is the op=
tion to shift the register overhead from the output side of Z() to the inpu=
t.<br>That might be help with some extreme register shuffling perhaps.<br><=
br>How wide does a register need to be to represent the error code?<br><br>=
Could we optimise the use of a single register return so that for example t=
he<br>high order bit contains the error flag and if set rest of the registe=
r is the error code.<br><br>On the other hand perhaps Z() can look in a kno=
wn (set at link time) location for the error location<br>just like the few =
other handlers we have.<br>This has to be per thread, perhaps even per coro=
utine context.<br><br>I've read "Draft 2 of D1095/N2259 C _Either(=
A, B) proposal paper"<br>and I see it gives examples or using the carr=
y or zero flags. <br><br>I also read http://www.open-std.org/jtc1/sc22/wg14=
/www/docs/n2289.pdf which is even better.<br>This look like a good way forw=
ards.<br><br>https://www.reddit.com/r/rust/comments/8wlphf/d1095r0n2xxx_dra=
ft_3_a_c_either_metatype_so_rust/<br>"Currently WG14 is feeling that m=
y proposal is nowhere near radical enough. They're actually currently t=
alking about breaking binary compatibility by making all C functions implic=
itly return a union type, which is very radical for C."<br><br>@Niall:=
This is interesting. Could you expand on it?<br>I would have expected a lo=
t of resistance. <br>The opinion of people like Linus Torvalds (i.e trying =
to maximise low-level performance in C) on this would be interesting.<br><b=
r>I also found:<br><br>https://groups.google.com/a/isocpp.org/forum/#!msg/s=
td-proposals/BnNRLtFXJMA/G3gIZ3FGBgAJ;context-place=3Dforum/std-proposals<b=
r><br>Some of the talk has been of "zero overhead" exceptions.<br=
>Surely setting a flag is not quite zero.<br>Propogating the flag and the e=
rror through functions which might want to use the flag<br>or the return re=
gister themselves is even bigger.<br><br>5. Has anyone considered something=
like a "conditions system" for C++?<br><br>In Common LISP for ex=
ample a function raises a "condition" rather than an exception.<b=
r>There is no stack unwinding.<br>Instead there are handlers which are acti=
ve in the current context (think set_new_handler() but not OOM)<br>A handle=
r may accept or ignore a condition allowing the next handler in the chain t=
o try it<br>(like a catch block).<br>A handler can abort, ignore or restart=
the function from designated restart points.<br><br>So Common LISP effecti=
vely supports resumable exceptions which were ruled out of C++ as dubious/d=
angerous<br>a long time ago. Regardless of that the rest of the Scheme (pun=
intended) may have merits.<br><br>To do this in C++ at present we would ha=
ve to supply handlers as callbacks.<br>That is rather than:<br><br>=C2=A0=
=C2=A0=C2=A0 Y() <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 try {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 X()<=
br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 catch(A) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 handl=
er1()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 catch(B) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
handler2()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=
}<br><br>We currently might write something like:<br><br>=C2=A0=C2=A0=C2=
=A0 X'(handler& handler) <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 // stuff X did<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (A) =
handler.handle(A) //calls handler1<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (B)=
handler.handle(B) //calls handler2<br>=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=
=C2=A0 <br>=C2=A0=C2=A0=C2=A0 Y()<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 X(Handler().setAHandler(handler1).setAHandler(hand=
ler2));<br>=C2=A0=C2=A0=C2=A0 }<br><br>An interesting thing about this patt=
ern is that stack unwinding (one of the current costs) is (sort of) optiona=
l<br>Could language level support for something like this complement or com=
pete with static exceptions?<br><br>Language support might include translat=
ing "try" to this kind of implementation so keep error informatio=
n<br>out of band as we desire.<br><br>6. Contracts and OOM errors can be ex=
ceptional<br>Tl;Dr; panics should have handlers<br><br>We use design by con=
tract and currently require assertions to throw an AssertionFailure excepti=
on so that:<br>=C2=A01. we can catch them in the right place and report the=
bug appropriately<br>=C2=A02. don't have to terminate<br><br>For examp=
le we have a multi-thread fault tolerant scheduler that executes tasks.<br>=
A bug in one task must not kill the scheduler.<br>The scheduler must contin=
ue to execute other tasks.<br><br>Tasks are not expected to fail. Therefore=
we treat failure as exceptional rather than just a kind of error that has =
to be handled.<br><br>An assertion indicates a bug. The bug must be reporte=
d.<br>We log all exceptions the same way.<br>We log assertion failures as b=
ugs. Internal errors which only a programmer can fix.<br><br>A pseudo log f=
ile for the scheduler might look something like:<br><br>timestamp: task X: =
foobar: error: file not found=C2=A0=C2=A0=C2=A0=C2=A0 <-- task failed du=
e to an error<br>timestamp: task X: task aborted=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 <br><br>timestamp: task Y: error: threw something this i=
t what it means=C2=A0 <-- task failed by throwing an exception<br>timest=
amp: task Y: task aborted=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 <b=
r><br>timestamp: task Z: error: internal error: assertion failure=C2=A0 Y !=
=3D X+1: report bug!<br>timestamp: task Z: task aborted<br><br>timestamp: t=
ask BIG: task started<br>timestamp: task Little: task started<br><br>timest=
amp: task Little: error: internal error: not enough memory <-- allocatio=
n failed for this task (it could recover)<br>timestamp: task Little: task a=
borted<br><br>timestamp: task BIG: error: internal error: not enough memory=
<-- allocation failed for this task (it probably couldn't recover)<=
br>timestamp: task BIG: task aborted<br><br>timestamp: task Little: task re=
trying<br>timestamp: task Little: task finished<br><br><br>Aborting a task =
is exceptional.<br>Logging is a responsibility of the scheduler not the tas=
k.<br><br>Memory exhaustion may be recoverable. <br>The BIG task doesn'=
t have sufficient memory to proceed but another task might after we recover=
the memory it used.<br>Or maybe there are too many things running at once.=
Maybe we have to scale down to performing tasks in serial.<br><br>The disc=
ussion suggests adding special kinds of "panic" for some things w=
hich were previously exceptions.<br><br>I like that we have contract violat=
ion handlers in C++20 and that we can throw provided the function violated<=
br>is not noexcept.<br>I like that we have the same for OOM via std::new_ha=
ndler though I think I would do this with a pool allocator myself.<br><br>I=
ts possible we want that to be a choice even for corruption.<br>I'm not=
sure how many types of corruption can be detected reliably.<br>If we take =
stack overflow as an example. That should kill the thread but perhaps not t=
he application.<br>*So it should be "std::terminate_thread()" not=
std::terminate()?*<br><br>We can't require your programs never fail bu=
t we can permit attempts (as long as they are not 100% futile)<br>at recove=
ry and graceful failure.<br><br>Regards,<br><br>Bruce<br><br></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/8fbad2b5-d934-4471-a63c-b4c505ef8a8d%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/8fbad2b5-d934-4471-a63c-b4c505ef8a8d=
%40isocpp.org</a>.<br />
------=_Part_529_2013518403.1544723019584--
------=_Part_528_1558586904.1544723019583--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Thu, 13 Dec 2018 15:59:04 -0800 (PST)
Raw View
------=_Part_592_321824669.1544745544971
Content-Type: multipart/alternative;
boundary="----=_Part_593_1350278833.1544745544972"
------=_Part_593_1350278833.1544745544972
Content-Type: text/plain; charset="UTF-8"
Firstly, I'll only answer from the perspective of P1095. I cannot speak for
Herb's P0709.
> 1. how do we handle propagating exceptions?
> This was touched upon in the thread [here](
> https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/D65DeZK23kg)
>
> void X()
> {
> try
> {
> Y()
> }
> catch(error e)
> {
> }
> }
>
> int Y() /*not declared throws?*/
> {
> return Z();
> }
>
> int Z() throws
> {
> }
>
>
> *Do users need to define two versions of Y() one declared throws() and one
> not?*
>
One cannot overload on throws in P0709 nor P1095.
> *Does the compiler need to create two versions of Y() internally?*
>
In P1095 Y() would cause a conversion from value-based throw to type-based
throw. The catch of std::error would work as expected.
>
> If it is a compile error for Y() to use "Z() throws" without itself being
> declared "throws" then
> you have an extra overhead in replacing dynamic exceptions.
> If X() previously caught dynamic exceptions from Z().
> Y() previously did not have to be aware of the contract between X and Z.
> It could
> choose to be ignorant of whether or not Z() throws an exception.
> To convert to static exceptions it now has itself to be declared throws.
> I can see this being an issue where there is a long chain of functions
> between the
> thrower and catcher.
>
None of the above is an issue with P1095. Static exception throws
implicitly convert to dynamic exception throws. Catches of std::error work
on both dynamic and static exception throws.
>
> I was thinking that the correct thing is that the compiler generates one
> verison of Y()
> that is effectively declared throws() but there are two problems with this:
>
> Callers of Y() previously not using exceptions have to pay the cost.
> If Y() is a library function it cannot be recompiled to use the new
> calling convention.
>
> This may seem like a false concern but consider callbacks.
>
One of WG14's big concerns with the proposal is that fails(T) functions
could not be used in a function pointer, which could generate surprise.
Me personally I felt that if you mismatch a fails(T) function with a
non-fails(T) function pointer, that's 100% on you for doing UB. But others
on WG14 proposed an offset workaround whereby every fails(T) function would
gain a preamble, and if mismatched/called wrong and the function failed, it
would trigger a contract failure/call abort(). This is effectively
noexcept, but for C, and I can't say I like it any better.
>
> // maybe in a library...
> struct snafu
> {
> callback* q;
>
> snafu(callback* q_):q(q) {}
>
> int Y() /*not declared throws?*/
> {
> return (*q)();
> }
> };
>
> int Z() throws {
> }
>
> void X() {
> try {
> Y(&Z); // Am I undefined behaviour?
> }
> catch(error e)
> {
> }
> }
>
> Should this compile or be undefined behaviour?
> Must the callback be declared not throwing?
> With dynamic exceptions this is not necessary. I can use a callable
> regardless of whether or not it throws.
>
Me personally I'd make noexcept and throws part of the type of a function
pointer, but not overloadable on that.
But as I mentioned, others on WG14 feel it's workable to be more relaxed.
>
> This suggests that static exceptions as currently specified cannot
> replace dynamic exceptions in all cases.
>
Under P1095, one would not want to replace them. There are valid use cases
for dynamic exceptions, and you should use them where they are the best
solution to the problem. Under P1095, static exceptions are a guaranteed
subset of dynamic exceptions, and dynamic exceptions of STL exceptions
types map one-one onto static exceptions, but not user defined exception
types (unless they tell std::error how to map them).
>
> 2. What are the semantics for void functions?
> E.g.
>
> void Z() throws {
> }
>
> I'm presuming this is legal.
> The return channel was not previously used but now will be.
> Assuming clever compilers could elide it in this case, this may add some
> (small) overhead.
>
That's fine under P1095. In fact, void Z() throws(void); is legal under
P1095. It may even be useful :).
> 3. ABI overhead is lower but not zero
>
> Return values can be either on the stack or in a register.
> The intention is that std::error is (at least on many processors) small
> enough to fit into a register.
> So there should be no stack overhead for a void function but there
> is a register overhead if an implementation was not previously using a
> return register.
>
Return registers are clobbered in void functions in every calling
convention I can think of. It's due to legacy compatibility with K&R C,
which had no void returns. So the register overhead is always there anyway.
> There is also an overhead for the additional flag (or register) for
> indicating whether
> the return value is a return or an error.
>
Probably only an issue on RISC-V.
>
> A tightly constrained embedded system is one place where static exceptions
> are targeted
> as a palatable replacement for dynamic exceptions.
> The total number and width of registers available may be constrained.
> On a tightly constrained embedded system an overhead of two registers
> might be significant.
>
I think calling convention overhead will be dwarfed by the overhead of
checking for failure after every function return on an in-order CPU. On
out-of-order CPUs it'll usually be free, but not for the in-order CPUs of
small computers.
However said small computers are probably already checking the return value
of a function anyway, as none of them will be enabling C++ exceptions,
assuming the toolchain even supports those anyway.
>
> 4. Can we do better?
>
> Perhaps a single error 'bit' could flag the compiler to look in a well
> defined location
> on the stack for the actual error code.
> It could be in the stack frame of the static exception handler and
> referenced by an
> offset from the stack pointer.
>
> The register overhead is less.
> However, though it seems like Y() doesn't need to know whether Z() throws
> something needs to tell Z() where to put the error code.
> So all this gives is the option to shift the register overhead from the
> output side of Z() to the input.
> That might be help with some extreme register shuffling perhaps.
>
> How wide does a register need to be to represent the error code?
>
> Could we optimise the use of a single register return so that for example
> the
> high order bit contains the error flag and if set rest of the register is
> the error code.
>
> On the other hand perhaps Z() can look in a known (set at link time)
> location for the error location
> just like the few other handlers we have.
> This has to be per thread, perhaps even per coroutine context.
>
Individual toolchains can decide on architecture-possible local
optimisations. We can suggest an approach, but totally up to each vendor to
decide. So long as they choose consistent with all other toolchains on
their architecture.
>
> I've read "Draft 2 of D1095/N2259 C _Either(A, B) proposal paper"
> and I see it gives examples or using the carry or zero flags.
>
> I also read http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2289.pdf
> <http://www.google.com/url?q=http%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg14%2Fwww%2Fdocs%2Fn2289.pdf&sa=D&sntz=1&usg=AFQjCNGms8HVq8rL42tyJ0frguFbl-A76A>
> which is even better.
> This look like a good way forwards.
>
>
> https://www.reddit.com/r/rust/comments/8wlphf/d1095r0n2xxx_draft_3_a_c_either_metatype_so_rust/
> "Currently WG14 is feeling that my proposal is nowhere near radical
> enough. They're actually currently talking about breaking binary
> compatibility by making all C functions implicitly return a union type,
> which is very radical for C."
>
> @Niall: This is interesting. Could you expand on it?
>
During the WG14 meeting papers were promised for the next meeting, which
I'll be physically attending.
> I would have expected a lot of resistance.
> The opinion of people like Linus Torvalds (i.e trying to maximise
> low-level performance in C) on this would be interesting.
>
> I also found:
>
>
> https://groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/BnNRLtFXJMA/G3gIZ3FGBgAJ;context-place=forum/std-proposals
>
> Some of the talk has been of "zero overhead" exceptions.
> Surely setting a flag is not quite zero.
> Propogating the flag and the error through functions which might want to
> use the flag
> or the return register themselves is even bigger.
>
Some people like using TLS to store out of band state.
e.g. https://zajo.github.io/boost-noexcept/
>
> 5. Has anyone considered something like a "conditions system" for C++?
>
> In Common LISP for example a function raises a "condition" rather than an
> exception.
> There is no stack unwinding.
> Instead there are handlers which are active in the current context (think
> set_new_handler() but not OOM)
> A handler may accept or ignore a condition allowing the next handler in
> the chain to try it
> (like a catch block).
> A handler can abort, ignore or restart the function from designated
> restart points.
>
> So Common LISP effectively supports resumable exceptions which were ruled
> out of C++ as dubious/dangerous
> a long time ago. Regardless of that the rest of the Scheme (pun intended)
> may have merits.
>
> To do this in C++ at present we would have to supply handlers as callbacks.
> That is rather than:
>
> Y()
> {
> try {
> X()
> }
> catch(A) {
> handler1()
> }
> catch(B) {
> handler2()
> }
> }
>
> We currently might write something like:
>
> X'(handler& handler)
> {
> // stuff X did
> if (A) handler.handle(A) //calls handler1
> if (B) handler.handle(B) //calls handler2
> }
>
> Y()
> {
> X(Handler().setAHandler(handler1).setAHandler(handler2));
> }
>
> An interesting thing about this pattern is that stack unwinding (one of
> the current costs) is (sort of) optional
> Could language level support for something like this complement or compete
> with static exceptions?
>
> Language support might include translating "try" to this kind of
> implementation so keep error information
> out of band as we desire.
>
I think combining async exceptions with sync exceptions makes it virtually
impossible to optimise code. MSVC is the only compiler which supports your
proposal, and the codegen with async-to-sync exception support is abysmal.
I do have a signal handler proposal in the works for WG21. It lets you run
a piece of code, and if anything terminal happens, you can deal with it and
resume, or cancel the entire thing without stack unwind. Nobody on WG21
seems to truly hate it, so it has a chance. I just need to find the time to
write it up.
>
> 6. Contracts and OOM errors can be exceptional
> Tl;Dr; panics should have handlers
>
> We use design by contract and currently require assertions to throw an
> AssertionFailure exception so that:
> 1. we can catch them in the right place and report the bug appropriately
> 2. don't have to terminate
>
> For example we have a multi-thread fault tolerant scheduler that executes
> tasks.
> A bug in one task must not kill the scheduler.
> The scheduler must continue to execute other tasks.
>
> Tasks are not expected to fail. Therefore we treat failure as exceptional
> rather than just a kind of error that has to be handled.
>
> An assertion indicates a bug. The bug must be reported.
>
Bugs come in classes of severity. Assertion failures are logic errors. They
should not be recoverable. They should mean termination, except for the
sole case of when run within a testing framework.
Niall
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d4ff53b7-e95e-4577-ad77-d7b222172db7%40isocpp.org.
------=_Part_593_1350278833.1544745544972
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div>Firstly, I'll only answer from the perspective of=
P1095. I cannot speak for Herb's P0709.</div><div>=C2=A0</div><blockqu=
ote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left=
: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">1. how do we handle p=
ropagating exceptions?<br>This was touched upon in the thread [here](<a hre=
f=3D"https://groups.google.com/a/isocpp.org/forum/#!topic/sg14/D65DeZK23kg"=
target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D'https:/=
/groups.google.com/a/isocpp.org/forum/#!topic/sg14/D65DeZK23kg';return =
true;" onclick=3D"this.href=3D'https://groups.google.com/a/isocpp.org/f=
orum/#!topic/sg14/D65DeZK23kg';return true;">https://groups.google.<wbr=
>com/a/isocpp.org/forum/#!<wbr>topic/sg14/D65DeZK23kg</a>)<br><br>=C2=A0=C2=
=A0=C2=A0 void X()<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 try <br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Y()<br>=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 catch(error e)<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 }<br><br=
>=C2=A0=C2=A0=C2=A0 int Y() /*not declared throws?*/ <br>=C2=A0=C2=A0=C2=A0=
{<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return Z();<br>=C2=A0=C2=A0=C2=
=A0 }<br><br>=C2=A0=C2=A0=C2=A0 int Z() throws <br>=C2=A0=C2=A0=C2=A0 {<br>=
=C2=A0=C2=A0=C2=A0 }<br><br><br>*Do users need to define two versions of Y(=
) one declared throws() and one not?*<br></div></blockquote><div><br></div>=
<div>One cannot overload on throws in P0709 nor P1095.</div><div>=C2=A0<br>=
</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8=
ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">*Does t=
he compiler need to create two versions of Y() internally?*<br></div></bloc=
kquote><div><br></div><div>In P1095 Y() would cause a conversion from value=
-based throw to type-based throw. The catch of std::error would work as exp=
ected.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"mar=
gin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><=
div dir=3D"ltr"><br>If it is a compile error for Y() to use "Z() throw=
s" without itself being declared "throws" then<br>you have a=
n extra overhead in replacing dynamic exceptions.<br>If X() previously caug=
ht dynamic exceptions from Z(). <br>Y() previously did not have to be aware=
of the contract between X and Z. It could<br>choose to be ignorant of whet=
her or not Z() throws an exception.<br>To convert to static exceptions it n=
ow has itself to be declared throws.<br>I can see this being an issue where=
there is a long chain of functions between the<br>thrower and catcher.<br>=
</div></blockquote><div><br></div><div>None of the above is an issue with P=
1095. Static exception throws implicitly convert to dynamic exception throw=
s. Catches of std::error work on both dynamic and static exception throws.<=
/div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;=
margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=
=3D"ltr"><br>I was thinking that the correct thing is that the compiler gen=
erates one verison of Y()<br>that is effectively declared throws() but ther=
e are two problems with this:<br><br>Callers of Y() previously not using ex=
ceptions have to pay the cost.<br>If Y() is a library function it cannot be=
recompiled to use the new calling convention.<br><br>This may seem like a =
false concern but consider callbacks.<br></div></blockquote><div><br></div>=
<div>One of WG14's big concerns with the proposal is that fails(T) func=
tions could not be used in a function pointer, which could generate surpris=
e.</div><div><br></div><div>Me personally I felt that if you mismatch a fai=
ls(T) function with a non-fails(T) function pointer, that's 100% on you=
for doing UB. But others on WG14 proposed an offset workaround whereby eve=
ry fails(T) function would gain a preamble, and if mismatched/called wrong =
and the function failed, it would trigger a contract failure/call abort(). =
This is effectively noexcept, but for C, and I can't say I like it any =
better.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">=
<div dir=3D"ltr"><br>=C2=A0=C2=A0=C2=A0 // maybe in a library...<br>=C2=A0=
=C2=A0=C2=A0 struct snafu<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 callback* q;<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 snafu(callback* q_):q(q) {}<br>=C2=A0=C2=A0=C2=A0 <br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 int Y() /*not declared throws?*/ <br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0 return (*q)();<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0 }<br>=C2=A0=C2=A0=C2=A0 };<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0=
int Z() throws {<br>=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=
=C2=A0=C2=A0 void X() {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Y(&Z); // Am I u=
ndefined behaviour?<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0 catch(error e)<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=
}<br><br>Should this compile or be undefined behaviour?<br>Must the callba=
ck be declared not throwing?<br>With dynamic exceptions this is not necessa=
ry. I can use a callable regardless of whether or not it throws.<br></div><=
/blockquote><div><br></div><div>Me personally I'd make noexcept and thr=
ows part of the type of a function pointer, but not overloadable on that.</=
div><div><br></div><div>But as I mentioned, others on WG14 feel it's wo=
rkable to be more relaxed.</div><div>=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;"><div dir=3D"ltr"><br>This suggests that static exception=
s as currently specified cannot <br>replace dynamic exceptions in all cases=
..<br></div></blockquote><div><br></div><div>Under P1095, one would not want=
to replace them. There are valid use cases for dynamic exceptions, and you=
should use them where they are the best solution to the problem. Under P10=
95, static exceptions are a guaranteed subset of dynamic exceptions, and dy=
namic exceptions of STL exceptions types map one-one onto static exceptions=
, but not user defined exception types (unless they tell std::error how to =
map them).</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D=
"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex=
;"><div dir=3D"ltr"><br>2. What are the semantics for void functions?<br>E.=
g.<br><br>void Z() throws {<br>}<br><br>I'm presuming this is legal.<br=
>The return channel was not previously used but now will be.<br>Assuming cl=
ever compilers could elide it in this case, this may add some (small) overh=
ead.<br></div></blockquote><div><br></div><div>That's fine under P1095.=
In fact, void Z() throws(void); is legal under P1095. It may even be usefu=
l :).</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"marg=
in: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><d=
iv dir=3D"ltr">3. ABI overhead is lower but not zero<br><br>Return values c=
an be either on the stack or in a register.<br>The intention is that std::e=
rror is (at least on many processors) small enough to fit into a register.<=
br>So there should be no stack overhead for a void function but there<br>is=
a register overhead if an implementation was not previously using a return=
register.<br></div></blockquote><div><br></div><div>Return registers are c=
lobbered in void functions in every calling convention I can think of. It&#=
39;s due to legacy compatibility with K&R C, which had no void returns.=
So the register overhead is always there anyway.</div><div>=C2=A0</div><bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">There is also an=
overhead for the additional flag (or register) for indicating whether<br>t=
he return value is a return or an error.<br></div></blockquote><div><br></d=
iv><div>Probably only an issue on RISC-V.</div><div>=C2=A0</div><blockquote=
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1=
px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>A tightly constraine=
d embedded system is one place where static exceptions are targeted<br>as a=
palatable replacement for dynamic exceptions.<br>The total number and widt=
h of registers available may be constrained.<br>On a tightly constrained em=
bedded system an overhead of two registers might be significant.<br></div><=
/blockquote><div><br></div><div>I think calling convention overhead will be=
dwarfed by the overhead of checking for failure after every function retur=
n on an in-order CPU. On out-of-order CPUs it'll usually be free, but n=
ot for the in-order CPUs of small computers.</div><div><br></div><div>Howev=
er said small computers are probably already checking the return value of a=
function anyway, as none of them will be enabling C++ exceptions, assuming=
the toolchain even supports those anyway.</div><div>=C2=A0</div><blockquot=
e class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: =
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br>4. Can we do better=
?<br><br>Perhaps a single error 'bit' could flag the compiler to lo=
ok in a well defined location<br>on the stack for the actual error code. <b=
r>It could be in the stack frame of the static exception handler and refere=
nced by an<br>offset from the stack pointer.<br><br>The register overhead i=
s less.<br>However, though it seems like Y() doesn't need to know wheth=
er Z() throws <br>something needs to tell Z() where to put the error code.<=
br>So all this gives is the option to shift the register overhead from the =
output side of Z() to the input.<br>That might be help with some extreme re=
gister shuffling perhaps.<br><br>How wide does a register need to be to rep=
resent the error code?<br><br>Could we optimise the use of a single registe=
r return so that for example the<br>high order bit contains the error flag =
and if set rest of the register is the error code.<br><br>On the other hand=
perhaps Z() can look in a known (set at link time) location for the error =
location<br>just like the few other handlers we have.<br>This has to be per=
thread, perhaps even per coroutine context.<br></div></blockquote><div><br=
></div><div>Individual toolchains can decide on architecture-possible local=
optimisations. We can suggest an approach, but totally up to each vendor t=
o decide. So long as they choose consistent with all other toolchains on th=
eir architecture.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" s=
tyle=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-le=
ft: 1ex;"><div dir=3D"ltr"><br>I've read "Draft 2 of D1095/N2259 C=
_Either(A, B) proposal paper"<br>and I see it gives examples or using=
the carry or zero flags. <br><br>I also read <a href=3D"http://www.google.=
com/url?q=3Dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg14%2Fwww%2Fdocs=
%2Fn2289.pdf&sa=3DD&sntz=3D1&usg=3DAFQjCNGms8HVq8rL42tyJ0frguFb=
l-A76A" target=3D"_blank" rel=3D"nofollow" onmousedown=3D"this.href=3D'=
http://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%=
2Fwg14%2Fwww%2Fdocs%2Fn2289.pdf\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGms=
8HVq8rL42tyJ0frguFbl-A76A';return true;" onclick=3D"this.href=3D'ht=
tp://www.google.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2F=
wg14%2Fwww%2Fdocs%2Fn2289.pdf\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGms8H=
Vq8rL42tyJ0frguFbl-A76A';return true;">http://www.open-std.org/jtc1/<wb=
r>sc22/wg14/www/docs/n2289.pdf</a> which is even better.<br>This look like =
a good way forwards.<br><br><a href=3D"https://www.reddit.com/r/rust/commen=
ts/8wlphf/d1095r0n2xxx_draft_3_a_c_either_metatype_so_rust/" target=3D"_bla=
nk" rel=3D"nofollow" onmousedown=3D"this.href=3D'https://www.google.com=
/url?q\x3dhttps%3A%2F%2Fwww.reddit.com%2Fr%2Frust%2Fcomments%2F8wlphf%2Fd10=
95r0n2xxx_draft_3_a_c_either_metatype_so_rust%2F\x26sa\x3dD\x26sntz\x3d1\x2=
6usg\x3dAFQjCNGOjuzGCAwhuMcC0ur6P59AMjVS0Q';return true;" onclick=3D"th=
is.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fwww.reddit.com=
%2Fr%2Frust%2Fcomments%2F8wlphf%2Fd1095r0n2xxx_draft_3_a_c_either_metatype_=
so_rust%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNGOjuzGCAwhuMcC0ur6P59AMj=
VS0Q';return true;">https://www.reddit.com/r/rust/<wbr>comments/8wlphf/=
d1095r0n2xxx_<wbr>draft_3_a_c_either_metatype_<wbr>so_rust/</a><br>"Cu=
rrently WG14 is feeling that my proposal is nowhere near radical enough. Th=
ey're actually currently talking about breaking binary compatibility by=
making all C functions implicitly return a union type, which is very radic=
al for C."<br><br>@Niall: This is interesting. Could you expand on it?=
<br></div></blockquote><div><br></div><div>During the WG14 meeting papers w=
ere promised for the next meeting, which I'll be physically attending.<=
/div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;=
margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=
=3D"ltr">I would have expected a lot of resistance. <br>The opinion of peop=
le like Linus Torvalds (i.e trying to maximise low-level performance in C) =
on this would be interesting.<br><br>I also found:<br><br><a href=3D"https:=
//groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/BnNRLtFXJMA/G3gI=
Z3FGBgAJ;context-place=3Dforum/std-proposals" target=3D"_blank" rel=3D"nofo=
llow" onmousedown=3D"this.href=3D'https://groups.google.com/a/isocpp.or=
g/forum/#!msg/std-proposals/BnNRLtFXJMA/G3gIZ3FGBgAJ;context-place\x3dforum=
/std-proposals';return true;" onclick=3D"this.href=3D'https://group=
s.google.com/a/isocpp.org/forum/#!msg/std-proposals/BnNRLtFXJMA/G3gIZ3FGBgA=
J;context-place\x3dforum/std-proposals';return true;">https://groups.go=
ogle.com/a/<wbr>isocpp.org/forum/#!msg/std-<wbr>proposals/BnNRLtFXJMA/<wbr>=
G3gIZ3FGBgAJ;context-place=3D<wbr>forum/std-proposals</a><br><br>Some of th=
e talk has been of "zero overhead" exceptions.<br>Surely setting =
a flag is not quite zero.<br>Propogating the flag and the error through fun=
ctions which might want to use the flag<br>or the return register themselve=
s is even bigger.<br></div></blockquote><div><br></div><div>Some people lik=
e using TLS to store out of band state. e.g.=C2=A0https://zajo.github.io/bo=
ost-noexcept/</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=
=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: =
1ex;"><div dir=3D"ltr"><br>5. Has anyone considered something like a "=
conditions system" for C++?<br><br>In Common LISP for example a functi=
on raises a "condition" rather than an exception.<br>There is no =
stack unwinding.<br>Instead there are handlers which are active in the curr=
ent context (think set_new_handler() but not OOM)<br>A handler may accept o=
r ignore a condition allowing the next handler in the chain to try it<br>(l=
ike a catch block).<br>A handler can abort, ignore or restart the function =
from designated restart points.<br><br>So Common LISP effectively supports =
resumable exceptions which were ruled out of C++ as dubious/dangerous<br>a =
long time ago. Regardless of that the rest of the Scheme (pun intended) may=
have merits.<br><br>To do this in C++ at present we would have to supply h=
andlers as callbacks.<br>That is rather than:<br><br>=C2=A0=C2=A0=C2=A0 Y()=
<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 X()<br>=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 catch(A)=
{<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 handler1()<br>=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ca=
tch(B) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 handler2()<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 }<br><br>We cu=
rrently might write something like:<br><br>=C2=A0=C2=A0=C2=A0 X'(handle=
r& handler) <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =
// stuff X did<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (A) handler.handle(A) /=
/calls handler1<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (B) handler.handle(B) =
//calls handler2<br>=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=
=C2=A0=C2=A0 Y()<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 X(Handler().setAHandler(<wbr>handler1).setAHandler(<wbr>handler2));<=
br>=C2=A0=C2=A0=C2=A0 }<br><br>An interesting thing about this pattern is t=
hat stack unwinding (one of the current costs) is (sort of) optional<br>Cou=
ld language level support for something like this complement or compete wit=
h static exceptions?<br><br>Language support might include translating &quo=
t;try" to this kind of implementation so keep error information<br>out=
of band as we desire.<br></div></blockquote><div><br></div><div>I think co=
mbining async exceptions with sync exceptions makes it virtually impossible=
to optimise code. MSVC is the only compiler which supports your proposal, =
and the codegen with async-to-sync exception support is abysmal.</div><div>=
<br></div><div>I do have a signal handler proposal in the works for WG21. I=
t lets you run a piece of code, and if anything terminal happens, you can d=
eal with it and resume, or cancel the entire thing without stack unwind. No=
body on WG21 seems to truly hate it, so it has a chance. I just need to fin=
d the time to write it up.</div><div>=C2=A0</div><blockquote class=3D"gmail=
_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;p=
adding-left: 1ex;"><div dir=3D"ltr"><br>6. Contracts and OOM errors can be =
exceptional<br>Tl;Dr; panics should have handlers<br><br>We use design by c=
ontract and currently require assertions to throw an AssertionFailure excep=
tion so that:<br>=C2=A01. we can catch them in the right place and report t=
he bug appropriately<br>=C2=A02. don't have to terminate<br><br>For exa=
mple we have a multi-thread fault tolerant scheduler that executes tasks.<b=
r>A bug in one task must not kill the scheduler.<br>The scheduler must cont=
inue to execute other tasks.<br><br>Tasks are not expected to fail. Therefo=
re we treat failure as exceptional rather than just a kind of error that ha=
s to be handled.<br><br>An assertion indicates a bug. The bug must be repor=
ted.<br></div></blockquote><div><br></div><div>Bugs come in classes of seve=
rity. Assertion failures are logic errors. They should not be recoverable. =
They should mean termination, except for the sole case of when run within a=
testing framework.</div><div><br></div><div>Niall</div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d4ff53b7-e95e-4577-ad77-d7b222172db7%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d4ff53b7-e95e-4577-ad77-d7b222172db7=
%40isocpp.org</a>.<br />
------=_Part_593_1350278833.1544745544972--
------=_Part_592_321824669.1544745544971--
.
Author: tortoise741@gmail.com
Date: Thu, 13 Dec 2018 17:16:46 -0800 (PST)
Raw View
------=_Part_625_1242877308.1544750206151
Content-Type: multipart/alternative;
boundary="----=_Part_626_1848536233.1544750206151"
------=_Part_626_1848536233.1544750206151
Content-Type: text/plain; charset="UTF-8"
On Thursday, 13 December 2018 23:59:05 UTC, Niall Douglas wrote:
>
> Firstly, I'll only answer from the perspective of P1095. I cannot speak
> for Herb's P0709.
>
>
>
>> 1. how do we handle propagating exceptions?
>>
>
> One cannot overload on throws in P0709 nor P1095.
>
>
I completely missed that. So throws is basically declaring a change in the
calling convention.
*Does the compiler need to create two versions of Y() internally?*
>>
>
> In P1095 Y() would cause a conversion from value-based throw to type-based
> throw. The catch of std::error would work as expected.
>
I see so this would be implemented via the exception handler code in the
C++ runtime used?
The conversion wouldn't actually happen in Y() itself it would happen only
if the exception handler found a suitable
try catch block in Y() that required it.
If that was in the next frame up and that frame was a "throws()" function
using the new ABI it would just use the cheaper value
based exception instead.
I think I've confused myself here as we are using the return channel rather
than the handler in the C++ runtime for static exceptions.
So somehow Y() must detect the error flag even though it is using the old
calling convention?
> None of the above is an issue with P1095. Static exception throws
> implicitly convert to dynamic exception throws. Catches of std::error work
> on both dynamic and static exception throws.
>
>>
>> This may seem like a false concern but consider callbacks.
>>
>
> One of WG14's big concerns with the proposal is that fails(T) functions
> could not be used in a function pointer, which could generate surprise.
>
> Me personally I felt that if you mismatch a fails(T) function with a
> non-fails(T) function pointer, that's 100% on you for doing UB. But others
> on WG14 proposed an offset workaround whereby every fails(T) function would
> gain a preamble, and if mismatched/called wrong and the function failed, it
> would trigger a contract failure/call abort(). This is effectively
> noexcept, but for C, and I can't say I like it any better.
>
>
I think I see their point. UB is harder to debug than an explicit failure.
>> // maybe in a library...
>> struct snafu
>> {
>> callback* q;
>>
>> snafu(callback* q_):q(q) {}
>>
>> int Y() /*not declared throws?*/
>> {
>> return (*q)();
>> }
>> };
>>
>> int Z() throws {
>> }
>>
>> void X() {
>> try {
>> Y(&Z); // Am I undefined behaviour?
>> }
>> catch(error e)
>> {
>> }
>> }
>>
>> Should this compile or be undefined behaviour?
>> Must the callback be declared not throwing?
>> With dynamic exceptions this is not necessary. I can use a callable
>> regardless of whether or not it throws.
>>
>
> Me personally I'd make noexcept and throws part of the type of a function
> pointer, but not overloadable on that.
>
> But as I mentioned, others on WG14 feel it's workable to be more relaxed.
>
I think you are right if you are in control of the implementation. However,
If the callback is being provided to a third party library you don't have
that option.
>
>> This suggests that static exceptions as currently specified cannot
>> replace dynamic exceptions in all cases.
>>
>
> Under P1095, one would not want to replace them. There are valid use cases
> for dynamic exceptions, and you should use them where they are the best
> solution to the problem. Under P1095, static exceptions are a guaranteed
> subset of dynamic exceptions, and dynamic exceptions of STL exceptions
> types map one-one onto static exceptions, but not user defined exception
> types (unless they tell std::error how to map them).
>
>
My interpretation of P0709 was that dynamic exceptions become legacy. What
uses remain for dynamic ones?
Is it function pointers etc. as I suggested?
>
>> 2. What are the semantics for void functions?
>> E.g.
>>
>> void Z() throws {
>> }
>>
>> I'm presuming this is legal.
>> The return channel was not previously used but now will be.
>> Assuming clever compilers could elide it in this case, this may add some
>> (small) overhead.
>>
>
> That's fine under P1095. In fact, void Z() throws(void); is legal under
> P1095. It may even be useful :).
>
That begs me to ask. Useful, how so?
>
>
>> 3. ABI overhead is lower but not zero
>>
>> Return values can be either on the stack or in a register.
>> The intention is that std::error is (at least on many processors) small
>> enough to fit into a register.
>> So there should be no stack overhead for a void function but there
>> is a register overhead if an implementation was not previously using a
>> return register.
>>
>
> Return registers are clobbered in void functions in every calling
> convention I can think of. It's due to legacy compatibility with K&R C,
> which had no void returns. So the register overhead is always there anyway.
>
>
>> There is also an overhead for the additional flag (or register) for
>> indicating whether
>> the return value is a return or an error.
>>
>
> Probably only an issue on RISC-V.
>
>> A tightly constrained embedded system is one place where static
>> exceptions are targeted
>> as a palatable replacement for dynamic exceptions.
>> The total number and width of registers available may be constrained.
>> On a tightly constrained embedded system an overhead of two registers
>> might be significant.
>>
>
> I think calling convention overhead will be dwarfed by the overhead of
> checking for failure after every function return on an in-order CPU. On
> out-of-order CPUs it'll usually be free, but not for the in-order CPUs of
> small computers.
>
> However said small computers are probably already checking the return
> value of a function anyway, as none of them will be enabling C++
> exceptions, assuming the toolchain even supports those anyway.
>
I was actually thinking of the overhead of the instructions to check the
error flag etc in each function / calling convention.
I thought an explicit aim of zero overhead exceptions was to eliminate
cases where C++ exceptions needed to switched off.
Maybe is a case of YMMV.
>
> Some people like using TLS to store out of band state. e.g.
> https://zajo.github.io/boost-noexcept/
>
>
Good.
>> 5. Has anyone considered something like a "conditions system" for C++?
>>
>> In Common LISP for example a function raises a "condition" rather than an
>> exception.
>> There is no stack unwinding.
>> Instead there are handlers which are active in the current context (think
>> set_new_handler() but not OOM)
>> A handler may accept or ignore a condition allowing the next handler in
>> the chain to try it
>> (like a catch block).
>> A handler can abort, ignore or restart the function from designated
>> restart points.
>>
>> So Common LISP effectively supports resumable exceptions which were ruled
>> out of C++ as dubious/dangerous
>> a long time ago. Regardless of that the rest of the Scheme (pun intended)
>> may have merits.
>>
>> To do this in C++ at present we would have to supply handlers as
>> callbacks.
>> That is rather than:
>>
>> Y()
>> {
>> try {
>> X()
>> }
>> catch(A) {
>> handler1()
>> }
>> catch(B) {
>> handler2()
>> }
>> }
>>
>> We currently might write something like:
>>
>> X'(handler& handler)
>> {
>> // stuff X did
>> if (A) handler.handle(A) //calls handler1
>> if (B) handler.handle(B) //calls handler2
>> }
>>
>> Y()
>> {
>> X(Handler().setAHandler(handler1).setAHandler(handler2));
>> }
>>
>> An interesting thing about this pattern is that stack unwinding (one of
>> the current costs) is (sort of) optional
>> Could language level support for something like this complement or
>> compete with static exceptions?
>>
>> Language support might include translating "try" to this kind of
>> implementation so keep error information
>> out of band as we desire.
>>
>
> I think combining async exceptions with sync exceptions makes it virtually
> impossible to optimise code. MSVC is the only compiler which supports your
> proposal, and the codegen with async-to-sync exception support is abysmal.
>
> I do have a signal handler proposal in the works for WG21. It lets you run
> a piece of code, and if anything terminal happens, you can deal with it and
> resume, or cancel the entire thing without stack unwind. Nobody on WG21
> seems to truly hate it, so it has a chance. I just need to find the time to
> write it up.
>
I was thinking of conditions being synchronous in this example but the
asynchronous case is very interesting. For that its more a like signals and
slots and goes way beyond errors.
>
>
>> 6. Contracts and OOM errors can be exceptional
>> Tl;Dr; panics should have handlers
>>
>
> Bugs come in classes of severity. Assertion failures are logic errors.
> They should not be recoverable. They should mean termination, except for
> the sole case of when run within a testing framework.
>
> Niall
>
I strongly disagree. One bug should not bring down a fault tolerant system.
The task related to that bug is generally not recoverable but the rest of
the system is.
Likewise, I would much rather keep my assertions enabled and detected in
production (except where peformancei is factor) rather than risk entering
into UB.
After all if a pre-condition you've asserted is not met in principle your
function could do anything including format your hard drive.
If you allow that then people might be persuaded to add unnecessary
defensive code to the function to prevent it.
Regards,
Bruce.
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/55b63f34-20ba-458d-bb43-ca9934ac4c75%40isocpp.org.
------=_Part_626_1848536233.1544750206151
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br><br>On Thursday, 13 December 2018 23:59:05 UTC, Niall =
Douglas wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-=
left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr=
"><div>Firstly, I'll only answer from the perspective of P1095. I canno=
t speak for Herb's P0709.</div><div>=C2=A0<br></div></div></blockquote>=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>=C2=A0</=
div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;b=
order-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">1. how do we h=
andle propagating exceptions?<br></div></blockquote><div><br></div><div>One=
cannot overload on throws in P0709 nor P1095.</div><div>=C2=A0<br></div></=
div></blockquote><div>I completely missed that. So throws is basically decl=
aring a change in the calling convention.<br></div><div><br></div><blockquo=
te class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left:=
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote=
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px =
#ccc solid;padding-left:1ex"><div dir=3D"ltr">*Does the compiler need to cr=
eate two versions of Y() internally?*<br></div></blockquote><div><br></div>=
<div>In P1095 Y() would cause a conversion from value-based throw to type-b=
ased throw. The catch of std::error would work as expected.</div><div></div=
></div></blockquote><div><br></div><div>I see so this would be implemented =
via the exception handler code in the C++ runtime used?</div><div>The conve=
rsion wouldn't actually happen in Y() itself it would happen only if th=
e exception handler found a suitable</div><div>try catch block in Y() that =
required it.</div><div>If that was in the next frame up and that frame was =
a "throws()" function using the new ABI it would just use the che=
aper value</div><div>based exception instead.</div><div><br></div><div>I th=
ink I've confused myself here as we are using the return channel rather=
than the handler in the C++ runtime for static exceptions.</div><div>So so=
mehow Y() must detect the error flag even though it is using the old callin=
g convention?<br></div><div><br></div><div></div><div></div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #=
ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br><div>None of the above i=
s an issue with P1095. Static exception throws implicitly convert to dynami=
c exception throws. Catches of std::error work on both dynamic and static e=
xception throws.</div><blockquote class=3D"gmail_quote" style=3D"margin:0;m=
argin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"l=
tr"><br>This may seem like a false concern but consider callbacks.<br></div=
></blockquote><div><br></div><div>One of WG14's big concerns with the p=
roposal is that fails(T) functions could not be used in a function pointer,=
which could generate surprise.</div><div><br></div><div>Me personally I fe=
lt that if you mismatch a fails(T) function with a non-fails(T) function po=
inter, that's 100% on you for doing UB. But others on WG14 proposed an =
offset workaround whereby every fails(T) function would gain a preamble, an=
d if mismatched/called wrong and the function failed, it would trigger a co=
ntract failure/call abort(). This is effectively noexcept, but for C, and I=
can't say I like it any better.</div><div>=C2=A0</div></div></blockquo=
te><div>I think I see their point. UB is harder to debug than an explicit f=
ailure.</div><div><br></div><blockquote class=3D"gmail_quote" style=3D"marg=
in: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><d=
iv dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-l=
eft:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><br=
>=C2=A0=C2=A0=C2=A0 // maybe in a library...<br>=C2=A0=C2=A0=C2=A0 struct s=
nafu<br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 callba=
ck* q;<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 snafu=
(callback* q_):q(q) {}<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 int Y() /*not declared throws?*/ <br>=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 re=
turn (*q)();<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=
=A0 };<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0 int Z() throws {<br>=C2=
=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0 void X() {<b=
r>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 try {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0 Y(&Z); // Am I undefined behaviour?<br>=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
catch(error e)<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0 }<br><br>Should this compi=
le or be undefined behaviour?<br>Must the callback be declared not throwing=
?<br>With dynamic exceptions this is not necessary. I can use a callable re=
gardless of whether or not it throws.<br></div></blockquote><div><br></div>=
<div>Me personally I'd make noexcept and throws part of the type of a f=
unction pointer, but not overloadable on that.</div><div><br></div></div></=
blockquote><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left=
: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><d=
iv></div><div>But as I mentioned, others on WG14 feel it's workable to =
be more relaxed.</div></div></blockquote><div>=C2=A0</div><div><div>I think=
you are right if you are in control of the implementation. However, If the=
callback is being provided to a third party library you don't have tha=
t option.</div><div></div>=C2=A0</div><blockquote class=3D"gmail_quote" sty=
le=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left=
: 1ex;"><div dir=3D"ltr"><div></div><blockquote class=3D"gmail_quote" style=
=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"=
><div dir=3D"ltr"><br>This suggests that static exceptions as currently spe=
cified cannot <br>replace dynamic exceptions in all cases.<br></div></block=
quote><div><br></div><div>Under P1095, one would not want to replace them. =
There are valid use cases for dynamic exceptions, and you should use them w=
here they are the best solution to the problem. Under P1095, static excepti=
ons are a guaranteed subset of dynamic exceptions, and dynamic exceptions o=
f STL exceptions types map one-one onto static exceptions, but not user def=
ined exception types (unless they tell std::error how to map them).</div><d=
iv>=C2=A0</div></div></blockquote><div>My interpretation of P0709 was that =
dynamic exceptions become legacy. What uses remain for dynamic ones?<br></d=
iv><div>Is it function pointers etc. as I suggested?<br></div><div>=C2=A0<b=
r></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0=
..8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><bloc=
kquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-lef=
t:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><br>2. What are the sem=
antics for void functions?<br>E.g.<br><br>void Z() throws {<br>}<br><br>I&#=
39;m presuming this is legal.<br>The return channel was not previously used=
but now will be.<br>Assuming clever compilers could elide it in this case,=
this may add some (small) overhead.<br></div></blockquote><div><br></div><=
div>That's fine under P1095. In fact, void Z() throws(void); is legal u=
nder P1095. It may even be useful :).</div><div></div></div></blockquote><d=
iv><br></div><div>That begs me to ask. Useful, how so? <br></div><blockquot=
e class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: =
1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>=C2=A0</div><block=
quote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left=
:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">3. ABI overhead is lower=
but not zero<br><br>Return values can be either on the stack or in a regis=
ter.<br>The intention is that std::error is (at least on many processors) s=
mall enough to fit into a register.<br>So there should be no stack overhead=
for a void function but there<br>is a register overhead if an implementati=
on was not previously using a return register.<br></div></blockquote><div><=
br></div><div>Return registers are clobbered in void functions in every cal=
ling convention I can think of. It's due to legacy compatibility with K=
&R C, which had no void returns. So the register overhead is always the=
re anyway.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D=
"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><d=
iv dir=3D"ltr">There is also an overhead for the additional flag (or regist=
er) for indicating whether<br>the return value is a return or an error.<br>=
</div></blockquote><div><br></div><div>Probably only an issue on RISC-V.</d=
iv><div></div></div></blockquote><div></div><blockquote class=3D"gmail_quot=
e" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;paddin=
g-left: 1ex;"><div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"m=
argin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div=
dir=3D"ltr"><br>A tightly constrained embedded system is one place where s=
tatic exceptions are targeted<br>as a palatable replacement for dynamic exc=
eptions.<br>The total number and width of registers available may be constr=
ained.<br>On a tightly constrained embedded system an overhead of two regis=
ters might be significant.<br></div></blockquote><div><br></div><div>I thin=
k calling convention overhead will be dwarfed by the overhead of checking f=
or failure after every function return on an in-order CPU. On out-of-order =
CPUs it'll usually be free, but not for the in-order CPUs of small comp=
uters.</div><div><br></div><div>However said small computers are probably a=
lready checking the return value of a function anyway, as none of them will=
be enabling C++ exceptions, assuming the toolchain even supports those any=
way.</div><div></div></div></blockquote><div><div><br></div><div>I was actu=
ally thinking of the overhead of the instructions to check the error flag e=
tc in each function / calling convention.</div><div>I thought an explicit a=
im of zero overhead exceptions was to eliminate cases where C++ exceptions =
needed to switched off.</div><div>Maybe is a case of YMMV.<br></div>=C2=A0<=
/div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8e=
x;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><br><div=
>Some people like using TLS to store out of band state. e.g.=C2=A0<a href=
=3D"https://zajo.github.io/boost-noexcept/" target=3D"_blank" rel=3D"nofoll=
ow" onmousedown=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3=
A%2F%2Fzajo.github.io%2Fboost-noexcept%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3=
dAFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ';return true;" onclick=3D"this.href=
=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fzajo.github.io%2Fboos=
t-noexcept%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH6KbyLzahb2bG2jxeNkxI=
pMDWjDQ';return true;">https://zajo.github.io/<wbr>boost-noexcept/</a><=
/div><div>=C2=A0</div></div></blockquote><div>Good. <br></div><div><br></di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><blockquote=
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px =
#ccc solid;padding-left:1ex"><div dir=3D"ltr"><br>5. Has anyone considered =
something like a "conditions system" for C++?<br><br>In Common LI=
SP for example a function raises a "condition" rather than an exc=
eption.<br>There is no stack unwinding.<br>Instead there are handlers which=
are active in the current context (think set_new_handler() but not OOM)<br=
>A handler may accept or ignore a condition allowing the next handler in th=
e chain to try it<br>(like a catch block).<br>A handler can abort, ignore o=
r restart the function from designated restart points.<br><br>So Common LIS=
P effectively supports resumable exceptions which were ruled out of C++ as =
dubious/dangerous<br>a long time ago. Regardless of that the rest of the Sc=
heme (pun intended) may have merits.<br><br>To do this in C++ at present we=
would have to supply handlers as callbacks.<br>That is rather than:<br><br=
>=C2=A0=C2=A0=C2=A0 Y() <br>=C2=A0=C2=A0=C2=A0 {<br>=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0 try {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 X()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0 catch(A) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 handler1()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0 catch(B) {<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0 handler2()<br>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 }<br>=C2=A0=
=C2=A0=C2=A0 }<br><br>We currently might write something like:<br><br>=C2=
=A0=C2=A0=C2=A0 X'(handler& handler) <br>=C2=A0=C2=A0=C2=A0 {<br>=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 // stuff X did<br>=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0 if (A) handler.handle(A) //calls handler1<br>=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0 if (B) handler.handle(B) //calls handler2<br>=C2=A0=C2=A0=C2=A0 }=
<br>=C2=A0=C2=A0=C2=A0 <br>=C2=A0=C2=A0=C2=A0 Y()<br>=C2=A0=C2=A0=C2=A0 {<b=
r>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 X(Handler().setAHandler(<wbr>handler=
1).setAHandler(<wbr>handler2));<br>=C2=A0=C2=A0=C2=A0 }<br><br>An interesti=
ng thing about this pattern is that stack unwinding (one of the current cos=
ts) is (sort of) optional<br>Could language level support for something lik=
e this complement or compete with static exceptions?<br><br>Language suppor=
t might include translating "try" to this kind of implementation =
so keep error information<br>out of band as we desire.<br></div></blockquot=
e><div><br></div><div>I think combining async exceptions with sync exceptio=
ns makes it virtually impossible to optimise code. MSVC is the only compile=
r which supports your proposal, and the codegen with async-to-sync exceptio=
n support is abysmal.</div><div><br></div><div>I do have a signal handler p=
roposal in the works for WG21. It lets you run a piece of code, and if anyt=
hing terminal happens, you can deal with it and resume, or cancel the entir=
e thing without stack unwind. Nobody on WG21 seems to truly hate it, so it =
has a chance. I just need to find the time to write it up.</div><div></div>=
</div></blockquote><div><br></div><div>I was thinking of conditions being s=
ynchronous in this example but the asynchronous case is very interesting. F=
or that its more a like signals and slots and goes way beyond errors.<br></=
div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex=
;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>=C2=
=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.=
8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">6. Contra=
cts and OOM errors can be exceptional<br>Tl;Dr; panics should have handlers=
</div></blockquote><br><div>Bugs come in classes of severity. Assertion fai=
lures are logic errors. They should not be recoverable. They should mean te=
rmination, except for the sole case of when run within a testing framework.=
</div><div><br></div><div>Niall</div></div></blockquote><div><br></div><div=
>I strongly disagree. One bug should not bring down a fault tolerant system=
.. The task related to that bug is generally not recoverable but the rest of=
the system is.</div><div></div><div>Likewise, I would much rather keep my =
assertions enabled and detected in production (except where peformancei is =
factor) rather than risk entering into UB.</div><div>After all if a pre-con=
dition you've asserted is not met in principle your function could do a=
nything including format your hard drive.</div><div>If you allow that then =
people might be persuaded to add unnecessary defensive code to the function=
to prevent it.</div><div><br></div><div>Regards,</div><div><br></div><div>=
Bruce.<br></div><div>=C2=A0</div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/55b63f34-20ba-458d-bb43-ca9934ac4c75%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/55b63f34-20ba-458d-bb43-ca9934ac4c75=
%40isocpp.org</a>.<br />
------=_Part_626_1848536233.1544750206151--
------=_Part_625_1242877308.1544750206151--
.
Author: Niall Douglas <nialldouglas14@gmail.com>
Date: Fri, 14 Dec 2018 06:19:35 -0800 (PST)
Raw View
------=_Part_831_1648456076.1544797175275
Content-Type: multipart/alternative;
boundary="----=_Part_832_1414749272.1544797175276"
------=_Part_832_1414749272.1544797175276
Content-Type: text/plain; charset="UTF-8"
>
>
> *Does the compiler need to create two versions of Y() internally?*
>>>
>>
>> In P1095 Y() would cause a conversion from value-based throw to
>> type-based throw. The catch of std::error would work as expected.
>>
>
> I see so this would be implemented via the exception handler code in the
> C++ runtime used?
>
It would literally be as if "throw" to rethrow the exception. Dynamic
exceptions already get created and thrown by the C++ runtime. So it would
be exactly as now, exact same overhead and cost.
> The conversion wouldn't actually happen in Y() itself it would happen only
> if the exception handler found a suitable
> try catch block in Y() that required it.
>
Under P1095, if a static exception throw encounters a dynamically throwing
function during stack unwind, it gets converted immediately to a dynamic
exception throw. If all the code is visible to the compiler and there is a
static catch, its optimiser may elide the conversion under the as-if rule.
But that's up to the implementation.
> If that was in the next frame up and that frame was a "throws()" function
> using the new ABI it would just use the cheaper value
> based exception instead.
>
That's a possible optimisation, but not required.
>
> I think I've confused myself here as we are using the return channel
> rather than the handler in the C++ runtime for static exceptions.
> So somehow Y() must detect the error flag even though it is using the old
> calling convention?
>
Y() knows it is calling Z() which is marked throws. So it knows it must
check the return from Z().
>
>>> This may seem like a false concern but consider callbacks.
>>>
>>
>> One of WG14's big concerns with the proposal is that fails(T) functions
>> could not be used in a function pointer, which could generate surprise.
>>
>> Me personally I felt that if you mismatch a fails(T) function with a
>> non-fails(T) function pointer, that's 100% on you for doing UB. But others
>> on WG14 proposed an offset workaround whereby every fails(T) function would
>> gain a preamble, and if mismatched/called wrong and the function failed, it
>> would trigger a contract failure/call abort(). This is effectively
>> noexcept, but for C, and I can't say I like it any better.
>>
>>
> I think I see their point. UB is harder to debug than an explicit failure.
>
Personally I'd prefer a contract failure if you pass mismatched pointers
i.e. it always aborts on mismatch. But that's not the majority opinion,
currently.
>
>> Me personally I'd make noexcept and throws part of the type of a function
>> pointer, but not overloadable on that.
>>
>> But as I mentioned, others on WG14 feel it's workable to be more relaxed.
>>
>
> I think you are right if you are in control of the implementation.
> However, If the callback is being provided to a third party library you
> don't have that option.
>
Me personally I think you can always wrap a fails function with a non-fails
one, or vice versa. So you simply wrap the callback and pass the wrapper. I
agree this is a pain for C, but it's generally a once off pain.
>
>
>>
>>> This suggests that static exceptions as currently specified cannot
>>> replace dynamic exceptions in all cases.
>>>
>>
>> Under P1095, one would not want to replace them. There are valid use
>> cases for dynamic exceptions, and you should use them where they are the
>> best solution to the problem. Under P1095, static exceptions are a
>> guaranteed subset of dynamic exceptions, and dynamic exceptions of STL
>> exceptions types map one-one onto static exceptions, but not user defined
>> exception types (unless they tell std::error how to map them).
>>
>>
> My interpretation of P0709 was that dynamic exceptions become legacy. What
> uses remain for dynamic ones?
> Is it function pointers etc. as I suggested?
>
P0709 is more radical than P1095, and it may be suggesting exactly that.
P1095 sees some valid uses remaining for dynamic exceptions e.g. throwing
unknown payload, or where failure is exceedingly rare and you want absolute
zero overhead for the successful code path. P1095 holds that static
exceptions *can* replace dynamic exceptions entirely, but recognises they
involve more mental load and it's possibly more work to implement for the
programmer, and some programmers won't want to bother, especially where
they are maintaining legacy codebases.
> That's fine under P1095. In fact, void Z() throws(void); is legal under
>> P1095. It may even be useful :).
>>
>
> That begs me to ask. Useful, how so?
>
This is a slight joke in Outcome where outcome::result<void, void> is
totally legal. Some want to ban it. But I can see no compelling reason why
it's a dangerous thing, I just can't see much use for it. And I'm minded to
not ban something which is useless unless it's obviously dangerous, or a
bad idea, or has some legitimate problem other than just that people don't
like how it looks.
The joke is, of course, that outcome::result<void, void> is identical in
every way to returning a bool, just a pointlessly elaborate way of doing so.
>
>> I was actually thinking of the overhead of the instructions to check the
> error flag etc in each function / calling convention.
> I thought an explicit aim of zero overhead exceptions was to eliminate
> cases where C++ exceptions needed to switched off.
> Maybe is a case of YMMV.
>
I think people who disable exceptions rarely do so since table based EH for
performance reasons.
I think the overwhelming majority disable them because of the lack of value
add proposition i.e. the value they deliver versus their cost isn't there
for perhaps half of all C++ users.
That cost is not performance. It's in maintenance costs, training costs,
hiring costs, audit costs, testing costs etc.
>
>
>>
>> Some people like using TLS to store out of band state. e.g.
>> https://zajo.github.io/boost-noexcept/
>>
>>
> Good.
>
Well, actually no. TLS is a design mistake the committee would love to undo
now we have hindsight. But ship has sailed :(
> Bugs come in classes of severity. Assertion failures are logic errors.
>> They should not be recoverable. They should mean termination, except for
>> the sole case of when run within a testing framework.
>>
>>
>> I strongly disagree. One bug should not bring down a fault tolerant
> system.
>
*Recoverable* bugs should not bring down a fault tolerant system.
*Unrecoverable* bugs should halt [and restart] every fault tolerant system.
> The task related to that bug is generally not recoverable but the rest of
> the system is.
> Likewise, I would much rather keep my assertions enabled and detected in
> production (except where peformancei is factor) rather than risk entering
> into UB.
> After all if a pre-condition you've asserted is not met in principle your
> function could do anything including format your hard drive.
> If you allow that then people might be persuaded to add unnecessary
> defensive code to the function to prevent it.
>
Logic error means that known program state has been lost. Your program
state is now into permanent undefined and unspecified behaviour, and cannot
be rescued. The only sane choice is immediate termination.
My only exception for this is in test frameworks, and thankfully at long
last WG21 is mostly leaning in my direction on this. The proposal is we
mark logic error type failure as unrecoverable failure, retarget the
standard around such things being unrecoverable (P0709 proposes OOM amongst
many other things), and provide a really awkward to use hook for test
frameworks to escape the "unstoppable" termination.
I recognise how unpopular this is outside of WG21. But it's received
unanimous or near-unanimous votes at WG21 so far. Unless something majorly
changes, I think this change is coming to future C++ standards. People who
really hate it can stick to an older language standard. And of course
toolchains may provide switches to force on the old behaviour in the latest
C++ standard.
Niall
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1b96821c-4429-476c-ac3f-471cde2e8294%40isocpp.org.
------=_Part_832_1414749272.1544797175276
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div><br></div><blockquote class=3D"gmail_quote" style=3D"margin:0;marg=
in-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"=
><div></div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left=
:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr">*Does =
the compiler need to create two versions of Y() internally?*<br></div></blo=
ckquote><div><br></div><div>In P1095 Y() would cause a conversion from valu=
e-based throw to type-based throw. The catch of std::error would work as ex=
pected.</div><div></div></div></blockquote><div><br></div><div>I see so thi=
s would be implemented via the exception handler code in the C++ runtime us=
ed?</div></div></blockquote><div><br></div><div>It would literally be as if=
"throw" to rethrow the exception. Dynamic exceptions already get=
created and thrown by the C++ runtime. So it would be exactly as now, exac=
t same overhead and cost.</div><div>=C2=A0</div><blockquote class=3D"gmail_=
quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;pa=
dding-left: 1ex;"><div dir=3D"ltr"><div>The conversion wouldn't actuall=
y happen in Y() itself it would happen only if the exception handler found =
a suitable</div><div>try catch block in Y() that required it.</div></div></=
blockquote><div><br></div><div>Under P1095, if a static exception throw enc=
ounters a dynamically throwing function during stack unwind, it gets conver=
ted immediately to a dynamic exception throw. If all the code is visible to=
the compiler and there is a static catch, its optimiser may elide the conv=
ersion under the as-if rule. But that's up to the implementation.</div>=
<div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margi=
n-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"l=
tr"><div>If that was in the next frame up and that frame was a "throws=
()" function using the new ABI it would just use the cheaper value</di=
v><div>based exception instead.</div></div></blockquote><div><br></div><div=
>That's a possible optimisation, but not required.</div><div>=C2=A0</di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><br></=
div><div>I think I've confused myself here as we are using the return c=
hannel rather than the handler in the C++ runtime for static exceptions.</d=
iv><div>So somehow Y() must detect the error flag even though it is using t=
he old calling convention?<br></div></div></blockquote><div><br></div><div>=
Y() knows it is calling Z() which is marked throws. So it knows it must che=
ck the return from Z().</div><div>=C2=A0</div><blockquote class=3D"gmail_qu=
ote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padd=
ing-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote class=3D"gmail_quot=
e" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-l=
eft:1ex"><div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin=
:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=
=3D"ltr"><br>This may seem like a false concern but consider callbacks.<br>=
</div></blockquote><div><br></div><div>One of WG14's big concerns with =
the proposal is that fails(T) functions could not be used in a function poi=
nter, which could generate surprise.</div><div><br></div><div>Me personally=
I felt that if you mismatch a fails(T) function with a non-fails(T) functi=
on pointer, that's 100% on you for doing UB. But others on WG14 propose=
d an offset workaround whereby every fails(T) function would gain a preambl=
e, and if mismatched/called wrong and the function failed, it would trigger=
a contract failure/call abort(). This is effectively noexcept, but for C, =
and I can't say I like it any better.</div><div>=C2=A0</div></div></blo=
ckquote><div>I think I see their point. UB is harder to debug than an expli=
cit failure.</div></div></blockquote><div><br></div><div>Personally I'd=
prefer a contract failure if you pass mismatched pointers i.e. it always a=
borts on mismatch. But that's not the majority opinion, currently.</div=
><div>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;marg=
in-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"=
ltr"><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;=
border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><br></di=
v><div>Me personally I'd make noexcept and throws part of the type of a=
function pointer, but not overloadable on that.</div><div><br></div></div>=
</blockquote><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-lef=
t:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>=
</div><div>But as I mentioned, others on WG14 feel it's workable to be =
more relaxed.</div></div></blockquote><div>=C2=A0</div><div><div>I think yo=
u are right if you are in control of the implementation. However, If the ca=
llback is being provided to a third party library you don't have that o=
ption.</div></div></div></blockquote><div><br></div><div>Me personally I th=
ink you can always wrap a fails function with a non-fails one, or vice vers=
a. So you simply wrap the callback and pass the wrapper. I agree this is a =
pain for C, but it's generally a once off pain.</div><div>=C2=A0</div><=
blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bord=
er-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div><div></di=
v>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-le=
ft:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div=
></div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8e=
x;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><br>This su=
ggests that static exceptions as currently specified cannot <br>replace dyn=
amic exceptions in all cases.<br></div></blockquote><div><br></div><div>Und=
er P1095, one would not want to replace them. There are valid use cases for=
dynamic exceptions, and you should use them where they are the best soluti=
on to the problem. Under P1095, static exceptions are a guaranteed subset o=
f dynamic exceptions, and dynamic exceptions of STL exceptions types map on=
e-one onto static exceptions, but not user defined exception types (unless =
they tell std::error how to map them).</div><div>=C2=A0</div></div></blockq=
uote><div>My interpretation of P0709 was that dynamic exceptions become leg=
acy. What uses remain for dynamic ones?<br></div><div>Is it function pointe=
rs etc. as I suggested?<br></div></div></blockquote><div><br></div><div>P07=
09 is more radical than P1095, and it may be suggesting exactly that. P1095=
sees some valid uses remaining for dynamic exceptions e.g. throwing unknow=
n payload, or where failure is exceedingly rare and you want absolute zero =
overhead for the successful code path. P1095 holds that static exceptions <=
i>can</i>=C2=A0replace dynamic exceptions entirely, but recognises they inv=
olve more mental load and it's possibly more work to implement for the =
programmer, and some programmers won't want to bother, especially where=
they are maintaining legacy codebases.</div><div>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px=
#ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc=
solid;padding-left:1ex"><div dir=3D"ltr"><div>That's fine under P1095.=
In fact, void Z() throws(void); is legal under P1095. It may even be usefu=
l :).</div><div></div></div></blockquote><div><br></div><div>That begs me t=
o ask. Useful, how so? <br></div></div></blockquote><div><br></div><div>Thi=
s is a slight joke in Outcome where outcome::result<void, void> is to=
tally legal. Some want to ban it. But I can see no compelling reason why it=
's a dangerous thing, I just can't see much use for it. And I'm=
minded to not ban something which is useless unless it's obviously dan=
gerous, or a bad idea, or has some legitimate problem other than just that =
people don't like how it looks.</div><div><br></div><div>The joke is, o=
f course, that outcome::result<void, void> is identical in every way =
to returning a bool, just a pointlessly elaborate way of doing so.</div><di=
v>=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-l=
eft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"=
><div></div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left=
:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><=
br></div></div></blockquote><div><div>I was actually thinking of the overhe=
ad of the instructions to check the error flag etc in each function / calli=
ng convention.</div><div>I thought an explicit aim of zero overhead excepti=
ons was to eliminate cases where C++ exceptions needed to switched off.</di=
v><div>Maybe is a case of YMMV.<br></div></div></div></blockquote><div><br>=
</div><div>I think people who disable exceptions rarely do so since table b=
ased EH for performance reasons.</div><div><br></div><div>I think the overw=
helming majority disable them because of the lack of value add proposition =
i.e. the value they deliver versus their cost isn't there for perhaps h=
alf of all C++ users.</div><div><br></div><div>That cost is not performance=
.. It's in maintenance costs, training costs, hiring costs, audit costs,=
testing costs etc.</div><div>=C2=A0</div><blockquote class=3D"gmail_quote"=
style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-=
left: 1ex;"><div dir=3D"ltr"><div>=C2=A0</div><blockquote class=3D"gmail_qu=
ote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding=
-left:1ex"><div dir=3D"ltr"><br><div>Some people like using TLS to store ou=
t of band state. e.g.=C2=A0<a href=3D"https://zajo.github.io/boost-noexcept=
/" rel=3D"nofollow" target=3D"_blank" onmousedown=3D"this.href=3D'https=
://www.google.com/url?q\x3dhttps%3A%2F%2Fzajo.github.io%2Fboost-noexcept%2F=
\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ';=
return true;" onclick=3D"this.href=3D'https://www.google.com/url?q\x3dh=
ttps%3A%2F%2Fzajo.github.io%2Fboost-noexcept%2F\x26sa\x3dD\x26sntz\x3d1\x26=
usg\x3dAFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ';return true;">https://zajo.g=
ithub.io/<wbr>boost-noexcept/</a></div><div>=C2=A0</div></div></blockquote>=
<div>Good. <br></div></div></blockquote><div><br></div><div>Well, actually =
no. TLS is a design mistake the committee would love to undo now we have hi=
ndsight. But ship has sailed :(</div><div>=C2=A0</div><blockquote class=3D"=
gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc so=
lid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><blockquote class=3D"gm=
ail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;p=
adding-left:1ex"><div dir=3D"ltr"><div>Bugs come in classes of severity. As=
sertion failures are logic errors. They should not be recoverable. They sho=
uld mean termination, except for the sole case of when run within a testing=
framework.</div><div><br></div><div><br></div></div></blockquote><div>I st=
rongly disagree. One bug should not bring down a fault tolerant system.</di=
v></div></blockquote><div><br></div><div><i>Recoverable</i>=C2=A0bugs shoul=
d not bring down a fault tolerant system.</div><div><br></div><div><i>Unrec=
overable</i>=C2=A0bugs should halt [and restart] every fault tolerant syste=
m.</div><div>=C2=A0<br></div><blockquote class=3D"gmail_quote" style=3D"mar=
gin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><=
div dir=3D"ltr"><div> The task related to that bug is generally not recover=
able but the rest of the system is.</div><div></div><div>Likewise, I would =
much rather keep my assertions enabled and detected in production (except w=
here peformancei is factor) rather than risk entering into UB.</div><div>Af=
ter all if a pre-condition you've asserted is not met in principle your=
function could do anything including format your hard drive.</div><div>If =
you allow that then people might be persuaded to add unnecessary defensive =
code to the function to prevent it.</div></div></blockquote><div><br></div>=
<div>Logic error means that known program state has been lost. Your program=
state is now into permanent undefined and unspecified behaviour, and canno=
t be rescued. The only sane choice is immediate termination.</div><div><br>=
</div><div>My only exception for this is in test frameworks, and thankfully=
at long last WG21 is mostly leaning in my direction on this. The proposal =
is we mark logic error type failure as unrecoverable failure, retarget the =
standard around such things being unrecoverable (P0709 proposes OOM amongst=
many other things), and provide a really awkward to use hook for test fram=
eworks to escape the "unstoppable" termination.</div><div><br></d=
iv><div>I recognise how unpopular this is outside of WG21. But it's rec=
eived unanimous or near-unanimous votes at WG21 so far. Unless something ma=
jorly changes, I think this change is coming to future C++ standards. Peopl=
e who really hate it can stick to an older language standard. And of course=
toolchains may provide switches to force on the old behaviour in the lates=
t C++ standard.</div><div><br></div><div>Niall</div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/1b96821c-4429-476c-ac3f-471cde2e8294%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/1b96821c-4429-476c-ac3f-471cde2e8294=
%40isocpp.org</a>.<br />
------=_Part_832_1414749272.1544797175276--
------=_Part_831_1648456076.1544797175275--
.
Author: tortoise741@gmail.com
Date: Fri, 14 Dec 2018 08:09:53 -0800 (PST)
Raw View
------=_Part_237_589731267.1544803794052
Content-Type: multipart/alternative;
boundary="----=_Part_238_1175351764.1544803794053"
------=_Part_238_1175351764.1544803794053
Content-Type: text/plain; charset="UTF-8"
On Friday, 14 December 2018 14:19:35 UTC, Niall Douglas wrote:
>
>
>> In P1095 Y() would cause a conversion from value-based throw to
>>> type-based throw. The catch of std::error would work as expected.
>>>
>>
>> I see so this would be implemented via the exception handler code in the
>> C++ runtime used?
>>
>
> It would literally be as if "throw" to rethrow the exception. Dynamic
> exceptions already get created and thrown by the C++ runtime. So it would
> be exactly as now, exact same overhead and cost.
>
>
>>
>> I think I've confused myself here as we are using the return channel
>> rather than the handler in the C++ runtime for static exceptions.
>> So somehow Y() must detect the error flag even though it is using the old
>> calling convention?
>>
>
> Y() knows it is calling Z() which is marked throws. So it knows it must
> check the return from Z().
>
I think the source of my confusion is who is responsible for stack
unwinding.
With dynamic exceptions throw invokes the handler in the C++ runtime.
With static exceptions throw would instead become _Fail() using the return
channel.
A implementation of Y() is free to invoke Z() throws.
If it doesn't understand the new calling convention it will silently work
until there is an exception.
It is then ill-defined (almost certainly bad) behaviour. Y() will act as if
the function has returned when the return value is actually a std::error.
If a compiler that knows about the new calling convention compiles Y() it
must surely compile Y() with the new calling convention to avoid this.
So a declaration of Y() must already be using the new calling convention.
Thus "Y() throws" throws can't just mean using the new calling convention.
It must mean using the new calling convention AND throwing an exception
using it.
So the only purpose declaring "throws" has is so that the compiler can
object if a function higher up is noexcept() and tries to use it.
Who converts the static exception to a dynamic one?
Doesn't something need to add a post-amble "if (error flag) throw(dyn)" to
the return from to Z()?
If Y() is also declared throws() that post-amble becomes "if (error flag)
throw(static)" i.e _Fail()
But the post-amble must be required whether or not Z() is declared throws
as Y() must assume Z()'s 'returns' channel supports static exceptions.
Does this not add both CPU instruction size and execution overhead?
(leaving a use case for -fno-exceptions ?)
Back to an earlier point if Y() is in a library compiled with the old ABI
or calling Z() via a callback the post-amble cannot exist we get UB.
We need to recompile everything to be safe.
Further we need to label the new ABI as a major revision incompatible with
the old one (recorded in the ELF object) and have the linker detect this as
an error.
(maybe new ABI code can invoke old ABI code but definitely not the reverse).
I think my mistake was assuming this change could be ABI compatible because
it works for the non-exceptional case.
>
>
>> I was actually thinking of the overhead of the instructions to check the
>> error flag etc in each function / calling convention.
>> I thought an explicit aim of zero overhead exceptions was to eliminate
>> cases where C++ exceptions needed to switched off.
>> Maybe is a case of YMMV.
>>
>
> I think people who disable exceptions rarely do so since table based EH
> for performance reasons.
>
> I think the overwhelming majority disable them because of the lack of
> value add proposition i.e. the value they deliver versus their cost isn't
> there for perhaps half of all C++ users.
>
> That cost is not performance. It's in maintenance costs, training costs,
> hiring costs, audit costs, testing costs etc.
>
>
Okay so (in your eyes at least) static exceptions are purely a performance
improvement?
Can we ever get people to stop using -fno-exceptions if performance and
bloat isn't the reason?
If that is not a goal then perhaps the standard should embrace
-fno-exceptions and define exceptions as opt-in?
>>
>>>
>>> Some people like using TLS to store out of band state. e.g.
>>> https://zajo.github.io/boost-noexcept/
>>> <https://www.google.com/url?q=https%3A%2F%2Fzajo.github.io%2Fboost-noexcept%2F&sa=D&sntz=1&usg=AFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ>
>>>
>>>
>> Good.
>>
>
> Well, actually no. TLS is a design mistake the committee would love to
> undo now we have hindsight. But ship has sailed :(
>
What is wrong with TLS? Do you mean specifically relating to errors? how
support was specified for TLS in the standard? or in general?
> Bugs come in classes of severity. Assertion failures are logic errors.
>>> They should not be recoverable. They should mean termination, except for
>>> the sole case of when run within a testing framework.
>>>
>>>
>>> I strongly disagree. One bug should not bring down a fault tolerant
>> system.
>>
>
> *Recoverable* bugs should not bring down a fault tolerant system.
>
> *Unrecoverable* bugs should halt [and restart] every fault tolerant
> system.
>
Deciding whether an error is recoverable or not is a non-local decision.
Is there even a way to rigorously define what is and isn't recoverable?
>
>
>> The task related to that bug is generally not recoverable but the rest of
>> the system is.
>> Likewise, I would much rather keep my assertions enabled and detected in
>> production (except where peformancei is factor) rather than risk entering
>> into UB.
>> After all if a pre-condition you've asserted is not met in principle your
>> function could do anything including format your hard drive.
>> If you allow that then people might be persuaded to add unnecessary
>> defensive code to the function to prevent it.
>>
>
> Logic error means that known program state has been lost. Your program
> state is now into permanent undefined and unspecified behaviour, and cannot
> be rescued. The only sane choice is immediate termination.
>
> My only exception for this is in test frameworks, and thankfully at long
> last WG21 is mostly leaning in my direction on this. The proposal is we
> mark logic error type failure as unrecoverable failure, retarget the
> standard around such things being unrecoverable (P0709 proposes OOM amongst
> many other things), and provide a really awkward to use hook for test
> frameworks to escape the "unstoppable" termination.
>
> I recognise how unpopular this is outside of WG21. But it's received
> unanimous or near-unanimous votes at WG21 so far. Unless something majorly
> changes, I think this change is coming to future C++ standards. People who
> really hate it can stick to an older language standard. And of course
> toolchains may provide switches to force on the old behaviour in the latest
> C++ standard.
>
> Niall
>
Consider the case of Erlang. Fault tolerant systems can be implemented in
Erlang which has a MxN runtime. An Erlang 'process' is a like coroutine.
The failure of a process does not kill the runtime just that process.
If you forbid C++ from doing something similar then the only way to get
that kind of behaviour is to have each and every routine spawned as a
separate process or worse an entire Docker container.
That is simply not necessary when you have designed with good isolation.
You also could not write a runtime for Erlang or Go in C++.
Also in the case of a contract. If you catch a pre-condition in a requires
clauses before the function is entered you have detected a bug in the
calling code. You haven't entered into undefined behaviour for the callee
if you throw at that point. You only enter into undefined behaviour if you
enter the called function or if the caller is not allowed to throw
exceptions.
Isolation means that state in one area is independent of another. Yes,
unless you have processes you have weaker isolation and there is a risk
that an undefined state in one area can leak into another but I think it is
as dangerous to assume it must always as it is to assume it will never. You
can limit the damage by ceasing to run the offending task, coroutine or
thread. This doesn't have to extend to the process.
A task, coroutine or thread in a dead or undefined state may or may not
result in a process in a dead or undefined state. std::terminate() is built
for a single thread of execution world. It is too blunt for the wider one.
Regards,
Bruce.
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2a343663-481c-4341-b971-2e7ba8f0a85a%40isocpp.org.
------=_Part_238_1175351764.1544803794053
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><br>On Friday, 14 December 2018 14:19:35 UTC, Niall Dougla=
s wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: =
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><blo=
ckquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-le=
ft:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><br></div><blockq=
uote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:=
1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div></div><div>In P1095 =
Y() would cause a conversion from value-based throw to type-based throw. Th=
e catch of std::error would work as expected.</div><div></div></div></block=
quote><div><br></div><div>I see so this would be implemented via the except=
ion handler code in the C++ runtime used?</div></div></blockquote><div><br>=
</div><div>It would literally be as if "throw" to rethrow the exc=
eption. Dynamic exceptions already get created and thrown by the C++ runtim=
e. So it would be exactly as now, exact same overhead and cost.</div><div>=
=C2=A0</div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left=
:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div><=
br></div><div>I think I've confused myself here as we are using the ret=
urn channel rather than the handler in the C++ runtime for static exception=
s.</div><div>So somehow Y() must detect the error flag even though it is us=
ing the old calling convention?<br></div></div></blockquote><div><br></div>=
<div>Y() knows it is calling Z() which is marked throws. So it knows it mus=
t check the return from Z().</div><div></div></div></blockquote><div><br></=
div><div>I think the source of my confusion is who is responsible for stack=
unwinding.</div><div>With dynamic exceptions throw invokes the handler in =
the C++ runtime.</div><div>With static exceptions throw would instead becom=
e _Fail() using the return channel.</div><div><br></div><div>A implementati=
on of Y() is free to invoke Z() throws.</div><div>If it doesn't underst=
and the new calling convention it will silently work until there is an exce=
ption.</div><div></div><div>It is then ill-defined (almost certainly bad) b=
ehaviour. Y() will act as if the function has returned when the return valu=
e is actually a std::error.</div><div></div><div>If a compiler that knows a=
bout the new calling convention compiles Y() it must surely compile Y() wit=
h the new calling convention to avoid this.<br></div><div>So a declaration =
of Y() must already be using the new calling convention.<br></div><div>Thus=
"Y() throws" throws can't just mean using the new calling co=
nvention. It must mean using the new calling convention AND throwing an exc=
eption using it.</div><div>So the only purpose declaring "throws"=
has is so that the compiler can object if a function higher up is noexcept=
() and tries to use it.<br></div><div><br></div><div>Who converts the stati=
c exception to a dynamic one? <br></div><div>Doesn't something need to =
add a post-amble "if (error flag) throw(dyn)" to the return from =
to Z()?</div><div>If Y() is also declared throws() that post-amble becomes =
"if (error flag) throw(static)" i.e _Fail()<br></div><div></div><=
div><br></div><div>But the post-amble must be required whether or not Z() i=
s declared=20
throws as Y() must assume Z()'s 'returns' channel supports stat=
ic exceptions.</div><div>Does this not add both CPU instruction size and ex=
ecution overhead? (leaving a use case for -fno-exceptions ?)<br></div><div>=
<br></div><div></div><div>Back to an earlier point if Y() is in a library c=
ompiled with the old ABI or calling Z() via a callback the post-amble canno=
t exist we get UB.</div><div>We need to recompile everything to be safe. <b=
r></div><div>Further we need to label the new ABI as a major revision incom=
patible with the old one (recorded in the ELF object) and have the linker d=
etect this as an error.</div><div>(maybe new ABI code can invoke old ABI co=
de but definitely not the reverse).<br></div><div>I think my mistake was as=
suming this change could be ABI compatible because it works for the non-exc=
eptional case.<br></div>=C2=A0<blockquote class=3D"gmail_quote" style=3D"ma=
rgin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">=
<div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin:0;margin=
-left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><=
br><div><div>I was actually thinking of the overhead of the instructions to=
check the error flag etc in each function / calling convention.</div><div>=
I thought an explicit aim of zero overhead exceptions was to eliminate case=
s where C++ exceptions needed to switched off.</div><div>Maybe is a case of=
YMMV.<br></div></div></div></blockquote><div><br></div><div>I think people=
who disable exceptions rarely do so since table based EH for performance r=
easons.</div><div><br></div><div>I think the overwhelming majority disable =
them because of the lack of value add proposition i.e. the value they deliv=
er versus their cost isn't there for perhaps half of all C++ users.</di=
v><div><br></div><div>That cost is not performance. It's in maintenance=
costs, training costs, hiring costs, audit costs, testing costs etc.</div>=
<div>=C2=A0</div></div></blockquote><div>Okay so (in your eyes at least) st=
atic exceptions are purely a performance improvement? <br></div><div>Can we=
ever get people to stop using -fno-exceptions if performance and bloat isn=
't the reason?</div><div>If that is not a goal then perhaps the standar=
d should embrace -fno-exceptions and define exceptions as opt-in?</div><div=
><br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left=
: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><b=
lockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-=
left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>=C2=A0</div><bl=
ockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-l=
eft:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><br><div>Some people =
like using TLS to store out of band state. e.g.=C2=A0<a href=3D"https://www=
..google.com/url?q=3Dhttps%3A%2F%2Fzajo.github.io%2Fboost-noexcept%2F&sa=
=3DD&sntz=3D1&usg=3DAFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ" rel=3D"nofo=
llow" target=3D"_blank" onmousedown=3D"this.href=3D'https://www.google.=
com/url?q\x3dhttps%3A%2F%2Fzajo.github.io%2Fboost-noexcept%2F\x26sa\x3dD\x2=
6sntz\x3d1\x26usg\x3dAFQjCNH6KbyLzahb2bG2jxeNkxIpMDWjDQ';return true;" =
onclick=3D"this.href=3D'https://www.google.com/url?q\x3dhttps%3A%2F%2Fz=
ajo.github.io%2Fboost-noexcept%2F\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNH=
6KbyLzahb2bG2jxeNkxIpMDWjDQ';return true;">https://zajo.github.io/<wbr>=
boost-noexcept/</a></div><div>=C2=A0</div></div></blockquote><div>Good. <br=
></div></div></blockquote><div><br></div><div>Well, actually no. TLS is a d=
esign mistake the committee would love to undo now we have hindsight. But s=
hip has sailed :(</div><div></div></div></blockquote><div><br></div><div>Wh=
at is wrong with TLS? Do you mean specifically relating to errors? how supp=
ort was specified for TLS in the standard? or in general?<br></div><div><br=
></div><div>=C2=A0 <br></div><blockquote class=3D"gmail_quote" style=3D"mar=
gin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><=
div dir=3D"ltr"><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-=
left:0.8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><d=
iv></div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.=
8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div>Bugs=
come in classes of severity. Assertion failures are logic errors. They sho=
uld not be recoverable. They should mean termination, except for the sole c=
ase of when run within a testing framework.</div><div><br></div><div><br></=
div></div></blockquote><div>I strongly disagree. One bug should not bring d=
own a fault tolerant system.</div></div></blockquote><div><br></div><div><i=
>Recoverable</i>=C2=A0bugs should not bring down a fault tolerant system.</=
div><div><br></div><div><i>Unrecoverable</i>=C2=A0bugs should halt [and res=
tart] every fault tolerant system.</div><div></div></div></blockquote><div>=
<br></div><div><div>Deciding whether an error is recoverable or not is a no=
n-local decision.</div><div></div><div>Is there even a way to rigorously de=
fine what is and isn't recoverable?<br></div><div></div>=C2=A0</div><bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border=
-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div>=C2=A0<br><=
/div><blockquote class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;=
border-left:1px #ccc solid;padding-left:1ex"><div dir=3D"ltr"><div> The tas=
k related to that bug is generally not recoverable but the rest of the syst=
em is.</div><div></div><div>Likewise, I would much rather keep my assertion=
s enabled and detected in production (except where peformancei is factor) r=
ather than risk entering into UB.</div><div>After all if a pre-condition yo=
u've asserted is not met in principle your function could do anything i=
ncluding format your hard drive.</div><div>If you allow that then people mi=
ght be persuaded to add unnecessary defensive code to the function to preve=
nt it.</div></div></blockquote><div><br></div><div>Logic error means that k=
nown program state has been lost. Your program state is now into permanent =
undefined and unspecified behaviour, and cannot be rescued. The only sane c=
hoice is immediate termination.</div><div><br></div><div>My only exception =
for this is in test frameworks, and thankfully at long last WG21 is mostly =
leaning in my direction on this. The proposal is we mark logic error type f=
ailure as unrecoverable failure, retarget the standard around such things b=
eing unrecoverable (P0709 proposes OOM amongst many other things), and prov=
ide a really awkward to use hook for test frameworks to escape the "un=
stoppable" termination.</div><div><br></div><div>I recognise how unpop=
ular this is outside of WG21. But it's received unanimous or near-unani=
mous votes at WG21 so far. Unless something majorly changes, I think this c=
hange is coming to future C++ standards. People who really hate it can stic=
k to an older language standard. And of course toolchains may provide switc=
hes to force on the old behaviour in the latest C++ standard.</div><div><br=
></div><div>Niall</div></div></blockquote><div><br></div><div>Consider the =
case of Erlang. Fault tolerant systems can be implemented in Erlang which h=
as a MxN runtime. An Erlang 'process' is a like coroutine. The fail=
ure of a process does not kill the runtime just that process.</div><div>If =
you forbid C++ from doing something similar then the only way to get that k=
ind of behaviour is to have each and every routine spawned as a separate pr=
ocess or worse an entire Docker container.</div><div>That is simply not nec=
essary when you have designed with good isolation.</div><div>You also could=
not write a runtime for Erlang or Go in C++.<br></div><div><br></div><div>=
Also in the case of a contract. If you catch a pre-condition in a requires =
clauses before the function is entered you have detected a bug in the calli=
ng code. You haven't entered into undefined behaviour for the callee</d=
iv><div>if you throw at that point. You only enter into undefined behaviour=
if you enter the called function or if the caller is not allowed to throw =
exceptions.</div><div><br></div><div>Isolation means that state in one area=
is independent of another. Yes, unless you have processes you have weaker =
isolation and there is a risk that an undefined state in one area can leak =
into another but I think it is</div><div>as dangerous to assume it must alw=
ays as it is to assume it will never. You can limit the damage by ceasing t=
o run the offending task, coroutine or thread. This doesn't have to ext=
end to the process. <br></div><div>A task, coroutine or thread in a dead or=
undefined state may or may not result in a process in a dead or undefined =
state. std::terminate() is built for a single thread of execution world. It=
is too blunt for the wider one.<br></div><div><br></div><div></div><div>Re=
gards,</div><div><br></div><div>Bruce.<br></div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/2a343663-481c-4341-b971-2e7ba8f0a85a%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/2a343663-481c-4341-b971-2e7ba8f0a85a=
%40isocpp.org</a>.<br />
------=_Part_238_1175351764.1544803794053--
------=_Part_237_589731267.1544803794052--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 14 Dec 2018 08:53:20 -0800 (PST)
Raw View
------=_Part_784_1466678138.1544806401071
Content-Type: multipart/alternative;
boundary="----=_Part_785_1638667995.1544806401071"
------=_Part_785_1638667995.1544806401071
Content-Type: text/plain; charset="UTF-8"
On Thursday, December 13, 2018 at 12:43:39 PM UTC-5, Bruce Adams wrote:
>
> I've finally had a chance to review the [p0709r0](
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf)
> (and the related work on possible implementations).
>
> I have a few questions/suggestions.
> After doing some background research and rubberducking this has come out
> as rather a long post.
> Hopefully some of it useful.
>
>
> 1. how do we handle propagating exceptions?
>
I thought P0709 was fairly clear on this, but perhaps not.
Static exceptions are not triggered by throwing a `std::error` type. Their
behavior is triggered when a function declared with `throws` *emits* an
exception. If such a function emits an exception of type `std::error`, then
that is the static exception which is propagated up to the next layer. If
such a function emits an exception of any other type, it is wrapped in an
`exception_ptr` and stored in a `std::error` object, which is propagated up
to the next layer.
If static exception propagation passes through a function which is not
declared `throws` (or `noexcept` obviously), then the `std::error` being
propagated will be converted into a dynamic exception. If the current
`std::error` contains an `exception_ptr`, then this exception is unpacked
and transmitted as if by `rethrow_exception`. Otherwise, `std::error` is
transmitted via the dynamic exception mechanism as a regular `std::error`
object (just like throwing any other type).
And, as Niall mentioned, you can't overload on `throws` anymore than you
can overload on return values (though see below for the exception).
`throws` is part of the signature, but it's part of the signature in (more
or less) the same way as the return value.
*So is there still a place for dynamic exceptions in the future or should
> they be entirely deprecated?*
>
Part of the goal of Contracts is to get rid of an entire class of
"exceptions": basically, `logic_error` and most things derived from it. And
P0709 states a very explicit goal of making allocation failures not throw.
Once you remove those two things, most standard library functions will
become de-facto `noexcept` (and thus we can make them de-jure `noexcept`).
How to deal with algorithms (that is, what kind of exceptions to propagate
from callbacks) is a different question. However, thanks to concepts, we do
have the possibility of overloading on whether the given callable is
`throws` or not.
But outside of template mechanism yes, whether a callback uses static or
dynamic exceptions is a part of that callback system's interface, just like
what object the callback is expected to return or what parameters it takes.
But since the two mechanisms are compatible for the most part, it's not a
real problem.
The eventual goal is that everyone can shift towards `throws`, and dynamic
exceptions will become legacy C++ stuff.
2. What are the semantics for void functions?
>
The semantics of a `void`-`throws` function is no different from the
semantics of an `int`-`throws` function. The function either succeeds (and
potentially generates a value) or emits an exception of type `std:error`
via static exception propagation.
How exactly the compiler implements this is up to it. Which brings us to:
> E.g.
>
> void Z() throws {
> }
>
> I'm presuming this is legal.
> The return channel was not previously used but now will be.
> Assuming clever compilers could elide it in this case, this may add some
> (small) overhead.
>
> The implementation of:
>
> void Y() {
> // do stuff 1
> Z();
> // do stuff 2
> }
>
> Has to silently become:
>
> void Y() {
> // do stuff 1
>
> retVal = Z();
>
> // save retVal and error flag if required
>
> // do stuff 2
>
> // put retVal into return register or similar
> // set error flag
> }
>
>
> 3. ABI overhead is lower but not zero
>
The goal as spelled out in P0709 is not to be *free*. It is to be no more
expensive than what you could achieve yourself.
If you have a function that already deals in a simple, integer error codes,
you should be able to rewrite your code to throw that error code via a
static exception and receive it in the same place you had before (replacing
the explicit error returns between the source and destination with static
exception propagation). And if you do so, your code should have the same
performance as if you had written the integer error code propagation
yourself, if not *better* performance.
That's what "zero-overhead" means in this context.
Take your `void Z() throws` function. The error code equivalent would be:
error_code Z() {
...
}
void Y() {
// do stuff 1
auto retVal = Z();
// save retVal and error flag if required
// do stuff 2
// put retVal into return register or similar
// set error flag
}
See? The static exception code inserted is fundamentally the same. The
"register overhead" was there in both versions.
4. Can we do better?
>
"Better" in what regard?
Are there functions whose error state is just one bit? Sure. But there are
a lot more functions that can error in multiple ways, and being able to
tell how they errored out kind of matters.
Being able to transmit a dynamic exception *through* code that uses static
exceptions is kind of important. This backwards compatibility will make it
easier to adopt static exception handling. Especially as removing most
exceptions from the standard library will require years of deprecation
before they can be fully culled, during which time static and dynamic
exceptions will have to interoperate.
Also, using a "well defined location on the stack" is problematic for cache
reasons. That location would have to be somewhere at the bottom of the
stack for that location to be easily accessible. But the bottom of the
stack (pre-main) probably doesn't get accessed very much, so it may not be
in the cache. So every time you emit a static exception, you get a cache
miss. By using a register (or rather, by allowing the compiler to be *able*
to use a register), you bypass the cache entirely.
5. Has anyone considered something like a "conditions system" for C++?
>
Something like that would require a complete, page-1 redesign of the basic
aspects of error handling. Static exceptions are a profound idea because
they mostly look and behave like dynamic exceptions, only with better
efficiency and a tolerable degree of virality (the need to mark functions
with `throws`). Static exceptions are not a brand new thing; they're the
same old thing that works in a well-understood way, only better.
For example, you say that a condition handler can ignore a failed function.
What exactly does that *mean* in terms of the C++ execution model? As the
language currently stands, a function can either succeed or fail; what does
it mean to be ignored? Does the function which calls it get to know about
this "ignoring", or do I have to explicitly set up some communication line?
What happens to the return value object from such an "ignored" function?
I'm not saying that this is necessarily a bad idea. But it is a *new idea*,
one which is far less fully-formed within the context of C++ compared to
exceptions. If an exception, static or dynamic, is thrown in a constructor,
we know what happens. We don't have to come up with a definition of what
that means for the object. And if you're going to add an error mechanism to
the language, it *needs* to have an answer for what it means for an object
to fail to be constructed.
Exceptions have an answer already, so an improved exception mechanism
doesn't need to provide one.
If you're going to offer an alternative design, I advise you to take a very
careful look at the goals outlined in P0709, section 3.2.
6. Contracts and OOM errors can be exceptional
>
The design for OOM handling in P0709R2 is perfectly capable of forcing
dynamic exception usage on allocation failure. However, contract failure as
exceptions? Absolutely not.
If you deference a null pointer, your code has done something so wrong that
it clearly must die right now. The same idea goes for contract failures. A
contract represents a promise between the writer of an API and its user. If
the user doesn't uphold their end, then the user's code is in a bad place.
There's no recovery option.
If it is recoverable, then I would argue that it's not really a *contract*
failure. It is a part of the function's interface. This is the difference
between `vector::operator[]` and `vector::at`. One of these says "if the
parameter is outside of the range, the world ends". The other says "I throw
an exception if the parameter is out of the range". This is a choice you
make when you pick which function to call.
Also, making contract failures into exceptions, if that's a thing that's
going to happen, is really orthogonal to static exceptions entirely. What
might be interesting is a way to invoke a contracted function in a way that
forces the compiler to verify the contracts and emit an exception if the
contract fails. That would eliminate the need for having separate
`operator[]/at` functions.
But again, that's orthogonal to static exceptions.
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1b403308-2217-4ede-803c-cc7f13e79253%40isocpp.org.
------=_Part_785_1638667995.1544806401071
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Thursday, December 13, 2018 at 12:43:39 PM UTC-5, Bruce=
Adams wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-le=
ft: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">=
I've finally had a chance to review the [p0709r0](<a href=3D"http://www=
..open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf" target=3D"_blank=
" rel=3D"nofollow" onmousedown=3D"this.href=3D'http://www.google.com/ur=
l?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2=
F2018%2Fp0709r0.pdf\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHW5MK2mGax84H1_=
opMGuyvnfsROw';return true;" onclick=3D"this.href=3D'http://www.goo=
gle.com/url?q\x3dhttp%3A%2F%2Fwww.open-std.org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%=
2Fpapers%2F2018%2Fp0709r0.pdf\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHW5MK=
2mGax84H1_opMGuyvnfsROw';return true;">http://www.open-std.<wbr>org/jtc=
1/sc22/wg21/docs/<wbr>papers/2018/p0709r0.pdf</a>) <br>(and the related wor=
k on possible implementations).<br><br><div>I have a few questions/suggesti=
ons.</div><div>After doing some background research and rubberducking this =
has come out as rather a long post.<br>Hopefully some of it useful.</div><b=
r><br>1. how do we handle propagating exceptions?<br></div></blockquote><di=
v><br></div><div>I thought P0709 was fairly clear on this, but perhaps not.=
<br></div><div><br></div><div>Static exceptions are not triggered by throw=
ing a `std::error` type. Their behavior is triggered when a function declar=
ed with `throws` <i>emits</i> an exception. If such a function emits an exc=
eption of type `std::error`, then that is the static exception which is pro=
pagated up to the next layer. If such a function emits an exception of any =
other type, it is wrapped in an `exception_ptr` and stored in a `std::error=
` object, which is propagated up to the next layer.</div><div><br></div><di=
v>If static exception propagation passes through a function which is not de=
clared `throws` (or `noexcept` obviously), then the `std::error` being prop=
agated will be converted into a dynamic exception. If the current `std::err=
or` contains an `exception_ptr`, then this exception is unpacked and transm=
itted as if by `rethrow_exception`. Otherwise, `std::error` is transmitted =
via the dynamic exception mechanism as a regular `std::error` object (just =
like throwing any other type).</div><div><br></div><div>And, as Niall menti=
oned, you can't overload on `throws` anymore than you can overload on r=
eturn values (though see below for the exception). `throws` is part of the =
signature, but it's part of the signature in (more or less) the same wa=
y as the return value.<br></div><div><br></div><blockquote class=3D"gmail_q=
uote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;pad=
ding-left: 1ex;"><div dir=3D"ltr">*So is there still a place for dynamic ex=
ceptions in the future or should they be entirely deprecated?*<br></div></b=
lockquote><div><br></div><div>Part of the goal of Contracts is to get rid o=
f an entire class of "exceptions": basically, `logic_error` and m=
ost things derived from it. And P0709 states a very explicit goal of making=
allocation failures not throw.</div><div><br></div><div>Once you remove th=
ose two things, most standard library functions will become de-facto `noexc=
ept` (and thus we can make them de-jure `noexcept`).</div><div><br></div><d=
iv>How to deal with algorithms (that is, what kind of exceptions to propaga=
te from callbacks) is a different question. However, thanks to concepts, we=
do have the possibility of overloading on whether the given callable is `t=
hrows` or not.</div><div><br></div><div>But outside of template mechanism y=
es, whether a callback uses static or dynamic exceptions is a part of that =
callback system's interface, just like what object the callback is expe=
cted to return or what parameters it takes. But since the two mechanisms ar=
e compatible for the most part, it's not a real problem.</div><div><br>=
</div><div>The eventual goal is that everyone can shift towards `throws`, a=
nd dynamic exceptions will become legacy C++ stuff.<br></div><div><br></div=
><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">2. What are =
the semantics for void functions?<br></div></blockquote><div><br></div><div=
>The semantics of a `void`-`throws` function is no different from the seman=
tics of an `int`-`throws` function. The function either succeeds (and poten=
tially generates a value) or emits an exception of type `std:error` via sta=
tic exception propagation.</div><div><br></div><div>How exactly the compile=
r implements this is up to it. Which brings us to:<br></div><div>=C2=A0</di=
v><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;b=
order-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">E.g.<br><br=
>void Z() throws {<br>}<br><br>I'm presuming this is legal.<br>The retu=
rn channel was not previously used but now will be.<br>Assuming clever comp=
ilers could elide it in this case, this may add some (small) overhead.<br><=
br>The implementation of:<br><br>void Y() {<br>=C2=A0 // do stuff 1<br>=C2=
=A0 Z();<br>=C2=A0 // do stuff 2<br>}<br><br>Has to silently become:<br><br=
>void Y() {<br>=C2=A0 // do stuff 1<br><br>=C2=A0 retVal =3D Z();<br><br>=
=C2=A0 // save retVal and error flag if required<br><br>=C2=A0 // do stuff =
2<br><br>=C2=A0 // put retVal into return register or similar<br>=C2=A0 // =
set error flag<br>}<br><br><br>3. ABI overhead is lower but not zero<br></d=
iv></blockquote><div><br></div><div>The goal as spelled out in P0709 is not=
to be <i>free</i>. It is to be no more expensive than what you could achie=
ve yourself.</div><br><div>If you have a function that already deals in a s=
imple, integer error codes, you should be able to rewrite your code to thro=
w that error code via a static exception and receive it in the same place y=
ou had before (replacing the explicit error returns between the source and =
destination with static exception propagation). And if you do so, your code=
should have the same performance as if you had written the integer error c=
ode propagation yourself, if not <i>better</i> performance.</div><div><br><=
/div><div>That's what "zero-overhead" means in this context.<=
/div><div><br></div><div>Take your `void Z() throws` function. The error co=
de equivalent would be:</div><div><br></div><div style=3D"background-color:=
rgb(250, 250, 250); border-color: rgb(187, 187, 187); border-style: solid;=
border-width: 1px; overflow-wrap: break-word;" class=3D"prettyprint"><code=
class=3D"prettyprint"><div class=3D"subprettyprint"><span style=3D"color: =
#000;" class=3D"styled-by-prettify">error_code Z</span><span style=3D"color=
: #660;" class=3D"styled-by-prettify">()</span><span style=3D"color: #000;"=
class=3D"styled-by-prettify"> </span><span style=3D"color: #660;" class=3D=
"styled-by-prettify">{</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"><br>=C2=A0 </span><span style=3D"color: #660;" class=3D"styled-=
by-prettify">...</span><span style=3D"color: #000;" class=3D"styled-by-pret=
tify"><br></span><span style=3D"color: #660;" class=3D"styled-by-prettify">=
}</span><span style=3D"color: #000;" class=3D"styled-by-prettify"><br><br><=
/span><span style=3D"color: #008;" class=3D"styled-by-prettify">void</span>=
<span style=3D"color: #000;" class=3D"styled-by-prettify"> Y</span><span st=
yle=3D"color: #660;" class=3D"styled-by-prettify">()</span><span style=3D"c=
olor: #000;" class=3D"styled-by-prettify"> </span><span style=3D"color: #66=
0;" class=3D"styled-by-prettify">{</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #800;" clas=
s=3D"styled-by-prettify">// do stuff 1</span><span style=3D"color: #000;" c=
lass=3D"styled-by-prettify"><br>=C2=A0 </span><span style=3D"color: #008;" =
class=3D"styled-by-prettify">auto</span><span style=3D"color: #000;" class=
=3D"styled-by-prettify"> retVal </span><span style=3D"color: #660;" class=
=3D"styled-by-prettify">=3D</span><span style=3D"color: #000;" class=3D"sty=
led-by-prettify"> Z</span><span style=3D"color: #660;" class=3D"styled-by-p=
rettify">();</span><span style=3D"color: #000;" class=3D"styled-by-prettify=
"><br><br>=C2=A0 </span><span style=3D"color: #800;" class=3D"styled-by-pre=
ttify">// save retVal and error flag if required</span><span style=3D"color=
: #000;" class=3D"styled-by-prettify"><br><br>=C2=A0 </span><span style=3D"=
color: #800;" class=3D"styled-by-prettify">// do stuff 2</span><span style=
=3D"color: #000;" class=3D"styled-by-prettify"><br><br>=C2=A0 </span><span =
style=3D"color: #800;" class=3D"styled-by-prettify">// put retVal into retu=
rn register or similar</span><span style=3D"color: #000;" class=3D"styled-b=
y-prettify"><br>=C2=A0 </span><span style=3D"color: #800;" class=3D"styled-=
by-prettify">// set error flag</span><span style=3D"color: #000;" class=3D"=
styled-by-prettify"><br></span><span style=3D"color: #660;" class=3D"styled=
-by-prettify">}</span><span style=3D"color: #000;" class=3D"styled-by-prett=
ify"><br><br></span></div></code></div><div><br>See? The static exception c=
ode inserted is fundamentally the same. The "register overhead" w=
as there in both versions.<br> </div><div></div><div><br></div><blockquote =
class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1p=
x #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">4. Can we do better?<br><=
/div></blockquote><div><br></div><div>"Better" in what regard?</d=
iv><div><br></div><div>Are there functions whose error state is just one bi=
t? Sure. But there are a lot more functions that can error in multiple ways=
, and being able to tell how they errored out kind of matters.</div><div><b=
r></div><div>Being able to transmit a dynamic exception <i>through</i> code=
that uses static exceptions is kind of important. This backwards compatibi=
lity will make it easier to adopt static exception handling. Especially as =
removing most exceptions from the standard library will require years of de=
precation before they can be fully culled, during which time static and dyn=
amic exceptions will have to interoperate.<br></div><div><br></div><div>Als=
o, using a "well defined location on the stack" is problematic fo=
r cache reasons. That location would have to be somewhere at the bottom of =
the stack for that location to be easily accessible. But the bottom of the =
stack (pre-main) probably doesn't get accessed very much, so it may not=
be in the cache. So every time you emit a static exception, you get a cach=
e miss. By using a register (or rather, by allowing the compiler to be <i>a=
ble</i> to use a register), you bypass the cache entirely.</div><div><br></=
div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex=
;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">5. Has an=
yone considered something like a "conditions system" for C++?<br>=
</div></blockquote><div><br></div><div>Something like that would require a =
complete, page-1 redesign of the basic aspects of error handling. Static ex=
ceptions are a profound idea because they mostly look and behave like dynam=
ic exceptions, only with better efficiency and a tolerable degree of virali=
ty (the need to mark functions with `throws`). Static exceptions are not a =
brand new thing; they're the same old thing that works in a well-unders=
tood way, only better.</div><div><br></div><div>For example, you say that a=
condition handler can ignore a failed function. What exactly does that <i>=
mean</i> in terms of the C++ execution model? As the language currently sta=
nds, a function can either succeed or fail; what does it mean to be ignored=
? Does the function which calls it get to know about this "ignoring&qu=
ot;, or do I have to explicitly set up some communication line? What happen=
s to the return value object from such an "ignored" function?</di=
v><div><br></div><div>I'm not saying that this is necessarily a bad ide=
a. But it is a <i>new idea</i>, one which is far less fully-formed within t=
he context of C++ compared to exceptions. If an exception, static or dynami=
c, is thrown in a constructor, we know what happens. We don't have to c=
ome up with a definition of what that means for the object. And if you'=
re going to add an error mechanism to the language, it <i>needs</i> to have=
an answer for what it means for an object to fail to be constructed.</div>=
<div><br></div><div>Exceptions have an answer already, so an improved excep=
tion mechanism doesn't need to provide one.</div><div><br></div><div>If=
you're going to offer an alternative design, I advise you to take a ve=
ry careful look at the goals outlined in P0709, section 3.2.<br></div><div>=
<br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left:=
0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr">6. =
Contracts and OOM errors can be exceptional<br></div></blockquote><div><br>=
</div><div>The design for OOM handling in P0709R2 is perfectly capable of f=
orcing dynamic exception usage on allocation failure. However, contract fai=
lure as exceptions? Absolutely not.</div><div><br></div><div>If you deferen=
ce a null pointer, your code has done something so wrong that it clearly mu=
st die right now. The same idea goes for contract failures. A contract repr=
esents a promise between the writer of an API and its user. If the user doe=
sn't uphold their end, then the user's code is in a bad place. Ther=
e's no recovery option.</div><div><br></div><div>If it is recoverable, =
then I would argue that it's not really a <i>contract</i> failure. It i=
s a part of the function's interface. This is the difference between `v=
ector::operator[]` and `vector::at`. One of these says "if the paramet=
er is outside of the range, the world ends". The other says "I th=
row an exception if the parameter is out of the range". This is a choi=
ce you make when you pick which function to call.<br></div><div><br></div><=
div>Also, making contract failures into exceptions, if that's a thing t=
hat's going to happen, is really orthogonal to static exceptions entire=
ly. What might be interesting is a way to invoke a contracted function in a=
way that forces the compiler to verify the contracts and emit an exception=
if the contract fails. That would eliminate the need for having separate `=
operator[]/at` functions.</div><div><br></div><div>But again, that's or=
thogonal to static exceptions.<br></div></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/1b403308-2217-4ede-803c-cc7f13e79253%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/1b403308-2217-4ede-803c-cc7f13e79253=
%40isocpp.org</a>.<br />
------=_Part_785_1638667995.1544806401071--
------=_Part_784_1466678138.1544806401071--
.
Author: Nicol Bolas <jmckesson@gmail.com>
Date: Fri, 14 Dec 2018 08:55:58 -0800 (PST)
Raw View
------=_Part_868_130539996.1544806559023
Content-Type: multipart/alternative;
boundary="----=_Part_869_507359712.1544806559024"
------=_Part_869_507359712.1544806559024
Content-Type: text/plain; charset="UTF-8"
On Friday, December 14, 2018 at 11:09:54 AM UTC-5, Bruce Adams wrote:
>
>
> On Friday, 14 December 2018 14:19:35 UTC, Niall Douglas wrote:
>>
>>
>>> In P1095 Y() would cause a conversion from value-based throw to
>>>> type-based throw. The catch of std::error would work as expected.
>>>>
>>>
>>> I see so this would be implemented via the exception handler code in the
>>> C++ runtime used?
>>>
>>
>> It would literally be as if "throw" to rethrow the exception. Dynamic
>> exceptions already get created and thrown by the C++ runtime. So it would
>> be exactly as now, exact same overhead and cost.
>>
>>
>>>
>>> I think I've confused myself here as we are using the return channel
>>> rather than the handler in the C++ runtime for static exceptions.
>>> So somehow Y() must detect the error flag even though it is using the
>>> old calling convention?
>>>
>>
>> Y() knows it is calling Z() which is marked throws. So it knows it must
>> check the return from Z().
>>
>
> I think the source of my confusion is who is responsible for stack
> unwinding.
> With dynamic exceptions throw invokes the handler in the C++ runtime.
> With static exceptions throw would instead become _Fail() using the return
> channel.
>
> A implementation of Y() is free to invoke Z() throws.
> If it doesn't understand the new calling convention it will silently work
> until there is an exception.
>
How did you compile `Y` without it "understanding the new calling
convention" of `Z`? Because right now `void Z() throws` is a *compile error*.
So it is not possible for `Y` to call it without understanding what that
means. That means `Y` must have been compiled under a compiler that knows
how static exceptions work.
I think talking about "calling conventions" and such is looking at this
from the wrong perspective. By virtue of being compiled under P0709, `Y`
shall be a function that understand what static exceptions are and how they
work. `Y` does not *emit them*, but it can work with them internally.
`Y`'s declaration and function notation does not need a special marker; it
is a function that emits dynamic exceptions, just like any function before
it. Its ABI is unchanged; it's *implementation* is changed.
> Thus "Y() throws" throws can't just mean using the new calling convention.
> It must mean using the new calling convention AND throwing an exception
> using it.
> So the only purpose declaring "throws" has is so that the compiler can
> object if a function higher up is noexcept() and tries to use it.
>
No. The purpose in declaring it `throws` is to say that it propagates
exceptions via the static exception process rather than the dynamic one.
Every function that calls it needs to know this.
> Who converts the static exception to a dynamic one?
>
The system does. Or in this case, the dynamic version of `Y` does it,
because `Y` is a function that emits dynamic exceptions (that's what it
means for it to not be marked `throws`). If `Y` emits an exception, it
therefore must be a dynamic one.
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d94c38f1-e0f5-4c5e-b15a-d5f79e15b45f%40isocpp.org.
------=_Part_869_507359712.1544806559024
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr">On Friday, December 14, 2018 at 11:09:54 AM UTC-5, Bruce A=
dams wrote:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left=
: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><b=
r>On Friday, 14 December 2018 14:19:35 UTC, Niall Douglas wrote:<blockquot=
e class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px=
#ccc solid;padding-left:1ex"><div dir=3D"ltr"><blockquote class=3D"gmail_q=
uote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;paddin=
g-left:1ex"><div dir=3D"ltr"><div><br></div><blockquote class=3D"gmail_quot=
e" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-l=
eft:1ex"><div dir=3D"ltr"><div></div><div>In P1095 Y() would cause a conver=
sion from value-based throw to type-based throw. The catch of std::error wo=
uld work as expected.</div><div></div></div></blockquote><div><br></div><di=
v>I see so this would be implemented via the exception handler code in the =
C++ runtime used?</div></div></blockquote><div><br></div><div>It would lite=
rally be as if "throw" to rethrow the exception. Dynamic exceptio=
ns already get created and thrown by the C++ runtime. So it would be exactl=
y as now, exact same overhead and cost.</div><div>=C2=A0</div><blockquote c=
lass=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #c=
cc solid;padding-left:1ex"><div dir=3D"ltr"><div><br></div><div>I think I&#=
39;ve confused myself here as we are using the return channel rather than t=
he handler in the C++ runtime for static exceptions.</div><div>So somehow Y=
() must detect the error flag even though it is using the old calling conve=
ntion?<br></div></div></blockquote><div><br></div><div>Y() knows it is call=
ing Z() which is marked throws. So it knows it must check the return from Z=
().</div><div></div></div></blockquote><div><br></div><div>I think the sour=
ce of my confusion is who is responsible for stack unwinding.</div><div>Wit=
h dynamic exceptions throw invokes the handler in the C++ runtime.</div><di=
v>With static exceptions throw would instead become _Fail() using the retur=
n channel.</div><div><br></div><div>A implementation of Y() is free to invo=
ke Z() throws.</div><div>If it doesn't understand the new calling conve=
ntion it will silently work until there is an exception.</div></div></block=
quote><div><br></div><div>How did you compile `Y` without it "understa=
nding the new calling convention" of `Z`? Because right now `void Z() =
throws` is a <i>compile error</i>. So it is not possible for `Y` to call it=
without understanding what that means. That means `Y` must have been compi=
led under a compiler that knows how static exceptions work.</div><div><br><=
/div><div>I think talking about "calling conventions" and such is=
looking at this from the wrong perspective. By virtue of being compiled un=
der P0709, `Y` shall be a function that understand what static exceptions a=
re and how they work. `Y` does not *emit them*, but it can work with them i=
nternally.</div><div><br></div><div>`Y`'s declaration and function nota=
tion does not need a special marker; it is a function that emits dynamic ex=
ceptions, just like any function before it. Its ABI is unchanged; it's =
<i>implementation</i> is changed.<br></div><div>=C2=A0</div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #=
ccc solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div>Thus "Y=
() throws" throws can't just mean using the new calling convention=
.. It must mean using the new calling convention AND throwing an exception u=
sing it.</div><div>So the only purpose declaring "throws" has is =
so that the compiler can object if a function higher up is noexcept() and t=
ries to use it.<br></div></div></blockquote><div><br></div><div>No. The pur=
pose in declaring it `throws` is to say that it propagates exceptions via t=
he static exception process rather than the dynamic one. Every function tha=
t calls it needs to know this.<br></div><div>=C2=A0</div><blockquote class=
=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #cc=
c solid;padding-left: 1ex;"><div dir=3D"ltr"><div></div><div></div><div>Who=
converts the static exception to a dynamic one? <br></div></div></blockquo=
te><div><br></div><div>The system does. Or in this case, the dynamic versio=
n of `Y` does it, because `Y` is a function that emits dynamic exceptions (=
that's what it means for it to not be marked `throws`). If `Y` emits an=
exception, it therefore must be a dynamic one.<br></div><br></div>
<p></p>
-- <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 />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/d94c38f1-e0f5-4c5e-b15a-d5f79e15b45f%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/d94c38f1-e0f5-4c5e-b15a-d5f79e15b45f=
%40isocpp.org</a>.<br />
------=_Part_869_507359712.1544806559024--
------=_Part_868_130539996.1544806559023--
.