Here we're neglecting to clear cp_unevaluated_operand when substituting into the arguments of the alias template-id skip<(T(), 0), T> with T=A, which means cp_unevaluated_operand remains set during mark_used for A::A() and so we never synthesize it. Later constant evaluation for the substituted template argument (A(), 0) (during coerce_template_parms) fails with "'constexpr A::A()' used before its definition" since it was never synthesized.
This minimal patch makes us clear cp_unevaluated_operand when substituting into the template arguments of an alias template-id, as in tsubst_aggr_type. (A few lines below we also substitute into the template arguments of a class-scope typedef, during which we arguably also should clear cp_unevaluated_operand, but I wasn't able to come up with a testcase for which this mattered, because if we've named a class-scope typedef, then at some point tsubst_aggr_type must have already substituted the class scope, which performs the same substitution with cp_unevaluated_operand cleared.) Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? PR c++/101906 gcc/cp/ChangeLog: * pt.cc (tsubst): Clear cp_unevaluated_operand when substituting the template arguments of an alias template specialization. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/alias-decl-75.C: New test. * g++.dg/cpp0x/alias-decl-75a.C: New test. --- gcc/cp/pt.cc | 6 +++++- gcc/testsuite/g++.dg/cpp0x/alias-decl-75.C | 15 +++++++++++++++ gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-75.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 7697615ac64..c7116849551 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -15545,7 +15545,11 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) /* DECL represents an alias template and we want to instantiate it. */ tree tmpl = most_general_template (DECL_TI_TEMPLATE (decl)); - tree gen_args = tsubst (DECL_TI_ARGS (decl), args, complain, in_decl); + tree gen_args; + { + cp_evaluated ev; + gen_args = tsubst (DECL_TI_ARGS (decl), args, complain, in_decl); + } r = instantiate_alias_template (tmpl, gen_args, complain); } else if (DECL_CLASS_SCOPE_P (decl) diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-75.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-75.C new file mode 100644 index 00000000000..c6176751283 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-75.C @@ -0,0 +1,15 @@ +// PR c++/101906 +// { dg-do compile { target c++11 } } + +template<int, class T> using skip = T; + +template<class T> +constexpr unsigned sizeof_() { + return sizeof(skip<(T(), 0), T>); +} + +struct A { + int m = -1; +}; + +static_assert(sizeof_<A>() == sizeof(A), ""); diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C new file mode 100644 index 00000000000..ce08a84f6d9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-75a.C @@ -0,0 +1,16 @@ +// PR c++/101906 +// Similar to alias-decl-75.C, but where the unevaluated context is a +// constraint instead of sizeof. +// { dg-do compile { target c++20 } } + +template<int> using voidify = void; + +template<class T> +concept constant_value_initializable + = requires { typename voidify<(T(), 0)>; }; + +struct A { + int m = -1; +}; + +static_assert(constant_value_initializable<A>); -- 2.35.1.607.gf01e51a7cf