Here we're rejecting the default template argument

  requires (T t) { x(t); }

because we consider the 't' in the requirement to be a local variable
(according to local_variable_p), and we generally forbid local variables
from appearing inside template arguments.  We can perhaps fix this by
giving special treatment to parameters introduced by requires-expressions,
but DR 2082 relaxed the restriction about local variables appearing inside
default arguments to permit them inside unevaluated operands thereof.
So this patch just implements DR 2082 which also fixes this PR since a
requires-expression is an unevaluated context.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk and perhaps 11?

        PR c++/101725
        DR 2082

gcc/cp/ChangeLog:

        * cp-tree.h (unevaluated_p): Return true for REQUIRES_EXPR.
        * decl.c (local_variable_p_walkfn): Don't walk into unevaluated
        operands.
        * parser.c (cp_parser_primary_expression) <case CPP_NAME>: Never
        reject uses of local variables in unevaluated contexts.
        * tree.c (cp_walk_subtrees) <case REQUIRES_EXPR>: Increment
        cp_unevaluated_operand.  Use cp_walk_tree directly instead of
        WALK_SUBTREE to avoid the goto.  Use REQUIRES_EXPR_REQS instead
        of TREE_OPERAND directly.

gcc/testsuite/ChangeLog:

        * g++.dg/DRs/dr2082.C: New test.
        * g++.dg/cpp2a/concepts-uneval4.C: New test.
        * g++.dg/cpp2a/concepts-uneval5.C: New test.
---
 gcc/cp/cp-tree.h                              |  3 ++-
 gcc/cp/decl.c                                 |  8 ++++++++
 gcc/cp/parser.c                               |  5 ++++-
 gcc/cp/tree.c                                 |  4 +++-
 gcc/testsuite/g++.dg/DRs/dr2082.C             | 12 ++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-uneval4.C | 12 ++++++++++++
 6 files changed, 41 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/DRs/dr2082.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-uneval4.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9a47a8787d6..6a8264b0c61 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8494,7 +8494,8 @@ unevaluated_p (tree_code code)
   return (code == DECLTYPE_TYPE
          || code == ALIGNOF_EXPR
          || code == SIZEOF_EXPR
-         || code == NOEXCEPT_EXPR);
+         || code == NOEXCEPT_EXPR
+         || code == REQUIRES_EXPR);
 }
 
 /* RAII class to push/pop the access scope for T.  */
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 6fa6b9adc87..b0b492360af 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14270,6 +14270,14 @@ static tree
 local_variable_p_walkfn (tree *tp, int *walk_subtrees,
                         void * /*data*/)
 {
+  if (unevaluated_p (TREE_CODE (*tp)))
+    {
+      /* DR 2082 permits local variables in unevaluated contexts
+        within a default argument.  */
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
   if (local_variable_p (*tp)
       && (!DECL_ARTIFICIAL (*tp) || DECL_NAME (*tp) == this_identifier))
     return *tp;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 47bf7d9ad1f..8b551db2c8a 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -5971,7 +5971,10 @@ cp_parser_primary_expression (cp_parser *parser,
            /* Check to see if DECL is a local variable in a context
               where that is forbidden.  */
            if ((parser->local_variables_forbidden_p & LOCAL_VARS_FORBIDDEN)
-               && local_variable_p (decl))
+               && local_variable_p (decl)
+               /* DR 2082 permits local variables in unevaluated contexts
+                  within a default argument.  */
+               && !cp_unevaluated_operand)
              {
                const char *msg
                  = (TREE_CODE (decl) == PARM_DECL
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 8345396ec33..e8831b21802 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -5386,7 +5386,9 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, 
walk_tree_fn func,
       // walk the parameter list. Doing so causes false
       // positives in the pack expansion checker since the
       // requires parameters are introduced as pack expansions.
-      WALK_SUBTREE (TREE_OPERAND (*tp, 1));
+      ++cp_unevaluated_operand;
+      result = cp_walk_tree (&REQUIRES_EXPR_REQS (*tp), func, data, pset);
+      --cp_unevaluated_operand;
       *walk_subtrees_p = 0;
       break;
 
diff --git a/gcc/testsuite/g++.dg/DRs/dr2082.C 
b/gcc/testsuite/g++.dg/DRs/dr2082.C
new file mode 100644
index 00000000000..84bb23f63f2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2082.C
@@ -0,0 +1,12 @@
+// DR 2082
+
+void f() {
+  int i;
+  extern void h(int x = sizeof(i));
+}
+
+class A {
+  void f(A* p = this) { } // { dg-error "this" }
+};
+
+int h(int a, int b = sizeof(a));
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-uneval4.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-uneval4.C
new file mode 100644
index 00000000000..1be27d1ab28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-uneval4.C
@@ -0,0 +1,12 @@
+// PR c++/101725
+// { dg-do compile { target c++20 } }
+
+template<class T, bool V = requires (T t) { x(t); }> void f();
+
+struct A {
+  int m;
+  void f(int a, int b = requires (int t) { a + m + t; });
+};
+
+void g();
+static_assert(noexcept(requires { g(); }));
-- 
2.32.0.452.g940fe202ad

Reply via email to