From 57b17858a1b3719507ccad926fb57b05f26935f8 Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Fri, 22 Jan 2021 08:48:27 -0800 Subject: [PATCH] c++: cross-module __cxa_atexit use [PR 98531] 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 | 7 ++- gcc/cp/decl.c | 66 +++++++++++++++++++--- gcc/cp/rtti.c | 18 ------ gcc/testsuite/g++.dg/abi/pr98531-1.C | 20 +++++++ gcc/testsuite/g++.dg/abi/pr98531-2.C | 20 +++++++ gcc/testsuite/g++.dg/abi/pr98531-3.C | 21 +++++++ gcc/testsuite/g++.dg/abi/pr98531-4.C | 19 +++++++ gcc/testsuite/g++.dg/modules/pr98531-1.h | 13 +++++ gcc/testsuite/g++.dg/modules/pr98531-1_a.H | 6 ++ gcc/testsuite/g++.dg/modules/pr98531-1_b.C | 5 ++ 10 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/pr98531-1.C create mode 100644 gcc/testsuite/g++.dg/abi/pr98531-2.C create mode 100644 gcc/testsuite/g++.dg/abi/pr98531-3.C create mode 100644 gcc/testsuite/g++.dg/abi/pr98531-4.C create mode 100644 gcc/testsuite/g++.dg/modules/pr98531-1.h create mode 100644 gcc/testsuite/g++.dg/modules/pr98531-1_a.H create mode 100644 gcc/testsuite/g++.dg/modules/pr98531-1_b.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 41472a895b5..4ed3936ade2 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index b0265fc3610..4ddee216016 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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); diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index d3cb9ad4425..b41d95469c6 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -143,24 +143,6 @@ static bool typeinfo_in_lib_p (tree); static int doing_runtime = 0; -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 . 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 index 00000000000..dc9ad99fccf --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/pr98531-1.C @@ -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 +#include diff --git a/gcc/testsuite/g++.dg/abi/pr98531-2.C b/gcc/testsuite/g++.dg/abi/pr98531-2.C new file mode 100644 index 00000000000..4bdf9b9ca0f --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/pr98531-2.C @@ -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 +#include + +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 index 00000000000..de6129dc808 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/pr98531-3.C @@ -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 index 00000000000..ec64ee07a17 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/pr98531-4.C @@ -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 index 00000000000..62d4c1d1f90 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/pr98531-1.h @@ -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 index 00000000000..cbd2090b4c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/pr98531-1_a.H @@ -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 index 00000000000..096cdc8c08a --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/pr98531-1_b.C @@ -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"; -- 2.30.2