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