This failure was ultimately from incorrect handling of alias templates, but required a specific set of occurrences to happen in the specialization hash table. This cleans up the specialization streaming to add alias instantiations at the same point as other instantiations. I also removed some unneeded global variables dealing with mapping of duplicate decl contexts.
PR c++/99425 gcc/cp/ * cp-tree.h (map_context_from, map_context_to): Delete. (add_mergeable_specialization): Add is_alias parm. * pt.c (add_mergeable_specialization): Add is_alias parm, add them. * module.cc (map_context_from, map_context_to): Delete. (trees_in::decl_value): Add specializations later, adjust call. Drop useless alias lookup. Set duplicate fn parm context. (check_mergeable_decl): Drop context mapping. (trees_in::is_matching_decl): Likewise. (trees_in::read_function_def): Drop parameter context adjustment here. gcc/testsuite/ * g++.dg/modules/pr99425-1.h: New. * g++.dg/modules/pr99425-1_a.H: New. * g++.dg/modules/pr99425-1_b.H: New. * g++.dg/modules/pr99425-1_c.C: New. * g++.dg/modules/pr99425-2_a.X: New. * g++.dg/modules/pr99425-2_b.X: New. * g++.dg/template/pr99425.C: New. -- Nathan Sidwell
diff --git c/gcc/cp/cp-tree.h w/gcc/cp/cp-tree.h index 81ff375f8a5..e68e3905f80 100644 --- c/gcc/cp/cp-tree.h +++ w/gcc/cp/cp-tree.h @@ -5444,10 +5444,6 @@ extern int comparing_specializations; FIXME we should always do this except during deduction/ordering. */ extern int comparing_dependent_aliases; -/* When comparing specializations permit context _FROM to match _TO. */ -extern tree map_context_from; -extern tree map_context_to; - /* In parser.c. */ /* Nonzero if we are parsing an unevaluated operand: an operand to @@ -7241,7 +7237,8 @@ extern void walk_specializations (bool, void *); extern tree match_mergeable_specialization (bool is_decl, spec_entry *); extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec); -extern void add_mergeable_specialization (bool is_decl, spec_entry *, +extern void add_mergeable_specialization (bool is_decl, bool is_alias, + spec_entry *, tree outer, unsigned); extern tree add_outermost_template_args (tree, tree); extern tree add_extra_args (tree, tree); diff --git c/gcc/cp/module.cc w/gcc/cp/module.cc index 6dbdc926cb4..2b529d80499 100644 --- c/gcc/cp/module.cc +++ w/gcc/cp/module.cc @@ -279,11 +279,6 @@ static inline tree identifier (const cpp_hashnode *node) return HT_IDENT_TO_GCC_IDENT (HT_NODE (const_cast<cpp_hashnode *> (node))); } -/* During duplicate detection we need to tell some comparators that - these are equivalent. */ -tree map_context_from; -tree map_context_to; - /* Id for dumping module information. */ int module_dump_id; @@ -8074,16 +8069,6 @@ trees_in::decl_value () if (spec.spec) set_constraints (decl, spec.spec); - if (mk & MK_template_mask - || mk == MK_partial) - { - /* Add to specialization tables now that constraints etc are - added. */ - bool is_type = mk == MK_partial || !(mk & MK_tmpl_decl_mask); - - spec.spec = is_type ? type : mk & MK_tmpl_tmpl_mask ? inner : decl; - add_mergeable_specialization (!is_type, &spec, decl, spec_flags); - } if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl)) { @@ -8111,28 +8096,25 @@ trees_in::decl_value () /* Set the TEMPLATE_DECL's type. */ TREE_TYPE (decl) = TREE_TYPE (inner); + if (mk & MK_template_mask + || mk == MK_partial) + { + /* Add to specialization tables now that constraints etc are + added. */ + bool is_type = mk == MK_partial || !(mk & MK_tmpl_decl_mask); + + spec.spec = is_type ? type : mk & MK_tmpl_tmpl_mask ? inner : decl; + add_mergeable_specialization (!is_type, + !is_type && mk & MK_tmpl_alias_mask, + &spec, decl, spec_flags); + } + if (NAMESPACE_SCOPE_P (decl) && (mk == MK_named || mk == MK_unique || mk == MK_enum || mk == MK_friend_spec) && !(VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl))) add_module_namespace_decl (CP_DECL_CONTEXT (decl), decl); - /* The late insertion of an alias here or an implicit member - (next block), is ok, because we ensured that all imports were - loaded up before we started this cluster. Thus an insertion - from some other import cannot have happened between the - merged insertion above and these insertions down here. */ - if (mk == MK_alias_spec) - { - /* Insert into type table. */ - tree ti = DECL_TEMPLATE_INFO (inner); - spec_entry elt = - {TI_TEMPLATE (ti), TI_ARGS (ti), TREE_TYPE (inner)}; - tree texist = match_mergeable_specialization (false, &elt); - if (texist) - set_overrun (); - } - if (DECL_ARTIFICIAL (decl) && TREE_CODE (decl) == FUNCTION_DECL && !DECL_TEMPLATE_INFO (decl) @@ -8176,6 +8158,14 @@ trees_in::decl_value () if (!is_matching_decl (existing, decl, is_typedef)) unmatched_duplicate (existing); + if (inner && TREE_CODE (inner) == FUNCTION_DECL) + { + tree e_inner = STRIP_TEMPLATE (existing); + for (auto parm = DECL_ARGUMENTS (inner); + parm; parm = DECL_CHAIN (parm)) + DECL_CONTEXT (parm) = e_inner; + } + /* And our result is the existing node. */ decl = existing; } @@ -8186,7 +8176,7 @@ trees_in::decl_value () if (!e) { spec.spec = inner; - add_mergeable_specialization (true, &spec, decl, spec_flags); + add_mergeable_specialization (true, false, &spec, decl, spec_flags); } else if (e != existing) set_overrun (); @@ -10378,8 +10368,9 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, { if (mk & MK_tmpl_alias_mask) /* It should be in both tables. */ - gcc_assert (match_mergeable_specialization (false, entry) - == TREE_TYPE (existing)); + gcc_checking_assert + (match_mergeable_specialization (false, entry) + == TREE_TYPE (existing)); else if (mk & MK_tmpl_tmpl_mask) existing = DECL_TI_TEMPLATE (existing); } @@ -10392,7 +10383,7 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, } /* The walkabout should have found ourselves. */ - gcc_assert (existing == decl); + gcc_checking_assert (existing == decl); } } else if (mk != MK_unique) @@ -10622,8 +10613,6 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key) break; case FUNCTION_DECL: - map_context_from = d_inner; - map_context_to = m_inner; if (tree m_type = TREE_TYPE (m_inner)) if ((!key.ret || same_type_p (key.ret, fndecl_declared_return_type (m_inner))) @@ -10647,7 +10636,6 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key) if (cp_tree_equal (key.constraints, m_reqs)) found = match; } - map_context_from = map_context_to = NULL_TREE; break; case TYPE_DECL: @@ -11022,12 +11010,6 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) gcc_checking_assert (TREE_CODE (e_inner) == TREE_CODE (d_inner)); } - gcc_checking_assert (!map_context_from); - /* This mapping requres the new decl on the lhs and the existing - entity on the rhs of the comparitors below. */ - map_context_from = d_inner; - map_context_to = e_inner; - if (TREE_CODE (d_inner) == FUNCTION_DECL) { tree e_ret = fndecl_declared_return_type (existing); @@ -11104,7 +11086,6 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) else if (!cp_tree_equal (TREE_TYPE (decl), TREE_TYPE (existing))) { mismatch: - map_context_from = map_context_to = NULL_TREE; if (DECL_IS_UNDECLARED_BUILTIN (existing)) /* Just like duplicate_decls, presum the user knows what they're doing in overriding a builtin. */ @@ -11121,8 +11102,6 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) } } - map_context_from = map_context_to = NULL_TREE; - if (DECL_IS_UNDECLARED_BUILTIN (existing) && !DECL_IS_UNDECLARED_BUILTIN (decl)) { @@ -11463,10 +11442,6 @@ trees_in::read_function_def (tree decl, tree maybe_template) tree maybe_dup = odr_duplicate (maybe_template, DECL_SAVED_TREE (decl)); bool installing = maybe_dup && !DECL_SAVED_TREE (decl); - if (maybe_dup) - for (auto parm = DECL_ARGUMENTS (maybe_dup); parm; parm = DECL_CHAIN (parm)) - DECL_CONTEXT (parm) = decl; - if (int wtag = i ()) { int tag = 1; @@ -12881,10 +12856,11 @@ specialization_add (bool decl_p, spec_entry *entry, void *data_) || DECL_CLASS_TEMPLATE_P (entry->tmpl)); /* Only alias templates can appear in both tables (and - if they're in the type table they must also be in the decl table). */ + if they're in the type table they must also be in the decl + table). */ gcc_checking_assert (!match_mergeable_specialization (true, entry) - == (decl_p || !DECL_ALIAS_TEMPLATE_P (entry->tmpl))); + == !DECL_ALIAS_TEMPLATE_P (entry->tmpl)); } else if (VAR_OR_FUNCTION_DECL_P (entry->spec)) gcc_checking_assert (!DECL_LOCAL_DECL_P (entry->spec)); diff --git c/gcc/cp/pt.c w/gcc/cp/pt.c index 5e485f10d19..3f05737f17c 100644 --- c/gcc/cp/pt.c +++ w/gcc/cp/pt.c @@ -30008,25 +30008,41 @@ get_mergeable_specialization_flags (tree tmpl, tree decl) get_mergeable_specialization_flags. */ void -add_mergeable_specialization (bool decl_p, spec_entry *elt, +add_mergeable_specialization (bool decl_p, bool alias_p, spec_entry *elt, tree decl, unsigned flags) { - hash_table<spec_hasher> *specializations - = decl_p ? decl_specializations : type_specializations; - hashval_t hash = spec_hasher::hash (elt); - auto *slot = specializations->find_slot_with_hash (elt, hash, INSERT); - - /* We don't distinguish different constrained partial type - specializations, so there could be duplicates. Everything else - must be new. */ - if (!(flags & 2 && *slot)) + if (decl_p) { - gcc_checking_assert (!*slot); + auto *slot = decl_specializations->find_slot_with_hash (elt, hash, INSERT); + gcc_checking_assert (!*slot); auto entry = ggc_alloc<spec_entry> (); *entry = *elt; *slot = entry; + + if (alias_p) + { + elt->spec = TREE_TYPE (elt->spec); + gcc_checking_assert (elt->spec); + } + } + + if (!decl_p || alias_p) + { + auto *slot = type_specializations->find_slot_with_hash (elt, hash, INSERT); + + /* We don't distinguish different constrained partial type + specializations, so there could be duplicates. Everything else + must be new. */ + if (!(flags & 2 && *slot)) + { + gcc_checking_assert (!*slot); + + auto entry = ggc_alloc<spec_entry> (); + *entry = *elt; + *slot = entry; + } } if (flags & 1) diff --git c/gcc/testsuite/g++.dg/modules/pr99425-1.h w/gcc/testsuite/g++.dg/modules/pr99425-1.h new file mode 100644 index 00000000000..de167a64331 --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-1.h @@ -0,0 +1,11 @@ +template<typename T> +struct make_signed +{ + using type = int; +}; + +template<typename S> +using make_signed_t = typename make_signed<S>::type; + +template<typename U> +auto ssize (U &parm) -> make_signed_t<decltype(parm.call())>; diff --git c/gcc/testsuite/g++.dg/modules/pr99425-1_a.H w/gcc/testsuite/g++.dg/modules/pr99425-1_a.H new file mode 100644 index 00000000000..6117d4a182c --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-1_a.H @@ -0,0 +1,4 @@ +// PR 99425 alias dependent specializations +// { dg-additional-options {-fmodule-header} } +// { dg-module-cmi {} } +#include "pr99425-1.h" diff --git c/gcc/testsuite/g++.dg/modules/pr99425-1_b.H w/gcc/testsuite/g++.dg/modules/pr99425-1_b.H new file mode 100644 index 00000000000..98303a0c687 --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-1_b.H @@ -0,0 +1,19 @@ +// { dg-additional-options {-fmodule-header -fdump-lang-module-alias} } +// { dg-module-cmi {} } + +#include "pr99425-1.h" + +import "pr99425-1_a.H"; + +struct Cont +{ + int call (); +}; + +inline void widget (Cont parm) +{ + ssize (parm); +} + +// { dg-final { scan-lang-dump {Read:-[0-9]*'s alias spec merge key \(new\) type_decl:'::make_signed_t'\n ... Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::make_signed'\n Read:-1's named merge key \(matched\) template_decl:'::template ssize'} module } } + diff --git c/gcc/testsuite/g++.dg/modules/pr99425-1_c.C w/gcc/testsuite/g++.dg/modules/pr99425-1_c.C new file mode 100644 index 00000000000..28ef3a1ff30 --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-1_c.C @@ -0,0 +1,11 @@ +// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias} } +import "pr99425-1_a.H"; +import "pr99425-1_b.H"; + +void frob (Cont parm) +{ + ssize (parm); +} + +// { dg-final { scan-lang-dump {Read:-1's named merge key \(new\) template_decl:'::template ssize'} module } } +// { dg-final { scan-lang-dump {Read:-1's named merge key \(matched\) template_decl:'::template ssize'} module } } diff --git c/gcc/testsuite/g++.dg/modules/pr99425-2_a.X w/gcc/testsuite/g++.dg/modules/pr99425-2_a.X new file mode 100644 index 00000000000..9a44dc33567 --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-2_a.X @@ -0,0 +1,7 @@ +// PR 99425 template aliases can cause equivalences in the hash +// tables, and for extra excitement be reordered on rehash. + +// { dg-additional-options {-x c++-system-header stdexcept -fmodules-ts} } +// { dg-prune-output {linker input file unused} } + +No! DO NOT COMPILE; diff --git c/gcc/testsuite/g++.dg/modules/pr99425-2_b.X w/gcc/testsuite/g++.dg/modules/pr99425-2_b.X new file mode 100644 index 00000000000..5e453542cd1 --- /dev/null +++ w/gcc/testsuite/g++.dg/modules/pr99425-2_b.X @@ -0,0 +1,4 @@ +// { dg-additional-options {-x c++-system-header mutex -fmodules-ts} } +// { dg-prune-output {linker input file unused} } + +No! DO NOT COMPILE; diff --git c/gcc/testsuite/g++.dg/template/pr99425.C w/gcc/testsuite/g++.dg/template/pr99425.C new file mode 100644 index 00000000000..fd49c86b74c --- /dev/null +++ w/gcc/testsuite/g++.dg/template/pr99425.C @@ -0,0 +1,45 @@ +// { dg-do compile { target c++20 } } +// a potential fix for 99425 generated an ICE here. + +template<typename _Tp> +struct is_nothrow_destructible; + +template<typename _Tp> +struct common_reference; + +template<typename _Tp> +concept same_as + = true; + +template<typename _Sent, typename _Iter> +concept sentinel_for + = same_as<common_reference<_Sent>> + && is_nothrow_destructible<_Iter>::value; + +template<typename _Tp> +concept __member_end + = requires (_Tp& __t) + { + { true } + -> sentinel_for<decltype(__t)>; + }; + +template<typename _Tp> +concept __adl_end + = requires (_Tp& __t) + { + { true } + -> sentinel_for<decltype(__t)>; + }; + +template<typename _Tp> +requires __member_end<_Tp> || __adl_end<_Tp> + void + Bar (_Tp&& __t) +{ +} + +void test03 () +{ + Bar (1); // { dg-error "no matching function" } +}