I wasn't sure whether I should include the ambiguity checking logic from process_partial_specialization; we don't do this anywhere else in the modules handling code that I could see so I left it out for now.
I could also rework process_partial_specialization to call the new create_mergeable_partial_spec function if you prefer, to reduce code duplication, though the early exit for VAR_DECL and the call to associate_classtype_constraints that relies on global state complicated it enough I didn't bother for this patch. Finally, I'm not entirely sure whether the choice of DECL_TEMPLATE_PARMS that I'm providing is correct, especially considering this hunk: /* Give template template parms a DECL_CONTEXT of the template for which they are a parameter. */ for (i = 0; i < ntparms; ++i) { tree parm = TREE_VALUE (TREE_VEC_ELT (inner_parms, i)); if (TREE_CODE (parm) == TEMPLATE_DECL) DECL_CONTEXT (parm) = tmpl; } I haven't been able to produce any testcase that relies on this context setting to cause a failure, though, and there doesn't seem to be one in the testsuite for the matching hunk in process_partial_specialization. Any hints on where this might be needed? Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 -- In some cases, when we go to import a partial specialisation there might already be an incomplete implicit instantiation in the specialisation table. This causes ICEs described in the linked PR as we now have two separate matching specialisations for this same arguments. This issue doesn't appear to happen for variable templates as it doesn't appear that we can create incomplete specialisations in the same way. This patch attempts to solve the issue by retrofitting the existing implicit instantiation as a partial specialisation (similarly to how maybe_process_partial_specialization operates) and then relying on the existing merging logic to fill in the definition of the type. This works because for types almost all relevant details are streamed and merged in 'read_class_def'. As an optimisation, we also mark the newly retrofitted specialisation as imported, since there isn't any useful information on the declaration that will be provided from this TU anyway, and so we don't need to write the decl into our own BMI but can instead just seed it as an import. PR c++/113814 gcc/cp/ChangeLog: * cp-tree.h (create_mergeable_partial_spec): * module.cc (trees_in::key_mergeable): * pt.cc (create_mergeable_partial_spec): gcc/testsuite/ChangeLog: * g++.dg/modules/partial-6.h: New test. * g++.dg/modules/partial-6_a.H: New test. * g++.dg/modules/partial-6_b.H: New test. * g++.dg/modules/partial-6_c.C: New test. Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> --- gcc/cp/cp-tree.h | 1 + gcc/cp/module.cc | 26 +++++++++++ gcc/cp/pt.cc | 50 ++++++++++++++++++++++ gcc/testsuite/g++.dg/modules/partial-6.h | 7 +++ gcc/testsuite/g++.dg/modules/partial-6_a.H | 11 +++++ gcc/testsuite/g++.dg/modules/partial-6_b.H | 23 ++++++++++ gcc/testsuite/g++.dg/modules/partial-6_c.C | 12 ++++++ 7 files changed, 130 insertions(+) create mode 100644 gcc/testsuite/g++.dg/modules/partial-6.h create mode 100644 gcc/testsuite/g++.dg/modules/partial-6_a.H create mode 100644 gcc/testsuite/g++.dg/modules/partial-6_b.H create mode 100644 gcc/testsuite/g++.dg/modules/partial-6_c.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a44100a2bc4..eda287df84e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7590,6 +7590,7 @@ extern bool is_specialization_of_friend (tree, tree); extern bool comp_template_args (tree, tree, tree * = NULL, tree * = NULL); extern int template_args_equal (tree, tree); +extern tree create_mergeable_partial_spec (tree, tree, tree); extern tree maybe_process_partial_specialization (tree); extern tree most_specialized_instantiation (tree); extern tree most_specialized_partial_spec (tree, tsubst_flags_t, bool = false); diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index e8c9a876903..bb166ee38f3 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -11296,6 +11296,32 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, break; } } + + if (!existing && type && CLASS_TYPE_P (type)) + { + spec_entry spec; + spec.tmpl = key.ret; + spec.args = key.args; + spec.spec = type; + + tree inst = match_mergeable_specialization (false, &spec); + if (inst + && CLASSTYPE_IMPLICIT_INSTANTIATION (inst) + && !COMPLETE_TYPE_P (inst)) + { + /* There's an existing implicit instantiation of this partial + specialization. We'll retrofit it as a partial spec and + then load over it. */ + existing = create_mergeable_partial_spec + (TYPE_NAME (inst), DECL_TEMPLATE_PARMS (decl), + key.constraints); + + /* Despite matching an existing declaration, this is actually + an import; mark it as such so we don't need to write it + when exporting from this module. */ + DECL_MODULE_IMPORT_P (TYPE_NAME (inst)) = true; + } + } } else if (mk == MK_keyed && DECL_LANG_SPECIFIC (name) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 0141c53b617..4b32d85890b 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -5477,6 +5477,56 @@ process_partial_specialization (tree decl) return decl; } +/* DECL is an implicit instantiation of a template that we're + streaming a matching partial specialization for. Build and + return a new TEMPLATE_DECL for the the type and prepare it + as with process_partial_specialization. */ + +tree +create_mergeable_partial_spec (tree decl, tree parms, tree constraints) +{ + tree type = TREE_TYPE (decl); + tree tinfo = get_template_info (decl); + tree maintmpl = TI_TEMPLATE (tinfo); + tree specargs = TI_ARGS (tinfo); + + gcc_checking_assert (TREE_CODE (decl) == TYPE_DECL + && CLASSTYPE_IMPLICIT_INSTANTIATION (type) + && !COMPLETE_TYPE_P (type)); + SET_CLASSTYPE_TEMPLATE_SPECIALIZATION (type); + + tree tmpl = build_template_decl (decl, parms, + DECL_MEMBER_TEMPLATE_P (maintmpl)); + SET_DECL_TEMPLATE_SPECIALIZATION (tmpl); + DECL_TEMPLATE_INFO (tmpl) = build_template_info (maintmpl, specargs); + DECL_PRIMARY_TEMPLATE (tmpl) = maintmpl; + + /* Give template template parms a DECL_CONTEXT of the template + for which they are a parameter. */ + tree inner_parms = INNERMOST_TEMPLATE_PARMS (parms); + int ntparms = TREE_VEC_LENGTH (inner_parms); + for (int i = 0; i < ntparms; ++i) + { + tree parm = TREE_VALUE (TREE_VEC_ELT (inner_parms, i)); + if (TREE_CODE (parm) == TEMPLATE_DECL) + DECL_CONTEXT (parm) = tmpl; + } + + set_constraints (decl, constraints); + + DECL_TEMPLATE_SPECIALIZATIONS (maintmpl) + = tree_cons (specargs, tmpl, + DECL_TEMPLATE_SPECIALIZATIONS (maintmpl)); + TREE_TYPE (DECL_TEMPLATE_SPECIALIZATIONS (maintmpl)) = type; + /* Link the DECL_TEMPLATE_RESULT back to the partial TEMPLATE_DECL. */ + gcc_checking_assert (!TI_PARTIAL_INFO (tinfo)); + TI_PARTIAL_INFO (tinfo) = build_template_info (tmpl, NULL_TREE); + + set_defining_module_for_partial_spec (decl); + + return tmpl; +} + /* PARM is a template parameter of some form; return the corresponding TEMPLATE_PARM_INDEX. */ diff --git a/gcc/testsuite/g++.dg/modules/partial-6.h b/gcc/testsuite/g++.dg/modules/partial-6.h new file mode 100644 index 00000000000..702c9a1b7ec --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/partial-6.h @@ -0,0 +1,7 @@ +// PR c++/113814 + +template <typename> struct A {}; +template <typename T> A<T*> f(); + +template <template <typename> typename, typename> struct B; +template <template <typename> typename TT> B<TT, int> g(); diff --git a/gcc/testsuite/g++.dg/modules/partial-6_a.H b/gcc/testsuite/g++.dg/modules/partial-6_a.H new file mode 100644 index 00000000000..6e0d5ddcaf0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/partial-6_a.H @@ -0,0 +1,11 @@ +// PR c++/113814 +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +#include "partial-6.h" + +template <typename T> +struct A<T*> { int a; }; + +template <template <typename> typename TT> +struct B<TT, int> { int b; }; diff --git a/gcc/testsuite/g++.dg/modules/partial-6_b.H b/gcc/testsuite/g++.dg/modules/partial-6_b.H new file mode 100644 index 00000000000..e9b75c35668 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/partial-6_b.H @@ -0,0 +1,23 @@ +// PR c++/113814 +// { dg-additional-options "-fmodules-ts -fdump-lang-module-alias" } +// { dg-module-cmi {} } + +#include "partial-6.h" +import "partial-6_a.H"; + +template <typename, typename = void> +struct TestTTP; + +inline void test() { + int a = f<int>().a; + int b = g<TestTTP>().b; +} + +// Dedup the partial specialisations against the existing implicit instantiation +// { dg-final { scan-lang-dump {Read:-[0-9]*'s partial merge key \(matched\) template_decl:'::template A'} module } } +// { dg-final { scan-lang-dump {Read:-[0-9]*'s partial merge key \(matched\) template_decl:'::template B'} module } } + +// Don't need to write the partial specialisations, they are entirely newly imported +// (but just matched against the implicit instantiation) +// { dg-final { scan-lang-dump-not {Wrote declaration entity:[0-9]* template_decl:'::template A<#null#>'} module } } +// { dg-final { scan-lang-dump-not {Wrote declaration entity:[0-9]* template_decl:'::template B<template TT,int>'} module } } diff --git a/gcc/testsuite/g++.dg/modules/partial-6_c.C b/gcc/testsuite/g++.dg/modules/partial-6_c.C new file mode 100644 index 00000000000..2a34457f5af --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/partial-6_c.C @@ -0,0 +1,12 @@ +// PR c++/113814 +// { dg-additional-options "-fmodules-ts" } + +import "partial-6_b.H"; + +template <typename> +struct TestTTP2; + +int main() { + int a = f<double>().a; + int b = g<TestTTP2>().b; +} -- 2.46.0