* decl.c (cxx_maybe_build_cleanup): Handle
authormmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 May 2007 23:47:29 +0000 (23:47 +0000)
committermmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 May 2007 23:47:29 +0000 (23:47 +0000)
__attribute__((cleanup)).
* g++.dg/ext/cleanup-1.C: New test.
* g++.dg/ext/cleanup-2.C: Likewise.
* g++.dg/ext/cleanup-3.C: Likewise.
* g++.dg/ext/cleanup-4.C: Likewise.
* g++.dg/ext/cleanup-5.C: Likewise.
* g++.dg/ext/cleanup-6.C: Likewise.
* g++.dg/ext/cleanup-8.C: Likewise.
* g++.dg/ext/cleanup-9.C: Likewise.
* g++.dg/ext/cleanup-10.C: Likewise.
* g++.dg/ext/cleanup-11.C: Likewise.
* g++.dg/ext/cleanup-dtor.C: Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@124930 138bc75d-0d04-0410-961f-82ee72b054a4

14 files changed:
gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/cleanup-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/cleanup-dtor.C [new file with mode: 0644]

index 6486ec0..e6b4f45 100644 (file)
@@ -1,3 +1,8 @@
+2007-05-21  Mark Mitchell  <mark@codesourcery.com>
+
+       * decl.c (cxx_maybe_build_cleanup): Handle
+       __attribute__((cleanup)).
+
 2007-05-19  Manuel Lopez-Ibanez  <manu@gcc.gnu.org>
 
        * cvt.c (cp_convert_and_check): Don't check warnings if the
index 9b62658..c76568a 100644 (file)
@@ -11636,39 +11636,80 @@ complete_vars (tree type)
   complete_type_check_abstract (type);
 }
 
-/* If DECL is of a type which needs a cleanup, build that cleanup
-   here.  */
+/* If DECL is of a type which needs a cleanup, build and return an
+   expression to perform that cleanup here.  Return NULL_TREE if no
+   cleanup need be done.  */
 
 tree
 cxx_maybe_build_cleanup (tree decl)
 {
-  tree type = TREE_TYPE (decl);
+  tree type;
+  tree attr;
+  tree cleanup;
+
+  /* Assume no cleanup is required.  */
+  cleanup = NULL_TREE;
 
-  if (type != error_mark_node && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
+  if (error_operand_p (decl))
+    return cleanup;
+
+  /* Handle "__attribute__((cleanup))".  We run the cleanup function
+     before the destructor since the destructor is what actually
+     terminates the lifetime of the object.  */
+  attr = lookup_attribute ("cleanup", DECL_ATTRIBUTES (decl));
+  if (attr)
+    {
+      tree id;
+      tree fn;
+      tree arg;
+
+      /* Get the name specified by the user for the cleanup function.  */
+      id = TREE_VALUE (TREE_VALUE (attr));
+      /* Look up the name to find the cleanup function to call.  It is
+        important to use lookup_name here because that is what is
+        used in c-common.c:handle_cleanup_attribute when performing
+        initial checks on the attribute.  Note that those checks
+        include ensuring that the function found is not an overloaded
+        function, or an object with an overloaded call operator,
+        etc.; we can rely on the fact that the functionfound is an
+        ordinary FUNCTION_DECL.  */
+      fn = lookup_name (id);
+      arg = build_address (decl);
+      mark_used (decl);
+      cleanup = build_function_call (fn, build_tree_list (NULL_TREE,
+                                                         arg));
+    }
+  /* Handle ordinary C++ destructors.  */
+  type = TREE_TYPE (decl);
+  if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type))
     {
       int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR;
-      tree rval;
       bool has_vbases = (TREE_CODE (type) == RECORD_TYPE
                         && CLASSTYPE_VBASECLASSES (type));
+      tree addr;
+      tree call;
 
       if (TREE_CODE (type) == ARRAY_TYPE)
-       rval = decl;
+       addr = decl;
       else
        {
          cxx_mark_addressable (decl);
-         rval = build_unary_op (ADDR_EXPR, decl, 0);
+         addr = build_unary_op (ADDR_EXPR, decl, 0);
        }
 
       /* Optimize for space over speed here.  */
       if (!has_vbases || flag_expensive_optimizations)
        flags |= LOOKUP_NONVIRTUAL;
 
-      rval = build_delete (TREE_TYPE (rval), rval,
+      call = build_delete (TREE_TYPE (addr), addr,
                           sfk_complete_destructor, flags, 0);
-
-      return rval;
+      if (cleanup)
+       cleanup = build_compound_expr (cleanup, call);
+      else
+       cleanup = call;
     }
-  return NULL_TREE;
+
+  return cleanup;
 }
 \f
 /* When a stmt has been parsed, this function is called.  */
index e46cf78..c37b343 100644 (file)
@@ -1,3 +1,17 @@
+2007-05-21  Mark Mitchell  <mark@codesourcery.com>
+
+       * g++.dg/ext/cleanup-1.C: New test.
+       * g++.dg/ext/cleanup-2.C: Likewise.
+       * g++.dg/ext/cleanup-3.C: Likewise.
+       * g++.dg/ext/cleanup-4.C: Likewise.
+       * g++.dg/ext/cleanup-5.C: Likewise.
+       * g++.dg/ext/cleanup-6.C: Likewise.
+       * g++.dg/ext/cleanup-8.C: Likewise.
+       * g++.dg/ext/cleanup-9.C: Likewise.
+       * g++.dg/ext/cleanup-10.C: Likewise.
+       * g++.dg/ext/cleanup-11.C: Likewise.
+       * g++.dg/ext/cleanup-dtor.C: Likewise.
+
 2007-05-21  Andrew Pinski  <andrew_pinski@playstation.sony.com>
 
        PR middle-end/31995
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-1.C b/gcc/testsuite/g++.dg/ext/cleanup-1.C
new file mode 100644 (file)
index 0000000..383c64e
--- /dev/null
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-Wall" } */
+/* Validate expected warnings and errors.  */
+
+#define U      __attribute__((unused))
+#define C(x)   __attribute__((cleanup(x)))
+
+static int f1(void *x U) { return 0; }
+static void f2() { } /* { dg-error "too many arguments" } */
+static void f3(void) { } /* { dg-error "too many arguments" } */
+static void f4(void *x U) { }
+static void f5(int *x U) { }
+static void f6(double *x U) { }
+static void f7(const int *x U) { }
+static void f8(const int *x U, int y U) { } /* { dg-error "too few arguments" } */
+static void f9(int x U) { }
+
+void test(void)
+{
+  int o1 C(f1);
+  int o2 C(f2);         /* { dg-error "at this point" } */
+  int o3 C(f3);                /* { dg-error "at this point" } */
+  int o4 C(f4);
+  int o5 C(f5);
+  int o6 C(f6);                /* { dg-error "cannot convert" } */
+  int o7 C(f7);
+  int o8 C(f8);                /* { dg-error "at this point" } */
+  int o9 C(f9);                /* { dg-error "conversion" } */
+  int o10 U C(undef);  /* { dg-error "not a function" } */
+  int o11 U C(o1);     /* { dg-error "not a function" } */
+  int o12 U C("f1");   /* { dg-error "not an identifier" } */
+  static int o13 U C(f1); /* { dg-warning "attribute ignored" } */
+}
+
+int o14 C(f1);         /* { dg-warning "attribute ignored" } */
+void t15(int o U C(f1)) {} /* { dg-warning "attribute ignored" } */
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-10.C b/gcc/testsuite/g++.dg/ext/cleanup-10.C
new file mode 100644 (file)
index 0000000..c0c6971
--- /dev/null
@@ -0,0 +1,117 @@
+/* { dg-do run { target hppa*-*-hpux* *-*-linux* powerpc*-*-darwin* } } */
+/* { dg-options "-fexceptions -fnon-call-exceptions -O2" } */
+/* Verify that cleanups work with exception handling through signal frames
+   on alternate stack.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc 
+    = (struct _Unwind_Exception *) malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig, siginfo_t *info, void *ctx)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  stack_t ss;
+  struct sigaction s;
+
+  ss.ss_size = 4 * sysconf (_SC_PAGESIZE);
+  if (ss.ss_size < SIGSTKSZ)
+    ss.ss_size = SIGSTKSZ;
+  ss.ss_sp = malloc (ss.ss_size);
+  if (ss.ss_sp == NULL)
+    exit (1);
+  ss.ss_flags = 0;
+  if (sigaltstack (&ss, NULL) < 0)
+    exit (1);
+
+  sigemptyset (&s.sa_mask);
+  s.sa_sigaction = fn4;
+  s.sa_flags = SA_RESETHAND | SA_ONSTACK;
+  sigaction (SIGSEGV, &s, NULL);
+  sigaction (SIGBUS, &s, NULL);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-11.C b/gcc/testsuite/g++.dg/ext/cleanup-11.C
new file mode 100644 (file)
index 0000000..06c6569
--- /dev/null
@@ -0,0 +1,117 @@
+/* { dg-do run { target hppa*-*-hpux* *-*-linux* powerpc*-*-darwin* } } */
+/* { dg-options "-fexceptions -fnon-call-exceptions -O2" } */
+/* Verify that cleanups work with exception handling through realtime signal
+   frames on alternate stack.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc 
+    = (struct _Unwind_Exception*) malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig, siginfo_t *info, void *ctx)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  stack_t ss;
+  struct sigaction s;
+
+  ss.ss_size = 4 * sysconf (_SC_PAGESIZE);
+  if (ss.ss_size < SIGSTKSZ)
+    ss.ss_size = SIGSTKSZ;
+  ss.ss_sp = malloc (ss.ss_size);
+  if (ss.ss_sp == NULL)
+    exit (1);
+  ss.ss_flags = 0;
+  if (sigaltstack (&ss, NULL) < 0)
+    exit (1);
+
+  sigemptyset (&s.sa_mask);
+  s.sa_sigaction = fn4;
+  s.sa_flags = SA_RESETHAND | SA_ONSTACK | SA_SIGINFO;
+  sigaction (SIGSEGV, &s, NULL);
+  sigaction (SIGBUS, &s, NULL);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-2.C b/gcc/testsuite/g++.dg/ext/cleanup-2.C
new file mode 100644 (file)
index 0000000..d9033b4
--- /dev/null
@@ -0,0 +1,22 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+/* Verify that cleanup works in the most basic of ways.  */
+
+extern "C" void exit(int);
+extern "C" void abort(void);
+
+static void handler(void *p __attribute__((unused)))
+{
+  exit (0);
+}
+
+static void doit(void)
+{
+  int x __attribute__((cleanup (handler)));
+}
+
+int main()
+{
+  doit ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-3.C b/gcc/testsuite/g++.dg/ext/cleanup-3.C
new file mode 100644 (file)
index 0000000..5367c49
--- /dev/null
@@ -0,0 +1,45 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+/* Verify that the cleanup handler receives the proper contents
+   of the variable.  */
+
+extern "C" void exit(int);
+extern "C" void abort(void);
+
+static int expected;
+
+static void
+handler(int *p)
+{
+  if (*p != expected)
+    abort ();
+}
+
+static void __attribute__((noinline))
+bar(void)
+{
+}
+
+static void doit(int x, int y)
+{
+  int r __attribute__((cleanup (handler)));
+  if (x < y)
+    {
+      r = 0;
+      return;
+    }
+
+  bar();
+  r = x + y;
+}
+
+int main()
+{
+  expected = 0;
+  doit (1, 2);
+
+  expected = 3;
+  doit (2, 1);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-4.C b/gcc/testsuite/g++.dg/ext/cleanup-4.C
new file mode 100644 (file)
index 0000000..ae9a6e4
--- /dev/null
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-options "" } */
+/* Verify cleanup execution on non-trivial exit from a block.  */
+
+extern "C" void exit(int);
+extern "C" void abort(void);
+
+static int counter;
+
+static void
+handler(int *p)
+{
+  counter += *p;
+}
+
+static void __attribute__((noinline))
+bar(void)
+{
+}
+
+static void doit(int n, int n2)
+{
+  int i;
+  for (i = 0; i < n; ++i)
+    {
+      int dummy __attribute__((cleanup (handler))) = i;
+      if (i == n2)
+       break;
+      bar();
+    }
+}
+
+int main()
+{
+  doit (10, 6);
+  if (counter != 0 + 1 + 2 + 3 + 4 + 5 + 6)
+    abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-5.C b/gcc/testsuite/g++.dg/ext/cleanup-5.C
new file mode 100644 (file)
index 0000000..db4c2bb
--- /dev/null
@@ -0,0 +1,54 @@
+/* HP-UX libunwind.so doesn't provide _UA_END_OF_STACK */
+/* { dg-do run } */
+/* { dg-options "-fexceptions" } */
+/* { dg-skip-if "" { "ia64-*-hpux11.*" }  { "*" } { "" } } */
+/* Verify that cleanups work with exception handling.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <string.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc 
+    = (struct _Unwind_Exception *) malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  exit (0);
+}
+
+static void doit ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  force_unwind ();
+}
+
+int main()
+{ 
+  doit ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-6.C b/gcc/testsuite/g++.dg/ext/cleanup-6.C
new file mode 100644 (file)
index 0000000..4e3d538
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O" } */
+/* Verify that a cleanup marked "inline" gets inlined.  */
+
+static inline void xyzzy(void *p __attribute__((unused)))
+{
+}
+
+void doit(void)
+{
+  int x __attribute__((cleanup (xyzzy)));
+}
+
+/* { dg-final { scan-assembler-not "xyzzy" } } */
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-8.C b/gcc/testsuite/g++.dg/ext/cleanup-8.C
new file mode 100644 (file)
index 0000000..ece12e8
--- /dev/null
@@ -0,0 +1,100 @@
+/* { dg-do run { target hppa*-*-hpux* *-*-linux* powerpc*-*-darwin* } } */
+/* { dg-options "-fexceptions -fnon-call-exceptions -O2" } */
+/* Verify that cleanups work with exception handling through signal
+   frames.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc 
+    = (struct _Unwind_Exception *) malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  signal (SIGSEGV, fn4);
+  signal (SIGBUS, fn4);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-9.C b/gcc/testsuite/g++.dg/ext/cleanup-9.C
new file mode 100644 (file)
index 0000000..59a9735
--- /dev/null
@@ -0,0 +1,104 @@
+/* { dg-do run { target hppa*-*-hpux* *-*-linux* powerpc*-*-darwin* } } */
+/* { dg-options "-fexceptions -fnon-call-exceptions -O2" } */
+/* Verify that cleanups work with exception handling through realtime
+   signal frames.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+                   _Unwind_Exception_Class exc_class,
+                   struct _Unwind_Exception *exc_obj,
+                   struct _Unwind_Context *context,
+                   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc 
+    = (struct _Unwind_Exception *) malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+                   
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+                   
+  abort ();
+}
+
+int count;
+char *null;
+
+static void counter (void *p __attribute__((unused)))
+{
+  ++count;
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  if (count != 2)
+    abort ();
+  exit (0);
+}
+
+static int __attribute__((noinline)) fn5 ()
+{
+  char dummy __attribute__((cleanup (counter)));
+  force_unwind ();
+  return 0;
+}
+
+static void fn4 (int sig, siginfo_t *info, void *ctx)
+{
+  char dummy __attribute__((cleanup (counter)));
+  fn5 ();
+  null = NULL;
+}
+
+static void fn3 ()
+{
+  abort ();
+}
+
+static int __attribute__((noinline)) fn2 ()
+{
+  *null = 0;
+  fn3 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn1 ()
+{
+  struct sigaction s;
+  sigemptyset (&s.sa_mask);
+  s.sa_sigaction = fn4;
+  s.sa_flags = SA_RESETHAND | SA_SIGINFO;
+  sigaction (SIGSEGV, &s, NULL);
+  sigaction (SIGBUS, &s, NULL);
+  fn2 ();
+  return 0;
+}
+
+static int __attribute__((noinline)) fn0 ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  fn1 ();
+  null = 0;
+  return 0;
+}
+
+int main()
+{ 
+  fn0 ();
+  abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/cleanup-dtor.C b/gcc/testsuite/g++.dg/ext/cleanup-dtor.C
new file mode 100644 (file)
index 0000000..58da646
--- /dev/null
@@ -0,0 +1,28 @@
+// Check that destructors are run after cleanup functions.
+// { dg-do run }
+
+extern "C" void abort ();
+
+int i;
+
+struct S {
+  ~S() {
+    if (i != 1)
+      abort ();
+    i = 2;
+  }
+};
+
+void f(void *) {
+  if (i != 0)
+    abort ();
+  i = 1;
+}
+
+int main () {
+  {
+    S s __attribute__((cleanup (f)));
+  }
+  if (i != 2)
+    abort ();
+}