* [PATCH] c++: alias template arguments are evaluated [PR101906]
@ 2022-03-22 17:18 Patrick Palka
2022-03-22 18:31 ` Patrick Palka
0 siblings, 1 reply; 5+ messages in thread
From: Patrick Palka @ 2022-03-22 17:18 UTC (permalink / raw)
To: gcc-patches
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
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] c++: alias template arguments are evaluated [PR101906]
2022-03-22 17:18 [PATCH] c++: alias template arguments are evaluated [PR101906] Patrick Palka
@ 2022-03-22 18:31 ` Patrick Palka
2022-03-23 20:11 ` Jason Merrill
0 siblings, 1 reply; 5+ messages in thread
From: Patrick Palka @ 2022-03-22 18:31 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches, jason
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
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] c++: alias template arguments are evaluated [PR101906]
2022-03-22 18:31 ` Patrick Palka
@ 2022-03-23 20:11 ` Jason Merrill
2022-06-07 18:25 ` Patrick Palka
0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2022-03-23 20:11 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches
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
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] c++: alias template arguments are evaluated [PR101906]
2022-03-23 20:11 ` Jason Merrill
@ 2022-06-07 18:25 ` Patrick Palka
2022-06-07 20:11 ` Jason Merrill
0 siblings, 1 reply; 5+ messages in thread
From: Patrick Palka @ 2022-06-07 18:25 UTC (permalink / raw)
To: Jason Merrill; +Cc: Patrick Palka, gcc-patches
On Wed, 23 Mar 2022, Jason Merrill wrote:
> 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,
Makes sense.
> and places that use plain tsubst for substituting template args should
> use it instead?
Even though tsubst already uses tsubst_template_args to substitute
TREE_VEC? AFAICT this change would have no effect except when
args is NULL_TREE, in which case tsubst exits early but
tsubst_template_args doesn't.
Here's what I have so far, which survives bootstrap and regtest.
-- >8 --
Subject: [PATCH] c++: template-id arguments are evaluated [PR101906]
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 doesn't happen with a class template because tsubst_aggr_type
clears cp_unevaluated_operand during substitution thereof. But
since template arguments are generally manifestly constant-evaluated,
which in turn are evaluated even in an unevaluated operand, we
should be clearing cp_unevaluated_operand more broadly whenever
substituting any set of template arguments. Thus this patch makes us
clear cp_unevaluated_operand during tsubst_template_args.
PR c++/101906
gcc/cp/ChangeLog:
* pt.cc (tsubst_template_args): Set cp_evaluated here.
(tsubst_aggr_type): Not here.
gcc/testsuite/ChangeLog:
* g++.dg/template/evaluated1.C: New test.
* g++.dg/template/evaluated1a.C: New test.
* g++.dg/template/evaluated1b.C: New test.
* g++.dg/template/evaluated1c.C: New test.
---
gcc/cp/pt.cc | 6 +++---
gcc/testsuite/g++.dg/template/evaluated1.C | 17 +++++++++++++++++
gcc/testsuite/g++.dg/template/evaluated1a.C | 16 ++++++++++++++++
gcc/testsuite/g++.dg/template/evaluated1b.C | 17 +++++++++++++++++
gcc/testsuite/g++.dg/template/evaluated1c.C | 17 +++++++++++++++++
5 files changed, 70 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/template/evaluated1.C
create mode 100644 gcc/testsuite/g++.dg/template/evaluated1a.C
create mode 100644 gcc/testsuite/g++.dg/template/evaluated1b.C
create mode 100644 gcc/testsuite/g++.dg/template/evaluated1c.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ee7d2c935cc..7fe1c7653aa 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -13475,6 +13475,9 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (t == error_mark_node)
return error_mark_node;
+ /* In "sizeof(X<I>)" we need to evaluate "I". */
+ cp_evaluated ev;
+
len = TREE_VEC_LENGTH (t);
elts = XALLOCAVEC (tree, len);
@@ -13709,9 +13712,6 @@ tsubst_aggr_type (tree t,
tree context;
tree r;
- /* In "sizeof(X<I>)" we need to evaluate "I". */
- cp_evaluated ev;
-
/* First, determine the context for the type we are looking
up. */
context = TYPE_CONTEXT (t);
diff --git a/gcc/testsuite/g++.dg/template/evaluated1.C b/gcc/testsuite/g++.dg/template/evaluated1.C
new file mode 100644
index 00000000000..41845c65acb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/evaluated1.C
@@ -0,0 +1,17 @@
+// PR c++/101906
+// Verify the template arguments of an alias template-id are evaluated even
+// in an unevaluated context.
+// { 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/template/evaluated1a.C b/gcc/testsuite/g++.dg/template/evaluated1a.C
new file mode 100644
index 00000000000..78286871004
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/evaluated1a.C
@@ -0,0 +1,16 @@
+// PR c++/101906
+// Like unevaluated1.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>);
diff --git a/gcc/testsuite/g++.dg/template/evaluated1b.C b/gcc/testsuite/g++.dg/template/evaluated1b.C
new file mode 100644
index 00000000000..7994065ac86
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/evaluated1b.C
@@ -0,0 +1,17 @@
+// PR c++/101906
+// Like unevaluated1.C, but using a function template instead of an
+// alias template.
+// { dg-do compile { target c++14 } }
+
+template<int, class T> T skip();
+
+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/template/evaluated1c.C b/gcc/testsuite/g++.dg/template/evaluated1c.C
new file mode 100644
index 00000000000..15c55821c01
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/evaluated1c.C
@@ -0,0 +1,17 @@
+// PR c++/101906
+// Like unevaluated1b.C, but using a variable template instead of a
+// function template.
+// { dg-do compile { target c++14 } }
+
+template<int, class T> T skip;
+
+template<class T>
+constexpr unsigned sizeof_() {
+ return sizeof(skip<(T(), 0), T>);
+}
+
+struct A {
+ int m = -1;
+};
+
+static_assert(sizeof_<A>() == sizeof(A), "");
--
2.36.1.299.gab336e8f1c
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] c++: alias template arguments are evaluated [PR101906]
2022-06-07 18:25 ` Patrick Palka
@ 2022-06-07 20:11 ` Jason Merrill
0 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2022-06-07 20:11 UTC (permalink / raw)
To: Patrick Palka; +Cc: gcc-patches
On 6/7/22 14:25, Patrick Palka wrote:
> On Wed, 23 Mar 2022, Jason Merrill wrote:
>
>> 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,
>
> Makes sense.
>
>> and places that use plain tsubst for substituting template args should
>> use it instead?
>
> Even though tsubst already uses tsubst_template_args to substitute
> TREE_VEC? AFAICT this change would have no effect except when
> args is NULL_TREE, in which case tsubst exits early but
> tsubst_template_args doesn't.
Never mind, then.
> Here's what I have so far, which survives bootstrap and regtest.
OK.
> -- >8 --
>
> Subject: [PATCH] c++: template-id arguments are evaluated [PR101906]
>
> 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 doesn't happen with a class template because tsubst_aggr_type
> clears cp_unevaluated_operand during substitution thereof. But
> since template arguments are generally manifestly constant-evaluated,
> which in turn are evaluated even in an unevaluated operand, we
> should be clearing cp_unevaluated_operand more broadly whenever
> substituting any set of template arguments. Thus this patch makes us
> clear cp_unevaluated_operand during tsubst_template_args.
>
> PR c++/101906
>
> gcc/cp/ChangeLog:
>
> * pt.cc (tsubst_template_args): Set cp_evaluated here.
> (tsubst_aggr_type): Not here.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/template/evaluated1.C: New test.
> * g++.dg/template/evaluated1a.C: New test.
> * g++.dg/template/evaluated1b.C: New test.
> * g++.dg/template/evaluated1c.C: New test.
> ---
> gcc/cp/pt.cc | 6 +++---
> gcc/testsuite/g++.dg/template/evaluated1.C | 17 +++++++++++++++++
> gcc/testsuite/g++.dg/template/evaluated1a.C | 16 ++++++++++++++++
> gcc/testsuite/g++.dg/template/evaluated1b.C | 17 +++++++++++++++++
> gcc/testsuite/g++.dg/template/evaluated1c.C | 17 +++++++++++++++++
> 5 files changed, 70 insertions(+), 3 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/template/evaluated1.C
> create mode 100644 gcc/testsuite/g++.dg/template/evaluated1a.C
> create mode 100644 gcc/testsuite/g++.dg/template/evaluated1b.C
> create mode 100644 gcc/testsuite/g++.dg/template/evaluated1c.C
>
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index ee7d2c935cc..7fe1c7653aa 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -13475,6 +13475,9 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> if (t == error_mark_node)
> return error_mark_node;
>
> + /* In "sizeof(X<I>)" we need to evaluate "I". */
> + cp_evaluated ev;
> +
> len = TREE_VEC_LENGTH (t);
> elts = XALLOCAVEC (tree, len);
>
> @@ -13709,9 +13712,6 @@ tsubst_aggr_type (tree t,
> tree context;
> tree r;
>
> - /* In "sizeof(X<I>)" we need to evaluate "I". */
> - cp_evaluated ev;
> -
> /* First, determine the context for the type we are looking
> up. */
> context = TYPE_CONTEXT (t);
> diff --git a/gcc/testsuite/g++.dg/template/evaluated1.C b/gcc/testsuite/g++.dg/template/evaluated1.C
> new file mode 100644
> index 00000000000..41845c65acb
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/evaluated1.C
> @@ -0,0 +1,17 @@
> +// PR c++/101906
> +// Verify the template arguments of an alias template-id are evaluated even
> +// in an unevaluated context.
> +// { 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/template/evaluated1a.C b/gcc/testsuite/g++.dg/template/evaluated1a.C
> new file mode 100644
> index 00000000000..78286871004
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/evaluated1a.C
> @@ -0,0 +1,16 @@
> +// PR c++/101906
> +// Like unevaluated1.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>);
> diff --git a/gcc/testsuite/g++.dg/template/evaluated1b.C b/gcc/testsuite/g++.dg/template/evaluated1b.C
> new file mode 100644
> index 00000000000..7994065ac86
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/evaluated1b.C
> @@ -0,0 +1,17 @@
> +// PR c++/101906
> +// Like unevaluated1.C, but using a function template instead of an
> +// alias template.
> +// { dg-do compile { target c++14 } }
> +
> +template<int, class T> T skip();
> +
> +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/template/evaluated1c.C b/gcc/testsuite/g++.dg/template/evaluated1c.C
> new file mode 100644
> index 00000000000..15c55821c01
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/evaluated1c.C
> @@ -0,0 +1,17 @@
> +// PR c++/101906
> +// Like unevaluated1b.C, but using a variable template instead of a
> +// function template.
> +// { dg-do compile { target c++14 } }
> +
> +template<int, class T> T skip;
> +
> +template<class T>
> +constexpr unsigned sizeof_() {
> + return sizeof(skip<(T(), 0), T>);
> +}
> +
> +struct A {
> + int m = -1;
> +};
> +
> +static_assert(sizeof_<A>() == sizeof(A), "");
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-06-07 20:11 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-22 17:18 [PATCH] c++: alias template arguments are evaluated [PR101906] Patrick Palka
2022-03-22 18:31 ` Patrick Palka
2022-03-23 20:11 ` Jason Merrill
2022-06-07 18:25 ` Patrick Palka
2022-06-07 20:11 ` Jason Merrill
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).