Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested
against the cmcstl2 project. Does this look OK to commit?
gcc/cp/ChangeLog:
PR c++/96409
PR c++/96410
* constraint.cc (tsubst_compound_requirement): When
processing_template_decl, don't check noexcept of the
substituted expression.
(tsubst_nested_requirement): Just substitute into the constraint
when processing_template_decl.
* cvt.c (convert_to_void): Don't resolve concept checks when
processing_template_decl.
gcc/testsuite/ChangeLog:
PR c++/96409
PR c++/96410
* g++.dg/cpp2a/concepts-lambda13.C: New test.
---
gcc/cp/constraint.cc | 9 ++++++-
gcc/cp/cvt.c | 2 +-
.../g++.dg/cpp2a/concepts-lambda13.C | 25 +++++++++++++++++++
3 files changed, 34 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index e4aace596e7..db2036502a7 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1993,7 +1993,8 @@ tsubst_compound_requirement (tree t, tree args,
subst_info info)
/* Check the noexcept condition. */
bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
- if (noexcept_p && !expr_noexcept_p (expr, tf_none))
+ if (!processing_template_decl
+ && noexcept_p && !expr_noexcept_p (expr, tf_none))
return error_mark_node;
/* Substitute through the type expression, if any. */
@@ -2023,6 +2024,12 @@ static tree
tsubst_nested_requirement (tree t, tree args, subst_info info)
{
/* Ensure that we're in an evaluation context prior to satisfaction. */
+ if (processing_template_decl)
+ {
+ tree r = tsubst_constraint (TREE_OPERAND (t, 0), args,
+ info.complain, info.in_decl);
Oops, the patch is missing a check for error_mark_node here, so that
upon substitution failure we immediately resolve the requires-expression
to false. Here's an updated patch with the check and a regression test
added:
-- >8 --
gcc/cp/ChangeLog:
PR c++/96409
PR c++/96410
* constraint.cc (tsubst_compound_requirement): When
processing_template_decl, don't check noexcept of the
substituted expression.
(tsubst_nested_requirement): Just substitute into the constraint
when processing_template_decl.
* cvt.c (convert_to_void): Don't resolve concept checks when
processing_template_decl.
gcc/testsuite/ChangeLog:
PR c++/96409
PR c++/96410
* g++.dg/cpp2a/concepts-lambda13.C: New test.
* g++.dg/cpp2a/concepts-lambda14.C: New test.
---
gcc/cp/constraint.cc | 11 +++++++-
gcc/cp/cvt.c | 2 +-
.../g++.dg/cpp2a/concepts-lambda13.C | 25 +++++++++++++++++++
.../g++.dg/cpp2a/concepts-lambda14.C | 10 ++++++++
4 files changed, 46 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index e4aace596e7..5b4964dd36e 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -1993,7 +1993,8 @@ tsubst_compound_requirement (tree t, tree args,
subst_info info)
/* Check the noexcept condition. */
bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
- if (noexcept_p && !expr_noexcept_p (expr, tf_none))
+ if (!processing_template_decl
+ && noexcept_p && !expr_noexcept_p (expr, tf_none))
return error_mark_node;
/* Substitute through the type expression, if any. */
@@ -2023,6 +2024,14 @@ static tree
tsubst_nested_requirement (tree t, tree args, subst_info info)
{
/* Ensure that we're in an evaluation context prior to satisfaction. */
+ if (processing_template_decl)
+ {
+ tree r = tsubst_constraint (TREE_OPERAND (t, 0), args,
+ info.complain, info.in_decl);
+ if (r == error_mark_node)
+ return error_mark_node;
+ return finish_nested_requirement (EXPR_LOCATION (t), r);
+ }
tree norm = TREE_TYPE (t);
tree result = satisfy_constraint (norm, args, info);
if (result == error_mark_node && info.quiet ())
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index c9e7b1ff044..1d2c2d3351c 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1159,7 +1159,7 @@ convert_to_void (tree expr, impl_conv_void implicit,
tsubst_flags_t complain)
/* Explicitly evaluate void-converted concept checks since their
satisfaction may produce ill-formed programs. */
- if (concept_check_p (expr))
+ if (!processing_template_decl && concept_check_p (expr))
expr = evaluate_concept_check (expr, tf_warning_or_error);
if (VOID_TYPE_P (TREE_TYPE (expr)))
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
new file mode 100644
index 00000000000..9757ce49d67
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
@@ -0,0 +1,25 @@
+// PR c++/96410
+// { dg-do compile { target c++20 } }
+
+struct S { using blah = void; };
+
+template <typename T> constexpr bool trait = !__is_same(T, S);
+template <typename T> concept C = trait<T>;
+
+template<typename T>
+void foo() noexcept(!__is_same(T, void)) { }
+
+template<typename U>
+auto f() {
+ return []<typename T>(T){
+ static_assert(requires { requires C<U> && (C<T> || C<T>); }); // { dg-error
"assert" }
+ static_assert(requires { C<T>; });
+ static_assert(requires { { foo<T>() } noexcept -> C; });
+ static_assert(!requires { typename T::blah; }); // { dg-error "assert" }
+ return 0;
+ };
+}
+
+auto g = f<int>(); // { dg-bogus "" }
+int n = g(0); // { dg-bogus "" }
+int m = g(S{}); // { dg-message "required from here" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
new file mode 100644
index 00000000000..e6ae73c4872
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+auto f() {
+ return [](auto){
+ static_assert(requires { requires T::fail; }); // { dg-error "assert" }
+ };
+}
+
+auto g = f<int>(); // { dg-message "required from here" }