diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b2eb17f34aa..65f73d0e91d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2001-08-03 Richard Henderson + + * except.c (collect_one_action_chain): Add an explicit cleanup + action if regions surrounding a catch were encoded entirely + within the call-site entry. + 2001-08-03 Richard Henderson * dbxout.c (dbxout_symbol_location): Flatten subregs first; diff --git a/gcc/except.c b/gcc/except.c index e24a901c3c4..4397cfc963d 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -3020,6 +3020,17 @@ expand_eh_return () emit_label (around_label); } +/* In the following functions, we represent entries in the action table + as 1-based indicies. Special cases are: + + 0: null action record, non-null landing pad; implies cleanups + -1: null action record, null landing pad; implies no action + -2: no call-site entry; implies must_not_throw + -3: we have yet to process outer regions + + Further, no special cases apply to the "next" field of the record. + For next, 0 means end of list. */ + struct action_record { int offset; @@ -3123,8 +3134,16 @@ collect_one_action_chain (ar_hash, region) if (next == -3) { next = collect_one_action_chain (ar_hash, region->outer); - if (next < 0) + + /* If there is no next action, terminate the chain. */ + if (next == -1) next = 0; + /* If all outer actions are cleanups or must_not_throw, + we'll have no action record for it, since we had wanted + to encode these states in the call-site record directly. + Add a cleanup action to the chain to catch these. */ + else if (next <= 0) + next = add_action_record (ar_hash, 0, 0); } next = add_action_record (ar_hash, c->u.catch.filter, next); } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c03e7e3948e..2424e7451f3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2001-08-03 Richard Henderson + + * g++.dg/eh/filter1.C, g++.dg/eh/filter2.C: New tests. + 2001-08-02 Neil Booth * gcc.dg/cpp/19951025-1.c: Update. diff --git a/gcc/testsuite/g++.dg/eh/filter1.C b/gcc/testsuite/g++.dg/eh/filter1.C new file mode 100644 index 00000000000..6ff0574c2a3 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/filter1.C @@ -0,0 +1,43 @@ +// Test that cleanups get run when a catch filter fails to match. +// { dg-do run } + +extern "C" void exit(int); +extern "C" void abort(); + +struct a +{ + a(); + ~a(); +}; + +struct e1 {}; +struct e2 {}; + +void +ex_test () +{ + a aa; + try + { + throw e1 (); + } + catch (e2 &) + { + } +} + +int +main () +{ + try + { + ex_test (); + } + catch (...) + { + } + abort (); +} + +a::a() { } +a::~a() { exit (0); } diff --git a/gcc/testsuite/g++.dg/eh/filter2.C b/gcc/testsuite/g++.dg/eh/filter2.C new file mode 100644 index 00000000000..fe87cc9a308 --- /dev/null +++ b/gcc/testsuite/g++.dg/eh/filter2.C @@ -0,0 +1,59 @@ +// Test that terminate gets run when a catch filter fails to match while +// running destructors. Original bug depended on a::~a being inlined. +// { dg-do run } +// { dg-options -O } + +#include +#include + +struct e1 {}; +struct e2 {}; + +struct a +{ + a () { } + + ~a () + { + try + { + throw e1(); + } + catch (e2 &) + { + } + } +}; + +void +ex_test () +{ + a aa; + try + { + throw e1 (); + } + catch (e2 &) + { + } +} + +void my_terminate () +{ + std::exit (0); +} + +int +main () +{ + std::set_terminate (my_terminate); + + try + { + ex_test (); + } + catch (...) + { + } + abort (); +}