longjmp
and destructorsSection: 17.13 [support.runtime] Status: NAD Submitter: Sean Hunt Opened: 2009-11-16 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [support.runtime].
View all issues with NAD status.
Discussion:
17.13 [support.runtime]/4 says that longjmp
is undefined if
unwinding by the mechanism used by catch and throw would invoke any nontrivial
destructors. However, the text as written is rather vague, in particular when
dealing with catch(...)
:
void foo() { jump_buf buf; non_trivial_dtor n1; // 1 if (!setjmp(buf)) { non_trivial_dtor n2; // 2 try { longjmp(buf, 1); } catch (...) { } } }
My interpretation of the meaning of 17.13 [support.runtime]/4 is that
declaration 2, but not 1, would cause the longjmp
to be undefined
behavior. However, it's not entirely clear from the text. Arguably, replacing
the setjmp
and longjmp
with catch
would still cause
the destructor for n1
to be called after the unwinding, which would
lead to undefined behavior. This is clearly not an intended consequence of the
wording. However, it is probably still UB, as n1
now has
"indeterminate" value, and running its destructor on foo
's exit will
cause Bad Things.
Declarations 2 has a more interesting issue. The catch(...)
muddles up
the definition that uses throw
and catch
- if
longjmp()
were indeed a throw
, control would never return to
the setjmp
. As such, n2
's destructor wouldn't be called
(except by the argument for n1
, which is that the destructor would be
called later as the frame was left in the normal control flow).
I suggest that paragraph 4 of 17.13 [support.runtime] should be replaced with the following, or something that reads better but has the same effect:
The function signature
longjmp(jmp_buf jbuf, int val)
has more restricted behavior in this International Standard. A call tolongjmp
has undefined behavior if any non-trivial destructors would be called were thelongjmp
call replaced with a throw-expression whose nearest matching handler were a (possibly imaginary) function-try-block on the function containing the correspondingsetjmp
call.
[ 2009-11-17 Moved to Tentatively NAD after 5 positive votes on c++std-lib. Rationale added below. ]
Proposed resolution:
Change 17.13 [support.runtime]/4:
The function signature
longjmp(jmp_buf jbuf, int val)
has more restricted behavior in this International Standard.AA call tosetjmp
/longjmp
call pair has undefined behavior if replacing thesetjmp
andlongjmp
bycatch
andthrow
would invoke any non-trivial destructors for any automatic objects.longjmp
has undefined behavior if any non-trivial destructors would be called were thelongjmp
call replaced with a throw-expression whose nearest matching handler were a (possibly imaginary) function-try-block on the function containing the correspondingsetjmp
call.
Rationale:
In the given example, it is clear that it is only n2
and not
n1
that is destroyed by the longjmp
.
At this late stage in the standards process, we are focusing on issues that impact users or implementers. Trying to rewrite complex wording just for the sake of improved clarity is likely to do more harm than good.