diff options
author | Patrick Palka <ppalka@redhat.com> | 2022-06-07 14:19:53 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2022-09-12 17:05:34 -0400 |
commit | c3ba0eaaa223f7b8208d279e3f39ff134912f9e9 (patch) | |
tree | 7053a8ee00c8cae538a5c5a6f7d7559482972224 /gcc | |
parent | 03381beccb52c0e2c15da3b8b8dfa3bb6eb71df9 (diff) | |
download | gcc-c3ba0eaaa223f7b8208d279e3f39ff134912f9e9.zip gcc-c3ba0eaaa223f7b8208d279e3f39ff134912f9e9.tar.gz gcc-c3ba0eaaa223f7b8208d279e3f39ff134912f9e9.tar.bz2 |
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 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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/pt.cc | 6 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/evaluated1.C | 17 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/evaluated1a.C | 16 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/evaluated1b.C | 17 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/template/evaluated1c.C | 17 |
5 files changed, 70 insertions, 3 deletions
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 31e3e39..4c6b343 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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 index 0000000..41845c6 --- /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 0000000..7828687 --- /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 0000000..7994065 --- /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 0000000..15c5582 --- /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), ""); |