c++: cross-module __cxa_atexit use [PR 98531]
authorNathan Sidwell <nathan@acm.org>
Fri, 22 Jan 2021 16:48:27 +0000 (08:48 -0800)
committerNathan Sidwell <nathan@acm.org>
Mon, 8 Feb 2021 17:24:39 +0000 (09:24 -0800)
The compiler's use of lazily-declared library functions must insert
said functions into a symbol table, so that they can be correctly
merged across TUs at the module-level.  We have too many different
ways of declaring such library functions.  This fixes __cxa_atexit (or
its system-specific variations), pushing (or merging) the decl into
the appropriate namespace.  Because we're pushing a lazy builtin,
check_redeclaration_exception_specification needed a tweak to allow a
such a builtin's eh spec to differ from what the user may have already
declared. (I suspect no all headers declare atexit as noexcept.)

We can't test the -fno-use-cxa-atexit path with modules, as that
requires a followup patch to a closely related piece (which also
affects cxa_atexit targets in other circumstances).

PR c++/98531
gcc/cp/
* cp-tree.h (push_abi_namespace, pop_abi_namespace): Declare.
* decl.c (push_abi_namespace, pop_abi_namespace): Moved
from rtti.c, add default namespace arg.
(check_redeclaration_exception_specification): Allow a lazy
builtin's eh spec to differ from an lready-declared user
declaration.
(declare_global_var): Use push/pop_abi_namespace.
(get_atexit_node): Push the fndecl into a namespace.
* rtti.c (push_abi_namespace, pop_abi_namespace): Moved to
decl.c.
gcc/testsuite/
* g++.dg/modules/pr98531-1.h: New.
* g++.dg/modules/pr98531-1_a.H: New.
* g++.dg/modules/pr98531-1_b.C: New.
* g++.dg/abi/pr98531-1.C: New.
* g++.dg/abi/pr98531-2.C: New.
* g++.dg/abi/pr98531-3.C: New.
* g++.dg/abi/pr98531-4.C: New.

gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/rtti.c
gcc/testsuite/g++.dg/abi/pr98531-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr98531-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr98531-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/pr98531-4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/pr98531-1.h [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/pr98531-1_a.H [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/pr98531-1_b.C [new file with mode: 0644]

index 41472a895b55f50d0554b4ae7a6e7b4457326954..4ed3936ade27435f02a41958476b4524d8fbaeec 100644 (file)
@@ -193,7 +193,9 @@ enum cp_tree_index
 
     CPTI_MODULE_HWM,
     /* Nodes after here change during compilation, or should not be in
-       the module's global tree table.  */
+       the module's global tree table.  Such nodes must be locatable
+       via name lookup or type-construction, as those are the only
+       cross-TU matching capabilities remaining.  */
 
     /* We must find these via the global namespace.  */
     CPTI_STD,
@@ -6625,6 +6627,9 @@ extern tree make_typename_type                    (tree, tree, enum tag_types, tsubst_flags_t);
 extern tree build_typename_type                        (tree, tree, tree, tag_types);
 extern tree make_unbound_class_template                (tree, tree, tree, tsubst_flags_t);
 extern tree make_unbound_class_template_raw    (tree, tree, tree);
+extern unsigned push_abi_namespace             (tree node = abi_node);
+extern void pop_abi_namespace                  (unsigned flags,
+                                                tree node = abi_node);
 extern tree build_library_fn_ptr               (const char *, tree, int);
 extern tree build_cp_library_fn_ptr            (const char *, tree, int);
 extern tree push_library_fn                    (tree, tree, tree, int);
index b0265fc36107548d31d05788e128f5750c993f1f..4ddee2160165ec4ed406ee4d7832a998a6d578bc 100644 (file)
@@ -1209,7 +1209,8 @@ check_redeclaration_exception_specification (tree new_decl,
      all declarations, including the definition and an explicit
      specialization, of that function shall have an
      exception-specification with the same set of type-ids.  */
-  if (! DECL_IS_UNDECLARED_BUILTIN (old_decl)
+  if (!DECL_IS_UNDECLARED_BUILTIN (old_decl)
+      && !DECL_IS_UNDECLARED_BUILTIN (new_decl)
       && !comp_except_specs (new_exceptions, old_exceptions, ce_normal))
     {
       const char *const msg
@@ -4696,6 +4697,30 @@ cxx_init_decl_processing (void)
     using_eh_for_cleanups ();
 }
 
+/* Enter an abi node in global-module context.  returns a cookie to
+   give to pop_abi_namespace.  */
+
+unsigned
+push_abi_namespace (tree node)
+{
+  push_nested_namespace (node);
+  push_visibility ("default", 2);
+  unsigned flags = module_kind;
+  module_kind = 0;
+  return flags;
+}
+
+/* Pop an abi namespace, FLAGS is the cookie push_abi_namespace gave
+   you.  */
+
+void
+pop_abi_namespace (unsigned flags, tree node)
+{
+  module_kind = flags;
+  pop_visibility (2);
+  pop_nested_namespace (node);
+}
+
 /* Create the VAR_DECL for __FUNCTION__ etc. ID is the name to give
    the decl, LOC is the location to give the decl, NAME is the
    initialization string and TYPE_DEP indicates whether NAME depended
@@ -8687,21 +8712,19 @@ cp_finish_decomp (tree decl, tree first, unsigned int count)
 static tree
 declare_global_var (tree name, tree type)
 {
-  tree decl;
-
-  push_to_top_level ();
-  decl = build_decl (input_location, VAR_DECL, name, type);
+  auto cookie = push_abi_namespace (global_namespace);
+  tree decl = build_decl (input_location, VAR_DECL, name, type);
   TREE_PUBLIC (decl) = 1;
   DECL_EXTERNAL (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
-  DECL_CONTEXT (decl) = FROB_CONTEXT (global_namespace);
+  DECL_CONTEXT (decl) = FROB_CONTEXT (current_namespace);
   /* If the user has explicitly declared this variable (perhaps
      because the code we are compiling is part of a low-level runtime
      library), then it is possible that our declaration will be merged
      with theirs by pushdecl.  */
   decl = pushdecl (decl);
   cp_finish_decl (decl, NULL_TREE, false, NULL_TREE, 0);
-  pop_from_top_level ();
+  pop_abi_namespace (cookie, global_namespace);
 
   return decl;
 }
@@ -8746,6 +8769,7 @@ get_atexit_node (void)
   tree fn_ptr_type;
   const char *name;
   bool use_aeabi_atexit;
+  tree ctx = global_namespace;
 
   if (atexit_node)
     return atexit_node;
@@ -8780,10 +8804,23 @@ get_atexit_node (void)
       fn_type = build_function_type_list (integer_type_node,
                                          argtype0, argtype1, argtype2,
                                          NULL_TREE);
+      /* ... which needs noexcept.  */
+      fn_type = build_exception_variant (fn_type, noexcept_true_spec);
       if (use_aeabi_atexit)
-       name = "__aeabi_atexit";
+       {
+         name = "__aeabi_atexit";
+         push_to_top_level ();
+         int n = push_namespace (get_identifier ("__aeabiv1"), false);
+         ctx = current_namespace;
+         while (n--)
+           pop_namespace ();
+         pop_from_top_level ();
+       }
       else
-       name = "__cxa_atexit";
+       {
+         name = "__cxa_atexit";
+         ctx = abi_node;
+       }
     }
   else
     {
@@ -8797,12 +8834,23 @@ get_atexit_node (void)
       /* Build the final atexit type.  */
       fn_type = build_function_type_list (integer_type_node,
                                          fn_ptr_type, NULL_TREE);
+      /* ... which needs noexcept.  */
+      fn_type = build_exception_variant (fn_type, noexcept_true_spec);
       name = "atexit";
     }
 
   /* Now, build the function declaration.  */
   push_lang_context (lang_name_c);
+  auto cookie = push_abi_namespace (ctx);
   atexit_fndecl = build_library_fn_ptr (name, fn_type, ECF_LEAF | ECF_NOTHROW);
+  DECL_CONTEXT (atexit_fndecl) = FROB_CONTEXT (current_namespace);
+  /* Install as hidden builtin so we're (a) more relaxed about
+    exception spec matching and (b) will not give a confusing location
+    in diagnostic and (c) won't magically appear in user-visible name
+    lookups.  */
+  DECL_SOURCE_LOCATION (atexit_fndecl) = BUILTINS_LOCATION;
+  atexit_fndecl = pushdecl (atexit_fndecl, /*hiding=*/true);
+  pop_abi_namespace (cookie, ctx);
   mark_used (atexit_fndecl);
   pop_lang_context ();
   atexit_node = decay_conversion (atexit_fndecl, tf_warning_or_error);
index d3cb9ad4425a2f8c91e44ee625d6f3f8f99515ca..b41d95469c6d17532e2172c3d8b68a14fcd73fd6 100644 (file)
@@ -143,24 +143,6 @@ static bool typeinfo_in_lib_p (tree);
 
 static int doing_runtime = 0;
 \f
-static unsigned
-push_abi_namespace (void)
-{
-  push_nested_namespace (abi_node);
-  push_visibility ("default", 2);
-  unsigned flags = module_kind;
-  module_kind = 0;
-  return flags;
-}
-
-static void
-pop_abi_namespace (unsigned flags)
-{
-  module_kind = flags;
-  pop_visibility (2);
-  pop_nested_namespace (abi_node);
-}
-
 /* Declare language defined type_info type and a pointer to const
    type_info.  This is incomplete here, and will be completed when
    the user #includes <typeinfo>.  There are language defined
diff --git a/gcc/testsuite/g++.dg/abi/pr98531-1.C b/gcc/testsuite/g++.dg/abi/pr98531-1.C
new file mode 100644 (file)
index 0000000..dc9ad99
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+// PR 98531  Making __cxa_atexit (or atexit) more visible means it
+// must be consistent with the std library's declarations
+
+struct C
+{
+  ~C () noexcept;
+  C () noexcept;
+};
+
+C &frob ()
+{
+  static C c; // Requires atexit functionality
+
+  return c;
+}
+
+// Make sure this agrees with what we introduced above
+#include <cxxabi.h>
+#include <cstdlib>
diff --git a/gcc/testsuite/g++.dg/abi/pr98531-2.C b/gcc/testsuite/g++.dg/abi/pr98531-2.C
new file mode 100644 (file)
index 0000000..4bdf9b9
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++11 } }
+// PR 98531  Making __cxa_atexit (or atexit) more visible means it
+// must be consistent with the std library's declarations
+
+// Make sure this agrees with what we introduce below
+#include <cxxabi.h>
+#include <cstdlib>
+
+struct C
+{
+  ~C () noexcept;
+  C () noexcept;
+};
+
+C &frob ()
+{
+  static C c; // Requires atexit functionality
+
+  return c;
+}
diff --git a/gcc/testsuite/g++.dg/abi/pr98531-3.C b/gcc/testsuite/g++.dg/abi/pr98531-3.C
new file mode 100644 (file)
index 0000000..de6129d
--- /dev/null
@@ -0,0 +1,21 @@
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -fno-use-cxa-atexit }
+// PR 98531  Making __cxa_atexit (or atexit) more visible means it
+// must be consistent with the std library's declarations
+
+extern "C" int atexit (void (*) (void));
+
+struct C
+{
+  ~C () noexcept;
+  C () noexcept;
+};
+
+C &frob ()
+{
+  static C c; // Requires atexit functionality
+
+  return c;
+}
+
+
diff --git a/gcc/testsuite/g++.dg/abi/pr98531-4.C b/gcc/testsuite/g++.dg/abi/pr98531-4.C
new file mode 100644 (file)
index 0000000..ec64ee0
--- /dev/null
@@ -0,0 +1,19 @@
+// { dg-do compile { target c++11 } }
+// { dg-additional-options -fno-use-cxa-atexit }
+// PR 98531  Making __cxa_atexit (or atexit) more visible means it
+// must be consistent with the std library's declarations
+
+struct C
+{
+  ~C () noexcept;
+  C () noexcept;
+};
+
+C &frob ()
+{
+  static C c; // Requires atexit functionality
+
+  return c;
+}
+
+extern "C" int atexit (void (*) (void));
diff --git a/gcc/testsuite/g++.dg/modules/pr98531-1.h b/gcc/testsuite/g++.dg/modules/pr98531-1.h
new file mode 100644 (file)
index 0000000..62d4c1d
--- /dev/null
@@ -0,0 +1,13 @@
+
+struct __waiters
+{
+  __waiters() noexcept;
+  ~__waiters () noexcept;
+
+  static __waiters &_S_for()
+  {
+    static __waiters w;
+    
+    return w;
+  }
+};
diff --git a/gcc/testsuite/g++.dg/modules/pr98531-1_a.H b/gcc/testsuite/g++.dg/modules/pr98531-1_a.H
new file mode 100644 (file)
index 0000000..cbd2090
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-require-cxa-atexit "" }
+// { dg-additional-options "-fmodule-header -fuse-cxa-atexit" }
+// PR c++ 98531  no-context __cxa_atexit
+// { dg-module-cmi {} }
+
+#include "pr98531-1.h"
diff --git a/gcc/testsuite/g++.dg/modules/pr98531-1_b.C b/gcc/testsuite/g++.dg/modules/pr98531-1_b.C
new file mode 100644 (file)
index 0000000..096cdc8
--- /dev/null
@@ -0,0 +1,5 @@
+// { dg-require-cxa-atexit "" }
+// { dg-additional-options "-fmodules-ts -fno-module-lazy -fuse-cxa-atexit" }
+
+#include "pr98531-1.h"
+import "pr98531-1_a.H";