EH level II

Jason Merrill jason at cygnus.com
Thu Aug 31 07:29:39 UTC 2000


Thanks a lot for writing this up.  Some comments follow.

Note that I will be unavailable for the three Thursdays after tomorrow, Sep
14 in particular.

2.2.2 says:

  uncaughtExceptions is a count of exceptions thrown and not yet caught by
  the current thread.

Add:
(this includes rethrown exceptions, which may still have active handlers,
but are not considered caught).

2.3.1 says:

  The interface to the emergency buffer is implementation-defined, and used
  only by the __cxa_allocate_exception runtime library routine specified in
  Section 2.4.2 below.

Shouldn't __cxa_get_globals be able to get space from the emergency buffer?

2.4.1 says:

  Evaluate the thrown expression, and copy it into the buffer returned by
  __cxa_allocate_exception, possibly using a copy constructor (see Section
  2.4.3). If evaluation of the thrown expression or the copy constructor
  itself exits by throwing an exception, that exception will propagate
  instead of the expression itself. Cleanup code must ensure that
  __cxa_free_exception is called on the just allocated exception object. 

But the standard says:
[except.terminate]:

  15.5.1  The terminate() function                    [except.terminate]

1 In the following situations exception handling must be  abandoned  for
  less subtle error handling techniques:
  
  --when  the  exception handling mechanism, after completing evaluation
    of the expression to be thrown but before the  exception  is  caught
    (_except.throw_),  calls  a user function that exits via an uncaught
    exception,134)
  _________________________
  134) For example, if the object being thrown is of a class with a copy
  constructor, terminate() will be called if that copy constructor exits
  with an exception during a throw.

I suggest removing the words "or the copy constructor itself", and adding a
note

  (If the copy constructor itself exits by throwing an exception, terminate
  is called.)

Furthermore, when I was dealing with this stuff in g++, I decided that
rather than try to deal with cleaning up the allocated buffer, I would
wait to allocate the exception until after the thrown expression has been
evaluated.  This means we can't elide the copy, but eliminates a cleanup,
making the code simpler and probably smaller.  If the temporary has a
destructor, that needs a cleanup, but that's handled by the standard
compiler mechanisms.

I don't see why this sequence needs to be part of the ABI, actually.  It
should be a QOI issue.

2.4.2 says:

  although an emergency buffer is available to handle bad_alloc exceptions
  during exception object allocation.

I don't see what this has to do with exception object allocation.  If I
have an infinite loop of new int[5], the emergency buffer will allow me to
throw bad_alloc in that case, too.

Perhaps "...available so the implementation can throw bad_alloc exceptions
under low memory conditions".

Actually, since there is no public interface to the emergency buffer, it
seems like a QOI issue as well.  It should be possible to have a conforming
implementation with no emergency buffer, though I'd leave the current stuff
in as a suggestion.

2.4.3: As mentioned above, this seems like overspecification.  This stuff is
constrained by the language, but that's it.

2.4.4: ***How do we tell the EH code how to destroy the exception?***
  I would think it should be an argument to __cxa_throw.

  "Save the typeinfo argument to __cxa_throw in the __cxa_exception header"
  is unclear; my first reaction was "but there isn't a __cxa_throw in that
  header."  I think we can drop "to __cxa_throw".

2.5: The landing pad stuff seems like part of Level III.  An implementation
  shouldn't need to use landing pads to be able to share exception objects
  with other implementations.

2.5.1 says:

  Some cleanup code generated by the front-end may then execute,
  corresponding to the exit of the try block. For instance, a local variable
  with lifetime limited by the try block enclosing the scope would be
  destroyed here.

The last two lines seem rather awkward.  How about

  an automatic variable local to the try block would be destroyed here.

And similarly,

  an automatic variable local to the outer block of the function would be
  destroyed here.

This should use "handler" rather than "catch clause" in most cases.

This should also note that we loop through the various try blocks on our
way out of the function, trying our switch value at each one.

We should replace [RESX] with "_Unwind_Resume();".

I think we need a "goto X1" at the end of the example.

2.5.2 says:

  If the exception is not a C++ exception, the C++ personality routine must
  ignore it, that is, return _URC_CONTINUE_UNWIND in both phases.

What about running cleanups?
Did we give up on being able to catch foreign exceptions?

2.5.4: Rather than "the current exception object", say "the exception on
    top of the caughtExceptions stack".  Rather than "there is no current
    exception", "the stack is empty".

2.5.5: Needs to say "When unexpected() exits (by throwing) _after being
    entered due to a throw_."

Cases 1 and 3 have the same effect; that should be clearer.

Jason




More information about the cxx-abi-dev mailing list