public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [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).