]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: template-id arguments are evaluated [PR101906]
authorPatrick Palka <ppalka@redhat.com>
Tue, 7 Jun 2022 18:19:53 +0000 (14:19 -0400)
committerPatrick Palka <ppalka@redhat.com>
Mon, 12 Sep 2022 21:05:34 +0000 (17:05 -0400)
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 don't synthesize it.  Later constant evaluation for
the substituted template argument '(A(), 0)' (from 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 into any set
of template arguments.  To that end this patch makes us clear it 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
gcc/testsuite/g++.dg/template/evaluated1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/evaluated1a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/evaluated1b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/evaluated1c.C [new file with mode: 0644]

index 31e3e3910989b3678c55a556cfb763d216b30778..4c6b343ab6ea69bb77c6f8bd5f322c087fac3818 100644 (file)
@@ -13616,6 +13616,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;
+
   const int len = TREE_VEC_LENGTH (t);
   tree *elts = XALLOCAVEC (tree, len);
   int expanded_len_adjust = 0;
@@ -13888,9 +13891,6 @@ tsubst_aggr_type (tree t,
          tree argvec;
          tree r;
 
-         /* In "sizeof(X<I>)" we need to evaluate "I".  */
-         cp_evaluated ev;
-
          /* Figure out what arguments are appropriate for the
             type we are trying to find.  For example, given:
 
diff --git a/gcc/testsuite/g++.dg/template/evaluated1.C b/gcc/testsuite/g++.dg/template/evaluated1.C
new file mode 100644 (file)
index 0000000..41845c6
--- /dev/null
@@ -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 (file)
index 0000000..7828687
--- /dev/null
@@ -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 (file)
index 0000000..7994065
--- /dev/null
@@ -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 (file)
index 0000000..15c5582
--- /dev/null
@@ -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), "");