On 25 November 2010 11:03, Duncan Sands <baldrick at free.fr> wrote:> I don't understand what you are saying. Cleanups (e.g. destructors)Hi Duncan, Cleanup landing pads normally call destructors, but they're not a destructor themselves. I'm simply saying that compiler generated blocks (such as cleanups) should never depend on user variables. But I get what you're saying. If a cleanup area calls a destructor, and destructors use user values, they have an indirect conditional dependency that cannot be solved at compile time, unless you have a way to normalize the call graph to disambiguate all dominance analysis.> I don't get what you are talking about here. You can access any variables > you like, whether local or global, in a catch handler. They are not passed > to the handler via llvm.eh.exception or llvm.eh.selector, they are simply > accessed directly (the unwinder restores registers etc making this > possible).Destructors and Catch areas are user code, you could access any variable as pleased. Cleanup areas are compiler code, those were the ones I was talking about.> In Ada you can throw and exception inside a destructor and it does not lead > to program termination.Ok, sorry. I should stop thinking C++ here.... it's difficult, but I'll try... ;) cheers, --renato
Hi Renato,>> I don't understand what you are saying. Cleanups (e.g. destructors) > > Hi Duncan, > > Cleanup landing pads normally call destructors, but they're not a > destructor themselves. I'm simply saying that compiler generated > blocks (such as cleanups) should never depend on user variables.I see what you are saying now. Unfortunately optimizations such as inlining can result in code ending up in cleanup landing pads.>> In Ada you can throw and exception inside a destructor and it does not lead >> to program termination. > > Ok, sorry. I should stop thinking C++ here.... it's difficult, but > I'll try... ;)OK :) Essentially what happens is as follows: when running destructors when exiting a scope (which may be a nested scope), if a destructor throws an exception then any remaining destructors are first run, then the Program_Error exception is thrown at the point of the scope exit. This may be caught by an enclosing handler. For example, in pseudo C++ code: { ... do some stuff ... { ... declare a variable A with a destructor that does not throw ... declare a variable B with a destructor that throws ... ... do some stuff ... } // end of scope; when execution reaches here, the destructor for B // is run, throwing an exception. The exception is intercepted and // the destructor for A is run. Once the destructor for A has run, // Program_Error is thrown here. ... do some stuff (never executed due to Program_Error being thrown) ... } catch (Program_Error) { // The Program_Error thrown above is caught here. print("evil destructors can't hurt me!"); } Ciao, Duncan.
On 25 November 2010 12:01, Duncan Sands <baldrick at free.fr> wrote:> I see what you are saying now. Unfortunately optimizations such as > inlining can result in code ending up in cleanup landing pads.This is the part that escapes me... but I haven't spent too much time thinking about inlining EH information yet.> OK :) Essentially what happens is as follows: when running destructors > when exiting a scope (which may be a nested scope), if a destructor throws > an exception then any remaining destructors are first run, then the > Program_Error exception is thrown at the point of the scope exit. This > may be caught by an enclosing handler.That's a neat model. What happens if two (or more) exceptions are thrown while you're cleaning up another? For instance, if A's destructor also throws an exception? A's exception was not thrown while processing B's exception (but while cleaning up *before* processing it), so it's not a nested exception, but an "aside" exception... It makes my head hurt... I think Stroustroup didn't want to think about that, too... :D cheers, --renato