On Tue, 22 Mar 2022, Patrick Palka wrote: > 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.
It occurred to me to check the case where 'skip' is a function/variable template instead of an alias template, and unfortunately seems we run into the same issue: template<int, class T> T skip(); // Function template // template<int, class T> T skip; // Variable template template<class T> constexpr unsigned sizeof_() { return sizeof(skip<(T(), 0), T>()); // return sizeof(skip<(T(), 0), T>); } struct A { int m = -1; }; static_assert(sizeof_<A>() == sizeof(A), ""); <stdin>: In instantiation of ‘constexpr unsigned int sizeof_() [with T = A]’: <stdin>:14:25: required from here <stdin>:6:34: error: ‘constexpr A::A()’ used before its definition We can fix this similarly by clearing cp_unevaluated_operand when substituting into the arguments of a TEMPLATE_ID_EXPR, but now I'm worried this cp_unevaluated_operand business might not be the best approach (despite it being consistent with what tsubst_aggr_type does). Maybe instantiate_cx_fn_r should be responsible for making sure A::A() gets 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 > >