Skip to content

Commit

Permalink
c++/modules: member alias tmpl partial inst [PR103994]
Browse files Browse the repository at this point in the history
Alias templates are weird in that their specializations can appear in
both decl_specializations and type_specializations.  They're always in
the decl table, and additionally appear in the type table only at parse
time via finish_template_type.  There seems to be no good reason for
them to appear in both tables, and the code paths end up stepping over
each other in particular for a partial instantiation such as
A<B>::key_arg<T> in the below modules testcase: the type code path
(lookup_template_class) wants to set TI_TEMPLATE to the most general
template whereas the decl code path (tsubst_template_decl called during
instantiation of A<B>) already set TI_TEMPLATE to the partially
instantiated TEMPLATE_DECL.  This TI_TEMPLATE change ends up confusing
modules which decides to stream the logically equivalent TYPE_DECL and
TEMPLATE_DECL for this partial instantiation separately.

This patch fixes this by making lookup_template_class dispatch to
instantiate_alias_template early for alias template specializations.
In turn we now add such specializations only to the decl table.  This
admits some nice simplification in the modules code which otherwise has
to cope with such specializations appearing in both tables.

	PR c++/103994

gcc/cp/ChangeLog:

	* cp-tree.h (add_mergeable_specialization): Remove second
	parameter.
	* module.cc (depset::disc_bits::DB_ALIAS_TMPL_INST_BIT): Remove.
	(depset::disc_bits::DB_ALIAS_SPEC_BIT): Remove.
	(depset::is_alias_tmpl_inst): Remove.
	(depset::is_alias): Remove.
	(merge_kind::MK_tmpl_alias_mask): Remove.
	(merge_kind::MK_alias_spec): Remove.
	(merge_kind_name): Remove entries for alias specializations.
	(trees_out::core_vals) <case TEMPLATE_DECL>: Adjust after
	removing is_alias_tmpl_inst.
	(trees_in::decl_value): Adjust add_mergeable_specialization
	calls.
	(trees_out::get_merge_kind) <case depset::EK_SPECIALIZATION>:
	Use MK_decl_spec for alias template specializations.
	(trees_out::key_mergeable): Simplify after MK_tmpl_alias_mask
	removal.
	(depset::hash::make_dependency): Adjust after removing
	DB_ALIAS_TMPL_INST_BIT.
	(specialization_add): Don't allow alias templates when !decl_p.
	(depset::hash::add_specializations): Remove now-dead code
	accomodating alias template specializations in the type table.
	* pt.cc (lookup_template_class): Dispatch early to
	instantiate_alias_template for alias templates.  Simplify
	accordingly.
	(add_mergeable_specialization): Remove alias_p parameter and
	simplify accordingly.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/pr99425-1_b.H: s/alias/decl in dump scan.
	* g++.dg/modules/tpl-alias-1_a.H: Likewise.
	* g++.dg/modules/tpl-alias-2_a.H: New test.
	* g++.dg/modules/tpl-alias-2_b.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
  • Loading branch information
Patrick Palka committed Mar 7, 2024
1 parent 19b23bf commit f5c1224
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 124 deletions.
3 changes: 1 addition & 2 deletions gcc/cp/cp-tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -7642,8 +7642,7 @@ 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, bool is_alias,
spec_entry *,
extern void add_mergeable_specialization (bool is_decl, spec_entry *,
tree outer, unsigned);
extern tree add_to_template_args (tree, tree);
extern tree add_outermost_template_args (tree, tree);
Expand Down
86 changes: 16 additions & 70 deletions gcc/cp/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2305,11 +2305,7 @@ class depset {
DB_HIDDEN_BIT, /* A hidden binding. */
/* The following bits are not independent, but enumerating them is
awkward. */
DB_ALIAS_TMPL_INST_BIT, /* An alias template instantiation. */
DB_ALIAS_SPEC_BIT, /* Specialization of an alias template
(in both spec tables). */
DB_TYPE_SPEC_BIT, /* Specialization in the type table.
*/
DB_TYPE_SPEC_BIT, /* Specialization in the type table. */
DB_FRIEND_SPEC_BIT, /* An instantiated template friend. */
};

Expand Down Expand Up @@ -2400,14 +2396,6 @@ class depset {
{
return get_flag_bit<DB_UNREACHED_BIT> ();
}
bool is_alias_tmpl_inst () const
{
return get_flag_bit<DB_ALIAS_TMPL_INST_BIT> ();
}
bool is_alias () const
{
return get_flag_bit<DB_ALIAS_SPEC_BIT> ();
}
bool is_hidden () const
{
return get_flag_bit<DB_HIDDEN_BIT> ();
Expand Down Expand Up @@ -2781,13 +2769,11 @@ enum merge_kind
MK_template_mask = 0x10, /* A template specialization. */

MK_tmpl_decl_mask = 0x4, /* In decl table. */
MK_tmpl_alias_mask = 0x2, /* Also in type table */

MK_tmpl_tmpl_mask = 0x1, /* We want TEMPLATE_DECL. */

MK_type_spec = MK_template_mask,
MK_decl_spec = MK_template_mask | MK_tmpl_decl_mask,
MK_alias_spec = MK_decl_spec | MK_tmpl_alias_mask,

MK_hwm = 0x20
};
Expand All @@ -2805,7 +2791,7 @@ static char const *const merge_kind_name[MK_hwm] =
NULL, NULL,

"decl spec", "decl tmpl spec", /* 20,21 decl (template). */
"alias spec", "alias tmpl spec", /* 22,23 alias (template). */
NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
};
Expand Down Expand Up @@ -6350,8 +6336,7 @@ trees_out::core_vals (tree t)
gcc_checking_assert
(TREE_VISITED (((lang_tree_node *)t)->template_decl.arguments));
gcc_checking_assert
(TREE_VISITED (((lang_tree_node *)t)->template_decl.result)
|| dep_hash->find_dependency (t)->is_alias_tmpl_inst ());
(TREE_VISITED (((lang_tree_node *)t)->template_decl.result));
if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (t))
WT (DECL_CHAIN (t));
break;
Expand Down Expand Up @@ -8270,16 +8255,13 @@ trees_in::decl_value ()
{
bool is_type = TREE_CODE (inner) == TYPE_DECL;
spec.spec = is_type ? type : inner;
add_mergeable_specialization (!is_type, false,
&spec, decl, spec_flags);
add_mergeable_specialization (!is_type, &spec, decl, spec_flags);
}
else if (mk & MK_template_mask)
{
bool is_type = !(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);
add_mergeable_specialization (!is_type, &spec, decl, spec_flags);
}

if (NAMESPACE_SCOPE_P (decl)
Expand Down Expand Up @@ -8356,7 +8338,7 @@ trees_in::decl_value ()
if (!e)
{
spec.spec = inner;
add_mergeable_specialization (true, false, &spec, decl, spec_flags);
add_mergeable_specialization (true, &spec, decl, spec_flags);
}
else if (e != existing)
set_overrun ();
Expand Down Expand Up @@ -10514,8 +10496,6 @@ trees_out::get_merge_kind (tree decl, depset *dep)
mk = MK_friend_spec;
else if (dep->is_type_spec ())
mk = MK_type_spec;
else if (dep->is_alias ())
mk = MK_alias_spec;
else
mk = MK_decl_spec;

Expand Down Expand Up @@ -10626,11 +10606,6 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
gcc_assert (existing);
if (mk & MK_tmpl_decl_mask)
{
if (mk & MK_tmpl_alias_mask)
/* It should be in both tables. */
gcc_checking_assert
(same_type_p (match_mergeable_specialization (false, entry),
TREE_TYPE (existing)));
if (mk & MK_tmpl_tmpl_mask)
existing = DECL_TI_TEMPLATE (existing);
}
Expand Down Expand Up @@ -12716,17 +12691,12 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
bindings. */
*slot = dep = make_entity (decl, ek, has_def);

if (TREE_CODE (decl) == TEMPLATE_DECL)
{
if (DECL_ALIAS_TEMPLATE_P (decl) && DECL_TEMPLATE_INFO (decl))
dep->set_flag_bit<DB_ALIAS_TMPL_INST_BIT> ();
else if (CHECKING_P)
/* The template_result should otherwise not be in the
table, or be an empty redirect (created above). */
if (auto *eslot = entity_slot (DECL_TEMPLATE_RESULT (decl), false))
gcc_checking_assert ((*eslot)->get_entity_kind () == EK_REDIRECT
&& !(*eslot)->deps.length ());
}
if (CHECKING_P && TREE_CODE (decl) == TEMPLATE_DECL)
/* The template_result should otherwise not be in the
table, or be an empty redirect (created above). */
if (auto *eslot = entity_slot (DECL_TEMPLATE_RESULT (decl), false))
gcc_checking_assert ((*eslot)->get_entity_kind () == EK_REDIRECT
&& !(*eslot)->deps.length ());

if (ek != EK_USING)
{
Expand Down Expand Up @@ -13114,16 +13084,10 @@ specialization_add (bool decl_p, spec_entry *entry, void *data_)
heuristic. We don't attempt to replicate that algorithm, but
observe its behaviour and reproduce it upon read back. */

gcc_checking_assert (DECL_ALIAS_TEMPLATE_P (entry->tmpl)
|| TREE_CODE (entry->spec) == ENUMERAL_TYPE
gcc_checking_assert (TREE_CODE (entry->spec) == ENUMERAL_TYPE
|| 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). */
gcc_checking_assert
(!match_mergeable_specialization (true, entry)
== !DECL_ALIAS_TEMPLATE_P (entry->tmpl));
gcc_checking_assert (!match_mergeable_specialization (true, entry));
}
else if (VAR_OR_FUNCTION_DECL_P (entry->spec))
gcc_checking_assert (!DECL_LOCAL_DECL_P (entry->spec));
Expand Down Expand Up @@ -13179,21 +13143,14 @@ depset::hash::add_specializations (bool decl_p)
spec_entry *entry = data.pop ();
tree spec = entry->spec;
int use_tpl = 0;
bool is_alias = false;
bool is_friend = false;

if (decl_p && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (entry->tmpl))
/* A friend of a template. This is keyed to the
instantiation. */
is_friend = true;

if (!decl_p && DECL_ALIAS_TEMPLATE_P (entry->tmpl))
{
spec = TYPE_NAME (spec);
is_alias = true;
}

if (decl_p || is_alias)
if (decl_p)
{
if (tree ti = DECL_TEMPLATE_INFO (spec))
{
Expand Down Expand Up @@ -13278,20 +13235,9 @@ depset::hash::add_specializations (bool decl_p)
gcc_checking_assert (!TREE_VISITED (spec));
depset *dep = make_dependency (spec, depset::EK_SPECIALIZATION);
if (dep->is_special ())
{
/* An already located specialization, this must be the TYPE
corresponding to an alias_decl we found in the decl
table. */
spec_entry *other = reinterpret_cast <spec_entry *> (dep->deps[0]);
gcc_checking_assert (!decl_p && is_alias && !dep->is_type_spec ());
gcc_checking_assert (other->tmpl == entry->tmpl
&& template_args_equal (other->args, entry->args)
&& TREE_TYPE (other->spec) == entry->spec);
dep->set_flag_bit<DB_ALIAS_SPEC_BIT> ();
}
gcc_unreachable ();
else
{
gcc_checking_assert (decl_p || !is_alias);
if (dep->get_entity_kind () == depset::EK_REDIRECT)
dep = dep->deps[0];
else if (dep->get_entity_kind () == depset::EK_SPECIALIZATION)
Expand Down
84 changes: 34 additions & 50 deletions gcc/cp/pt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10055,6 +10055,32 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
/* Now we should have enough arguments. */
gcc_assert (parm_depth == arg_depth);

if (DECL_ALIAS_TEMPLATE_P (gen_tmpl))
{
/* The user referred to a specialization of an alias
template represented by GEN_TMPL.

[temp.alias]/2 says:

When a template-id refers to the specialization of an
alias template, it is equivalent to the associated
type obtained by substitution of its
template-arguments for the template-parameters in the
type-id of the alias template. */

t = instantiate_alias_template (gen_tmpl, arglist, complain);
/* Note that the call above (by indirectly calling
register_specialization in tsubst_decl) registers the
TYPE_DECL representing the specialization of the alias
template. So next time someone substitutes ARGLIST for
the template parms into the alias template (GEN_TMPL),
she'll get that TYPE_DECL back. */

if (t == error_mark_node)
return error_mark_node;
return TREE_TYPE (t);
}

/* From here on, we're only interested in the most general
template. */

Expand Down Expand Up @@ -10120,7 +10146,6 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
lookup. This prevents redundant checks on previously
instantiated specializations. */
if (flag_concepts
&& !DECL_ALIAS_TEMPLATE_P (gen_tmpl)
&& !constraints_satisfied_p (gen_tmpl, arglist))
{
if (complain & tf_error)
Expand Down Expand Up @@ -10189,31 +10214,7 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
context = global_namespace;

/* Create the type. */
if (DECL_ALIAS_TEMPLATE_P (gen_tmpl))
{
/* The user referred to a specialization of an alias
template represented by GEN_TMPL.

[temp.alias]/2 says:

When a template-id refers to the specialization of an
alias template, it is equivalent to the associated
type obtained by substitution of its
template-arguments for the template-parameters in the
type-id of the alias template. */

t = tsubst (TREE_TYPE (gen_tmpl), arglist, complain, in_decl);
/* Note that the call above (by indirectly calling
register_specialization in tsubst_decl) registers the
TYPE_DECL representing the specialization of the alias
template. So next time someone substitutes ARGLIST for
the template parms into the alias template (GEN_TMPL),
she'll get that TYPE_DECL back. */

if (t == error_mark_node)
return t;
}
else if (TREE_CODE (template_type) == ENUMERAL_TYPE)
if (TREE_CODE (template_type) == ENUMERAL_TYPE)
{
if (!is_dependent_type)
{
Expand Down Expand Up @@ -10301,8 +10302,7 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
}
}

if (OVERLOAD_TYPE_P (t)
&& !DECL_ALIAS_TEMPLATE_P (gen_tmpl))
if (OVERLOAD_TYPE_P (t))
{
static const char *tags[] = {"abi_tag", "may_alias"};

Expand Down Expand Up @@ -10369,7 +10369,7 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
{
TREE_VEC_LENGTH (arglist)--;
++processing_template_decl;
tree tinfo = TYPE_TEMPLATE_INFO_MAYBE_ALIAS (TREE_TYPE (gen_tmpl));
tree tinfo = TYPE_TEMPLATE_INFO (TREE_TYPE (gen_tmpl));
tree partial_inst_args =
tsubst (INNERMOST_TEMPLATE_ARGS (TI_ARGS (tinfo)),
arglist, complain, NULL_TREE);
Expand Down Expand Up @@ -10407,17 +10407,9 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
TEMPLATE_PARM_LEVEL. */
found = tsubst (gen_tmpl, arglist, tf_none, NULL_TREE);
TREE_VEC_LENGTH (arglist)++;
/* FOUND is either a proper class type, or an alias
template specialization. In the later case, it's a
TYPE_DECL, resulting from the substituting of arguments
for parameters in the TYPE_DECL of the alias template
done earlier. So be careful while getting the template
of FOUND. */
found = (TREE_CODE (found) == TEMPLATE_DECL
? found
: (TREE_CODE (found) == TYPE_DECL
? DECL_TI_TEMPLATE (found)
: CLASSTYPE_TI_TEMPLATE (found)));
: CLASSTYPE_TI_TEMPLATE (found));

if (DECL_CLASS_TEMPLATE_P (found)
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (found)))
Expand Down Expand Up @@ -10447,8 +10439,7 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context,
DECL_TEMPLATE_INSTANTIATIONS (found));

if (TREE_CODE (template_type) == ENUMERAL_TYPE
&& !uses_template_parms (current_nonlambda_scope ())
&& !DECL_ALIAS_TEMPLATE_P (gen_tmpl))
&& !uses_template_parms (current_nonlambda_scope ()))
/* Now that the type has been registered on the instantiations
list, we set up the enumerators. Because the enumeration
constants may involve the enumeration type itself, we make
Expand Down Expand Up @@ -31578,8 +31569,8 @@ get_mergeable_specialization_flags (tree tmpl, tree decl)
get_mergeable_specialization_flags. */

void
add_mergeable_specialization (bool decl_p, bool alias_p, spec_entry *elt,
tree decl, unsigned flags)
add_mergeable_specialization (bool decl_p, spec_entry *elt, tree decl,
unsigned flags)
{
hashval_t hash = spec_hasher::hash (elt);
if (decl_p)
Expand All @@ -31590,15 +31581,8 @@ add_mergeable_specialization (bool decl_p, bool alias_p, spec_entry *elt,
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)
else
{
auto *slot = type_specializations->find_slot_with_hash (elt, hash, INSERT);

Expand Down
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/pr99425-1_b.H
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ 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:-[0-9]*'s named merge key \(matched\) template_decl:'::template ssize'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s decl 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:-[0-9]*'s named merge key \(matched\) template_decl:'::template ssize'} module } }

2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/tpl-alias-1_a.H
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
// { dg-final { scan-lang-dump {Writing decl tmpl spec:-[0-9]* template_decl:'::allocator_traits<::allocator<long int>>::template rebind_alloc<_Up>'} module } }
// { dg-final { scan-lang-dump {Writing decl tmpl spec:-[0-9]* type_decl:'::allocator_traits<::allocator<long int>>::template rebind_alloc<_Up>'} module } }

// { dg-final { scan-lang-dump {Writing:-[0-9]*'s alias spec merge key \(specialization\) type_decl:'::allocator_traits<::allocator<long int>>::rebind_alloc<long int>'} module } }
// { dg-final { scan-lang-dump {Writing:-[0-9]*'s decl spec merge key \(specialization\) type_decl:'::allocator_traits<::allocator<long int>>::rebind_alloc<long int>'} module } }
15 changes: 15 additions & 0 deletions gcc/testsuite/g++.dg/modules/tpl-alias-2_a.H
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// PR c++/103994
// { dg-additional-options -fmodule-header }
// { dg-module-cmi {} }

template<class>
struct A {
template<class> using key_arg = int;
};

struct B {
template<class T>
void f() {
using type = A<B>::key_arg<T>;
}
};
9 changes: 9 additions & 0 deletions gcc/testsuite/g++.dg/modules/tpl-alias-2_b.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// PR c++/103994
// { dg-additional-options -fmodules-ts }

import "tpl-alias-2_a.H";

int main() {
B b;
b.f<int>();
}

0 comments on commit f5c1224

Please sign in to comment.