c++: parameterized requires-expr as default argument [PR101725]

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 default 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 within
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.

	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.

(cherry picked from commit 9707d2e5db)
This commit is contained in:
Patrick Palka 2021-08-11 16:53:53 -04:00
parent 4ee9e57684
commit be45bc283e
6 changed files with 41 additions and 3 deletions

View File

@ -8461,7 +8461,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 class scope T; if T is not a class, do nothing. */

View File

@ -14178,6 +14178,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;

View File

@ -5948,7 +5948,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

View File

@ -5382,7 +5382,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;

View File

@ -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));

View File

@ -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(); }));