c++ modules: partial variable template specializations [PR106826]

With partial variable template specializations, it looks like we
stream the VAR_DECL (i.e. the DECL_TEMPLATE_RESULT of the corresponding
TEMPLATE_DECL) since process_partial_specialization adds it to the
specializations table, but we end up never streaming the corresponding
TEMPLATE_DECL itself that's reachable only from the primary template's
DECL_TEMPLATE_SPECIALIZATIONS list, which leads to this list being
incomplete on stream-in.

The modules machinery already has special logic for streaming partial
specializations of class templates; this patch attempts to generalize
it to handle those of variable templates as well.

	PR c++/106826

gcc/cp/ChangeLog:

	* module.cc (trees_out::decl_value): Use get_template_info in
	the MK_partial case to handle both VAR_DECL and TYPE_DECL.
	(trees_out::key_mergeable): Likewise.
	(trees_in::key_mergeable): Likewise.
	(has_definition): Consider DECL_INITIAL of a partial variable
	template specialization.
	(depset:#️⃣:make_dependency): Handle partial variable template
	specializations too.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/partial-2_a.C: New test.
	* g++.dg/modules/partial-2_b.C: New test.
This commit is contained in:
Patrick Palka 2022-09-22 08:46:23 -04:00
parent 26607a63da
commit 32d8123cd6
3 changed files with 82 additions and 14 deletions

View file

@ -7789,8 +7789,9 @@ trees_out::decl_value (tree decl, depset *dep)
}
else
{
tree_node (CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner)));
tree_node (CLASSTYPE_TI_ARGS (TREE_TYPE (inner)));
tree ti = get_template_info (inner);
tree_node (TI_TEMPLATE (ti));
tree_node (TI_ARGS (ti));
}
}
tree_node (get_constraints (decl));
@ -10625,9 +10626,10 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
case MK_partial:
{
tree ti = get_template_info (inner);
key.constraints = get_constraints (inner);
key.ret = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner));
key.args = CLASSTYPE_TI_ARGS (TREE_TYPE (inner));
key.ret = TI_TEMPLATE (ti);
key.args = TI_ARGS (ti);
}
break;
}
@ -10866,8 +10868,8 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
spec; spec = TREE_CHAIN (spec))
{
tree tmpl = TREE_VALUE (spec);
if (template_args_equal (key.args,
CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)))
tree ti = get_template_info (tmpl);
if (template_args_equal (key.args, TI_ARGS (ti))
&& cp_tree_equal (key.constraints,
get_constraints
(DECL_TEMPLATE_RESULT (tmpl))))
@ -11381,8 +11383,7 @@ has_definition (tree decl)
case VAR_DECL:
if (DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_INFO (decl)
&& DECL_USE_TEMPLATE (decl) < 2)
&& DECL_TEMPLATE_INFO (decl))
return DECL_INITIAL (decl);
else
{
@ -12498,11 +12499,14 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
if (!dep)
{
if (DECL_IMPLICIT_TYPEDEF_P (decl)
/* ... not an enum, for instance. */
&& RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))
&& TYPE_LANG_SPECIFIC (TREE_TYPE (decl))
&& CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl)) == 2)
if ((DECL_IMPLICIT_TYPEDEF_P (decl)
/* ... not an enum, for instance. */
&& RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))
&& TYPE_LANG_SPECIFIC (TREE_TYPE (decl))
&& CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl)) == 2)
|| (VAR_P (decl)
&& DECL_LANG_SPECIFIC (decl)
&& DECL_USE_TEMPLATE (decl) == 2))
{
/* A partial or explicit specialization. Partial
specializations might not be in the hash table, because
@ -12515,7 +12519,7 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
dep_hash, and then convert the dep we just found into a
redirect. */
tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl));
tree ti = get_template_info (decl);
tree tmpl = TI_TEMPLATE (ti);
tree partial = NULL_TREE;
for (tree spec = DECL_TEMPLATE_SPECIALIZATIONS (tmpl);

View file

@ -0,0 +1,43 @@
// PR c++/106826
// { dg-additional-options -fmodules-ts }
// { dg-module-cmi pr106826 }
export module pr106826;
template<class T> constexpr bool is_reference_v = false;
template<class T> constexpr bool is_reference_v<T&> = true;
template<class T> constexpr bool is_reference_v<T&&> = true;
struct A {
template<class T> static constexpr bool is_reference_v = false;
};
template<class T> constexpr bool A::is_reference_v<T&> = true;
template<class T> constexpr bool A::is_reference_v<T&&> = true;
#if __cpp_concepts
namespace concepts {
template<class T> bool is_reference_v;
template<class T> requires __is_same(T, T&)
constexpr bool is_reference_v<T> = true;
template<class T> requires __is_same(T, T&&) && (!__is_same(T, T&))
constexpr bool is_reference_v<T> = true;
template<class T> requires (!__is_same(T, T&)) && (!__is_same(T, T&&))
constexpr bool is_reference_v<T> = false;
struct A {
template<class T> static bool is_reference_v;
};
template<class T> requires __is_same(T, T&)
constexpr bool A::is_reference_v<T> = true;
template<class T> requires __is_same(T, T&&) && (!__is_same(T, T&))
constexpr bool A::is_reference_v<T> = true;
template<class T> requires (!__is_same(T, T&)) && (!__is_same(T, T&&))
constexpr bool A::is_reference_v<T> = false;
}
#endif

View file

@ -0,0 +1,21 @@
// PR c++/106826
// { dg-additional-options -fmodules-ts }
module pr106826;
static_assert(is_reference_v<int&>);
static_assert(is_reference_v<int&&>);
static_assert(!is_reference_v<int>);
static_assert(A::is_reference_v<long&>);
static_assert(A::is_reference_v<long&&>);
static_assert(!A::is_reference_v<long>);
#if __cpp_concepts
static_assert(concepts::is_reference_v<char&>);
static_assert(concepts::is_reference_v<char&&>);
static_assert(!concepts::is_reference_v<char>);
static_assert(concepts::A::is_reference_v<bool&>);
static_assert(concepts::A::is_reference_v<bool&&>);
static_assert(!concepts::A::is_reference_v<bool>);
#endif