On 3/22/22 14:31, Patrick Palka wrote:
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?

Or cxx_eval_call_expression, but just as a workaround: manifestly-constant-evaluated expressions are evaluated even in an unevaluated operand, so I think adjusting cp_unevaluated_operand is correct.

Perhaps tsubst_template_args should use cp_evaluated, and places that use plain tsubst for substituting template args should use it instead?

Jason

Reply via email to