aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Sutton <asutton@lock3software.com>2019-11-27 15:16:37 +0000
committerAndrew Sutton <asutton@lock3software.com>2019-11-27 15:16:37 +0000
commita59ae882bf6fe03991a4bddb167be219ce8442b2 (patch)
tree64d5c8552df1714eea45f75ccb12ffbf7b2cb181
parentbe934e97d167d91aba6f98594bed0224630357f9 (diff)
2019-11-27 Andrew Sutton <asutton@lock3software.com>
PR c++/92439 Improve quality of diagnostics for subexpressions that need parens. gcc/cp/ * parser.c (cp_parser_requires_clause_opt): Add a flag to indicate when parsing a requires-clause before lambda parameters, and... (cp_parser_lambda_declarator_opt): ... use that here ... (cp_parser_type_parameter): ... and here ... (cp_parser_late_return_type_opt): ... and here ... (cp_parser_explicit_template_declaration): ... and here. (cp_parser_diagnose_ungrouped_constraint_plain): Adjust the message because this can apply to subexpressions that are not immediately after a requires-clause. (cp_parser_diagnose_ungrouped_constraint_rich): Likewise. (primary_constraint_error): New. (cp_parser_constraint_requires_parens): New. (cp_parser_unary_constraint_requires_parens): New. (cp_parser_constraint_primary_expression): Check for unary expressions before parsing the primary expression. Also check for binary and postfix operators after a successful parse of the primary expression. Force a re-parse if the result would form a lower-precedence string. (cp_parser_constraint_logical_and_expression): Propagate lambda flag; move checks for ill-formed constraints into the constraint primary expression. (cp_parser_constraint_logical_or_expression): Likewise. (cp_parser_requires_clause_expression): Propagate lambda flag. gcc/testsuite/ * g++.dg/cpp2a/concepts-requires20.C: New. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@278774 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/cp/ChangeLog27
-rw-r--r--gcc/cp/parser.c276
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C65
4 files changed, 275 insertions, 100 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 470a5271ded..36302e43aca 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,32 @@
2019-11-27 Andrew Sutton <asutton@lock3software.com>
+ PR c++/92439
+ Improve quality of diagnostics for subexpressions that need parens.
+ * parser.c (cp_parser_requires_clause_opt): Add a flag to indicate
+ when parsing a requires-clause before lambda parameters, and...
+ (cp_parser_lambda_declarator_opt): ... use that here ...
+ (cp_parser_type_parameter): ... and here ...
+ (cp_parser_late_return_type_opt): ... and here ...
+ (cp_parser_explicit_template_declaration): ... and here.
+ (cp_parser_diagnose_ungrouped_constraint_plain): Adjust the message
+ because this can apply to subexpressions that are not immediately
+ after a requires-clause.
+ (cp_parser_diagnose_ungrouped_constraint_rich): Likewise.
+ (primary_constraint_error): New.
+ (cp_parser_constraint_requires_parens): New.
+ (cp_parser_unary_constraint_requires_parens): New.
+ (cp_parser_constraint_primary_expression): Check for unary expressions
+ before parsing the primary expression. Also check for binary and
+ postfix operators after a successful parse of the primary expression.
+ Force a re-parse if the result would form a lower-precedence string.
+ (cp_parser_constraint_logical_and_expression): Propagate lambda flag;
+ move checks for ill-formed constraints into the constraint primary
+ expression.
+ (cp_parser_constraint_logical_or_expression): Likewise.
+ (cp_parser_requires_clause_expression): Propagate lambda flag.
+
+2019-11-27 Andrew Sutton <asutton@lock3software.com>
+
PR c++/88395
* constraint.cc (satisfy_declaration_constraints): Push tinst levels
around satisfaction.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d0c9ff01d9e..27318d3aeed 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2440,7 +2440,7 @@ static tree cp_parser_concept_definition
static tree cp_parser_constraint_expression
(cp_parser *);
static tree cp_parser_requires_clause_opt
- (cp_parser *);
+ (cp_parser *, bool);
static tree cp_parser_requires_expression
(cp_parser *);
static tree cp_parser_requirement_parameter_list
@@ -10897,7 +10897,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
/* We may have a constrained generic lambda; parse the requires-clause
immediately after the template-parameter-list and combine with any
shorthand constraints present. */
- tree dreqs = cp_parser_requires_clause_opt (parser);
+ tree dreqs = cp_parser_requires_clause_opt (parser, true);
if (flag_concepts)
{
tree reqs = get_shorthand_constraints (current_template_parms);
@@ -10990,7 +10990,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
gnu_attrs = cp_parser_gnu_attributes_opt (parser);
/* Parse optional trailing requires clause. */
- trailing_requires_clause = cp_parser_requires_clause_opt (parser);
+ trailing_requires_clause = cp_parser_requires_clause_opt (parser, false);
/* The function parameters must be in scope all the way until after the
trailing-return-type in case of decltype. */
@@ -16328,11 +16328,11 @@ cp_parser_type_parameter (cp_parser* parser, bool *is_parameter_pack)
/* Look for the `>'. */
cp_parser_require (parser, CPP_GREATER, RT_GREATER);
- // If template requirements are present, parse them.
+ /* If template requirements are present, parse them. */
if (flag_concepts)
{
tree reqs = get_shorthand_constraints (current_template_parms);
- if (tree dreqs = cp_parser_requires_clause_opt (parser))
+ if (tree dreqs = cp_parser_requires_clause_opt (parser, false))
reqs = combine_constraint_expressions (reqs, dreqs);
TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
}
@@ -21940,7 +21940,7 @@ cp_parser_late_return_type_opt (cp_parser* parser, cp_declarator *declarator,
/* Function declarations may be followed by a trailing
requires-clause. */
- requires_clause = cp_parser_requires_clause_opt (parser);
+ requires_clause = cp_parser_requires_clause_opt (parser, false);
if (declare_simd_p)
declarator->attributes
@@ -27122,8 +27122,7 @@ cp_parser_concept_definition (cp_parser *parser)
static void
cp_parser_diagnose_ungrouped_constraint_plain (location_t loc)
{
- error_at (loc, "expression after %<requires%> must be enclosed "
- "in parentheses");
+ error_at (loc, "expression must be enclosed in parentheses");
}
static void
@@ -27132,56 +27131,48 @@ cp_parser_diagnose_ungrouped_constraint_rich (location_t loc)
gcc_rich_location richloc (loc);
richloc.add_fixit_insert_before ("(");
richloc.add_fixit_insert_after (")");
- error_at (&richloc, "expression after %<requires%> must be enclosed "
- "in parentheses");
+ error_at (&richloc, "expression must be enclosed in parentheses");
}
-/* Parse a primary expression within a constraint. */
-
-static cp_expr
-cp_parser_constraint_primary_expression (cp_parser *parser)
+/* Characterizes the likely kind of expression intended by a mis-written
+ primary constraint. */
+enum primary_constraint_error
{
- cp_parser_parse_tentatively (parser);
- cp_id_kind idk;
- location_t loc = input_location;
- cp_expr expr = cp_parser_primary_expression (parser,
- /*address_p=*/false,
- /*cast_p=*/false,
- /*template_arg_p=*/false,
- &idk);
- expr.maybe_add_location_wrapper ();
- if (expr != error_mark_node)
- expr = finish_constraint_primary_expr (expr);
- if (cp_parser_parse_definitely (parser))
- return expr;
-
- /* Retry the parse at a lower precedence. If that succeeds, diagnose the
- error, but return the expression as if it were valid. */
- cp_parser_parse_tentatively (parser);
- expr = cp_parser_simple_cast_expression (parser);
- if (cp_parser_parse_definitely (parser))
- {
- cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location());
- return expr;
- }
-
- /* Otherwise, something has gone wrong, but we can't generate a more
- meaningful diagnostic or recover. */
- cp_parser_diagnose_ungrouped_constraint_plain (loc);
- return error_mark_node;
-}
+ pce_ok,
+ pce_maybe_operator,
+ pce_maybe_postfix,
+};
-/* Examine the token following EXPR. If it is an operator in a non-logical
- binary expression, diagnose that as an error. Returns ERROR_MARK_NODE. */
+/* Returns true if the token(s) following a primary-expression in a
+ constraint-logical-* expression would require parentheses. */
-static cp_expr
-cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
+static primary_constraint_error
+cp_parser_constraint_requires_parens (cp_parser *parser, bool lambda_p)
{
cp_token *token = cp_lexer_peek_token (parser->lexer);
switch (token->type)
{
default:
- return lhs;
+ return pce_ok;
+
+ case CPP_EQ:
+ {
+ /* An equal sign may be part of the the definition of a function,
+ and not an assignment operator, when parsing the expression
+ for a trailing requires-clause. For example:
+
+ template<typename T>
+ struct S {
+ S() requires C<T> = default;
+ };
+
+ Don't try to reparse this a binary operator. */
+ if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE)
+ || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT))
+ return pce_ok;
+
+ gcc_fallthrough ();
+ }
/* Arithmetic operators. */
case CPP_PLUS:
@@ -27196,15 +27187,13 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
case CPP_RSHIFT:
case CPP_LSHIFT:
/* Relational operators. */
- /* FIXME: Handle '<=>'. */
case CPP_EQ_EQ:
case CPP_NOT_EQ:
case CPP_LESS:
case CPP_GREATER:
case CPP_LESS_EQ:
case CPP_GREATER_EQ:
- /* Conditional operator */
- case CPP_QUERY:
+ case CPP_SPACESHIP:
/* Pointer-to-member. */
case CPP_DOT_STAR:
case CPP_DEREF_STAR:
@@ -27219,52 +27208,138 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
case CPP_XOR_EQ:
case CPP_RSHIFT_EQ:
case CPP_LSHIFT_EQ:
- break;
+ /* Conditional operator */
+ case CPP_QUERY:
+ /* Unenclosed binary or conditional operator. */
+ return pce_maybe_operator;
- case CPP_EQ: {
- /* An equal sign may be part of the the definition of a function,
- and not an assignment operator, when parsing the expression
- for a trailing requires-clause. For example:
+ case CPP_OPEN_PAREN:
+ {
+ /* A primary constraint that precedes the parameter-list of a
+ lambda expression is followed by an open paren.
- template<typename T>
- struct S {
- S() requires C<T> = default;
- }
+ []<typename T> requires C (T a, T b) { ... }
- This is not an error. */
- if (cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DELETE)
- || cp_lexer_nth_token_is_keyword (parser->lexer, 2, RID_DEFAULT))
- return lhs;
+ Don't try to re-parse this as a postfix expression. */
+ if (lambda_p)
+ return pce_ok;
- break;
- }
+ gcc_fallthrough ();
+ }
+ case CPP_OPEN_SQUARE:
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ case CPP_DOT:
+ case CPP_DEREF:
+ /* Unenclosed postfix operator. */
+ return pce_maybe_postfix;
}
+}
+
+/* Returns true if the next token begins a unary expression, preceded by
+ an operator or keyword. */
+
+static bool
+cp_parser_unary_constraint_requires_parens (cp_parser *parser)
+{
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ switch (token->type)
+ {
+ case CPP_NOT:
+ case CPP_PLUS:
+ case CPP_MINUS:
+ case CPP_MULT:
+ case CPP_COMPL:
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ return true;
+
+ case CPP_KEYWORD:
+ {
+ switch (token->keyword)
+ {
+ case RID_STATCAST:
+ case RID_DYNCAST:
+ case RID_REINTCAST:
+ case RID_CONSTCAST:
+ case RID_TYPEID:
+ case RID_SIZEOF:
+ case RID_ALIGNOF:
+ case RID_NOEXCEPT:
+ case RID_NEW:
+ case RID_DELETE:
+ case RID_THROW:
+ return true;
+
+ default:
+ break;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/* Parse a primary expression within a constraint. */
+
+static cp_expr
+cp_parser_constraint_primary_expression (cp_parser *parser, bool lambda_p)
+{
+ /* If this looks like a unary expression, parse it as such, but diagnose
+ it as ill-formed; it requires parens. */
+ if (cp_parser_unary_constraint_requires_parens (parser))
+ {
+ cp_expr e = cp_parser_assignment_expression (parser, NULL, false, false);
+ cp_parser_diagnose_ungrouped_constraint_rich (e.get_location());
+ return e;
+ }
- /* Try to parse the RHS as either the remainder of a conditional-expression
- or a logical-or-expression so we can form a good diagnostic. */
cp_parser_parse_tentatively (parser);
- cp_expr rhs;
- if (token->type == CPP_QUERY)
- rhs = cp_parser_question_colon_clause (parser, lhs);
- else
+ cp_id_kind idk;
+ location_t loc = input_location;
+ cp_expr expr = cp_parser_primary_expression (parser,
+ /*address_p=*/false,
+ /*cast_p=*/false,
+ /*template_arg_p=*/false,
+ &idk);
+ expr.maybe_add_location_wrapper ();
+
+ primary_constraint_error pce = pce_ok;
+ if (expr != error_mark_node)
{
- cp_lexer_consume_token (parser->lexer);
- rhs = cp_parser_binary_expression (parser, false, false, false,
- PREC_NOT_OPERATOR, NULL);
+ /* The primary-expression could be part of an unenclosed non-logical
+ compound expression. */
+ pce = cp_parser_constraint_requires_parens (parser, lambda_p);
+ if (pce != pce_ok)
+ cp_parser_simulate_error (parser);
+ else
+ expr = finish_constraint_primary_expr (expr);
}
+ if (cp_parser_parse_definitely (parser))
+ return expr;
+ if (expr == error_mark_node)
+ return error_mark_node;
- /* If we couldn't parse the RHS, then emit the best diagnostic we can. */
- if (!cp_parser_parse_definitely (parser))
+ /* Retry the parse at a lower precedence. If that succeeds, diagnose the
+ error, but return the expression as if it were valid. */
+ gcc_assert (pce != pce_ok);
+ cp_parser_parse_tentatively (parser);
+ if (pce == pce_maybe_operator)
+ expr = cp_parser_assignment_expression (parser, NULL, false, false);
+ else
+ expr = cp_parser_simple_cast_expression (parser);
+ if (cp_parser_parse_definitely (parser))
{
- cp_parser_diagnose_ungrouped_constraint_plain (token->location);
- return error_mark_node;
+ cp_parser_diagnose_ungrouped_constraint_rich (expr.get_location());
+ return expr;
}
- /* Otherwise, emit a fixit for the complete binary expression. */
- location_t loc = make_location (token->location,
- lhs.get_start(),
- rhs.get_finish());
- cp_parser_diagnose_ungrouped_constraint_rich (loc);
+ /* Otherwise, something has gone very wrong, and we can't generate a more
+ meaningful diagnostic or recover. */
+ cp_parser_diagnose_ungrouped_constraint_plain (loc);
return error_mark_node;
}
@@ -27275,16 +27350,16 @@ cp_parser_check_non_logical_constraint (cp_parser *parser, cp_expr lhs)
constraint-logical-and-expression '&&' primary-expression */
static cp_expr
-cp_parser_constraint_logical_and_expression (cp_parser *parser)
+cp_parser_constraint_logical_and_expression (cp_parser *parser, bool lambda_p)
{
- cp_expr lhs = cp_parser_constraint_primary_expression (parser);
+ cp_expr lhs = cp_parser_constraint_primary_expression (parser, lambda_p);
while (cp_lexer_next_token_is (parser->lexer, CPP_AND_AND))
{
cp_token *op = cp_lexer_consume_token (parser->lexer);
- tree rhs = cp_parser_constraint_primary_expression (parser);
+ tree rhs = cp_parser_constraint_primary_expression (parser, lambda_p);
lhs = finish_constraint_and_expr (op->location, lhs, rhs);
}
- return cp_parser_check_non_logical_constraint (parser, lhs);
+ return lhs;
}
/* Parse a constraint-logical-or-expression.
@@ -27294,27 +27369,27 @@ cp_parser_constraint_logical_and_expression (cp_parser *parser)
constraint-logical-or-expression '||' constraint-logical-and-expression */
static cp_expr
-cp_parser_constraint_logical_or_expression (cp_parser *parser)
+cp_parser_constraint_logical_or_expression (cp_parser *parser, bool lambda_p)
{
- cp_expr lhs = cp_parser_constraint_logical_and_expression (parser);
+ cp_expr lhs = cp_parser_constraint_logical_and_expression (parser, lambda_p);
while (cp_lexer_next_token_is (parser->lexer, CPP_OR_OR))
{
cp_token *op = cp_lexer_consume_token (parser->lexer);
- cp_expr rhs = cp_parser_constraint_logical_and_expression (parser);
+ cp_expr rhs = cp_parser_constraint_logical_and_expression (parser, lambda_p);
lhs = finish_constraint_or_expr (op->location, lhs, rhs);
}
- return cp_parser_check_non_logical_constraint (parser, lhs);
+ return lhs;
}
/* Parse the expression after a requires-clause. This has a different grammar
than that in the concepts TS. */
static tree
-cp_parser_requires_clause_expression (cp_parser *parser)
+cp_parser_requires_clause_expression (cp_parser *parser, bool lambda_p)
{
processing_constraint_expression_sentinel parsing_constraint;
++processing_template_decl;
- cp_expr expr = cp_parser_constraint_logical_or_expression (parser);
+ cp_expr expr = cp_parser_constraint_logical_or_expression (parser, lambda_p);
if (check_for_bare_parameter_packs (expr))
expr = error_mark_node;
--processing_template_decl;
@@ -27347,12 +27422,15 @@ cp_parser_constraint_expression (cp_parser *parser)
/* Optionally parse a requires clause:
requires-clause:
- `requires` constraint-logical-or-expression.
+ `requires` constraint-logical-or-expression.
[ConceptsTS]
- `requires constraint-expression. */
+ `requires constraint-expression.
+
+ LAMBDA_P is true when the requires-clause is parsed before the
+ parameter-list of a lambda-declarator. */
static tree
-cp_parser_requires_clause_opt (cp_parser *parser)
+cp_parser_requires_clause_opt (cp_parser *parser, bool lambda_p)
{
cp_token *tok = cp_lexer_peek_token (parser->lexer);
if (tok->keyword != RID_REQUIRES)
@@ -27372,7 +27450,7 @@ cp_parser_requires_clause_opt (cp_parser *parser)
cp_lexer_consume_token (parser->lexer);
if (!flag_concepts_ts)
- return cp_parser_requires_clause_expression (parser);
+ return cp_parser_requires_clause_expression (parser, lambda_p);
else
return cp_parser_constraint_expression (parser);
}
@@ -28927,7 +29005,7 @@ cp_parser_explicit_template_declaration (cp_parser* parser, bool member_p)
if (flag_concepts)
{
tree reqs = get_shorthand_constraints (current_template_parms);
- if (tree treqs = cp_parser_requires_clause_opt (parser))
+ if (tree treqs = cp_parser_requires_clause_opt (parser, false))
reqs = combine_constraint_expressions (reqs, treqs);
TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs;
}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 8605b0b38fa..9b22c91ce69 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,4 +1,9 @@
-2019-11-18 Andrew Sutton <asutton@lock3software.com>
+2019-11-27 Andrew Sutton <asutton@lock3software.com>
+
+ PR c++/92439
+ * g++.dg/cpp2a/concepts-requires20.C: New.
+
+2019-11-27 Andrew Sutton <asutton@lock3software.com>
PR c++/88395
* g++.dg/cpp2a/concepts-pr88395.C: New.
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C
new file mode 100644
index 00000000000..089db2ba013
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires20.C
@@ -0,0 +1,65 @@
+// { dg-do compile { target c++2a } }
+
+template<typename ...>
+constexpr bool r () { return true; }
+
+template<typename ... Ts>
+ requires r<Ts...>() // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires ++N // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N++ // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N == 0 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N ? true : false // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N = 0 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N + 1 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N - 1 // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N.x // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N->x && true // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N && N
+void f() { }
+
+template<typename T, T N>
+ requires N || N
+void f() { }
+
+template<typename T, T N>
+ requires N || !N // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires N[0] // { dg-error "enclose" }
+void f() { }
+
+template<typename T, T N>
+ requires static_cast<bool>(N) // { dg-error "enclose" }
+void f() { }
+