c++: duplicate alias templates with decltype [PR 99425]

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.
This commit is contained in:
Nathan Sidwell 2021-03-18 05:12:59 -07:00
parent 150a829acc
commit 87e3c2ef68
10 changed files with 159 additions and 69 deletions

View File

@ -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);

View File

@ -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));

View File

@ -30009,25 +30009,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)

View File

@ -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())>;

View File

@ -0,0 +1,4 @@
// PR 99425 alias dependent specializations
// { dg-additional-options {-fmodule-header} }
// { dg-module-cmi {} }
#include "pr99425-1.h"

View File

@ -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 } }

View File

@ -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 } }

View File

@ -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;

View File

@ -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;

View File

@ -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" }
}