In the first testcase, coerce_template_template_parms was adding too much of outer_args when coercing to match P's template parameters, so that when substituting into the 'const T&' parameter we got an unrelated template argument for T. We should only add outer_args when the argument template is a nested template.
Tested x86_64-pc-linux-gnu, applying to trunk. PR c++/104107 PR c++/95036 gcc/cp/ChangeLog: * pt.cc (coerce_template_template_parms): Take full parms. Avoid adding too much of outer_args. (coerce_template_template_parm): Adjust. (template_template_parm_bindings_ok_p): Adjust. (convert_template_argument): Adjust. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/alias-decl-ttp2.C: New test. * g++.dg/cpp1z/ttp2.C: New test. --- gcc/cp/pt.cc | 41 +++++++++++++++----- gcc/testsuite/g++.dg/cpp0x/alias-decl-ttp2.C | 25 ++++++++++++ gcc/testsuite/g++.dg/cpp1z/ttp2.C | 21 ++++++++++ 3 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-ttp2.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/ttp2.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 1b18e2a7787..6dda66081bd 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -7731,8 +7731,8 @@ coerce_template_template_parm (tree parm, template <template <template <class> class> class TT> class C; */ { - tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm); - tree argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg); + tree parmparm = DECL_TEMPLATE_PARMS (parm); + tree argparm = DECL_TEMPLATE_PARMS (arg); if (!coerce_template_template_parms (parmparm, argparm, complain, in_decl, outer_args)) @@ -8001,8 +8001,8 @@ unify_bound_ttp_args (tree tparms, tree targs, tree parm, tree& arg, the parameters to A, and OUTER_ARGS contains A. */ static int -coerce_template_template_parms (tree parm_parms, - tree arg_parms, +coerce_template_template_parms (tree parm_parms_full, + tree arg_parms_full, tsubst_flags_t complain, tree in_decl, tree outer_args) @@ -8011,6 +8011,9 @@ coerce_template_template_parms (tree parm_parms, tree parm, arg; int variadic_p = 0; + tree parm_parms = INNERMOST_TEMPLATE_PARMS (parm_parms_full); + tree arg_parms = INNERMOST_TEMPLATE_PARMS (arg_parms_full); + gcc_assert (TREE_CODE (parm_parms) == TREE_VEC); gcc_assert (TREE_CODE (arg_parms) == TREE_VEC); @@ -8046,8 +8049,26 @@ coerce_template_template_parms (tree parm_parms, specialized as P, so they match.*/ processing_template_decl_sentinel ptds (/*reset*/false); ++processing_template_decl; + tree pargs = template_parms_level_to_args (parm_parms); - pargs = add_outermost_template_args (outer_args, pargs); + + /* PARM, and thus the context in which we are passing ARG to it, may be + at a deeper level than ARG; when trying to coerce to ARG_PARMS, we + want to provide the right number of levels, so we reduce the number of + levels in OUTER_ARGS before prepending them. This is most important + when ARG is a namespace-scope template, as in alias-decl-ttp2.C. + + ARG might also be deeper than PARM (ttp23). In that case, we include + all of OUTER_ARGS. The missing levels seem potentially problematic, + but I can't come up with a testcase that breaks. */ + if (int arg_outer_levs = TMPL_PARMS_DEPTH (arg_parms_full) - 1) + { + auto x = make_temp_override (TREE_VEC_LENGTH (outer_args)); + if (TMPL_ARGS_DEPTH (outer_args) > arg_outer_levs) + TREE_VEC_LENGTH (outer_args) = arg_outer_levs; + pargs = add_to_template_args (outer_args, pargs); + } + pargs = coerce_template_parms (arg_parms, pargs, NULL_TREE, tf_none, /*require_all*/true, /*use_default*/true); if (pargs != error_mark_node) @@ -8186,16 +8207,16 @@ template_template_parm_bindings_ok_p (tree tparms, tree targs) /* Extract the template parameters from the template argument. */ if (TREE_CODE (targ) == TEMPLATE_DECL) - targ_parms = DECL_INNERMOST_TEMPLATE_PARMS (targ); + targ_parms = DECL_TEMPLATE_PARMS (targ); else if (TREE_CODE (targ) == TEMPLATE_TEMPLATE_PARM) - targ_parms = DECL_INNERMOST_TEMPLATE_PARMS (TYPE_NAME (targ)); + targ_parms = DECL_TEMPLATE_PARMS (TYPE_NAME (targ)); /* Verify that we can coerce the template template parameters from the template argument to the template parameter. This requires an exact match. */ if (targ_parms && !coerce_template_template_parms - (DECL_INNERMOST_TEMPLATE_PARMS (tparm), + (DECL_TEMPLATE_PARMS (tparm), targ_parms, tf_none, tparm, @@ -8489,13 +8510,13 @@ convert_template_argument (tree parm, val = orig_arg; else { - tree parmparm = DECL_INNERMOST_TEMPLATE_PARMS (parm); + tree parmparm = DECL_TEMPLATE_PARMS (parm); tree argparm; /* Strip alias templates that are equivalent to another template. */ arg = get_underlying_template (arg); - argparm = DECL_INNERMOST_TEMPLATE_PARMS (arg); + argparm = DECL_TEMPLATE_PARMS (arg); if (coerce_template_template_parms (parmparm, argparm, complain, in_decl, diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-ttp2.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-ttp2.C new file mode 100644 index 00000000000..230b277a503 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-ttp2.C @@ -0,0 +1,25 @@ +// PR c++/104107 +// { dg-do compile { target c++11 } } + +template<int targ_id, typename t_parameter, + template<typename T, const T&> class tt_w_data> +struct tt_main { + static t_parameter m_parameter; + template<template<typename T, const T&> class t_data> + using t_make = t_data<t_parameter, m_parameter>; + using t_data = t_make<tt_w_data>; +}; + +template<int targ_id, typename t_parameter, + template<typename T, const T&> class tt_w_data> +t_parameter tt_main<targ_id, t_parameter, tt_w_data>::m_parameter; + +template<typename T, const T&> struct P {}; +struct t_parameter {}; + +using toto = tt_main<0, t_parameter, P>; + +int main() { + toto t; + return 0; +} diff --git a/gcc/testsuite/g++.dg/cpp1z/ttp2.C b/gcc/testsuite/g++.dg/cpp1z/ttp2.C new file mode 100644 index 00000000000..1f161e5c9ae --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/ttp2.C @@ -0,0 +1,21 @@ +// PR c++/95036 +// { dg-do compile { target c++17 } } + +namespace a { +template <int b> struct c { static constexpr int d = b; }; +template <bool, typename e = void> using f = e; +template <typename e, e...> struct g; +template <typename> struct array; +} // namespace a +template <template <class> class h> struct i { + template <template <class, auto...> class, class = void> struct n; + template <class j> struct n<h, j> : a::c<true> {}; + template <template <class> class k, class = a::f<n<k>::d>> void function(); +}; +template <template <class> class... l> struct derived : i<l>... { + using i<l>::function...; +}; +int main() { + derived<a::array, a::g> m; + m.function<a::array>(); +} base-commit: f320197c8b495324dc6997a99d53e7f45ecf5840 -- 2.27.0