From 8237beb118c5b5a724a459584975a11c3234c1c7 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 6 Oct 2015 21:46:54 -0400 Subject: [PATCH] re PR c++/67810 (Non-expression recognized as fold expression) PR c++/67810 * parser.c (cp_parser_fold_expr_p): Remove. (is_binary_op): New. (cp_parser_fold_expression): Take LHS as parameter. (cp_parser_primary_expression): Call it after parsing an expression. (cp_parser_binary_expression, cp_parser_assignment_operator_opt) (cp_parser_expression): Ignore an operator followed by '...'. (is_binary_op): New. * pt.c (tsubst_unary_left_fold, tsubst_binary_left_fold) (tsubst_unary_right_fold, tsubst_binary_right_fold): Handle errors. From-SVN: r228556 --- gcc/cp/ChangeLog | 13 ++ gcc/cp/parser.c | 153 ++++++++++++++--------- gcc/cp/pt.c | 12 ++ gcc/testsuite/g++.dg/cpp1y/var-templ45.C | 8 ++ gcc/testsuite/g++.dg/cpp1z/fold2.C | 4 +- gcc/testsuite/g++.dg/cpp1z/fold6.C | 42 +++++++ 6 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/var-templ45.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/fold6.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 623c3fcc55c..1b2f6b4cd74 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2015-10-06 Jason Merrill + + PR c++/67810 + * parser.c (cp_parser_fold_expr_p): Remove. + (is_binary_op): New. + (cp_parser_fold_expression): Take LHS as parameter. + (cp_parser_primary_expression): Call it after parsing an expression. + (cp_parser_binary_expression, cp_parser_assignment_operator_opt) + (cp_parser_expression): Ignore an operator followed by '...'. + (is_binary_op): New. + * pt.c (tsubst_unary_left_fold, tsubst_binary_left_fold) + (tsubst_unary_right_fold, tsubst_binary_right_fold): Handle errors. + 2015-10-06 Marek Polacek PR c++/67863 diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index ffed595ac0e..f9b668ab0cb 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -4339,6 +4339,50 @@ cp_parser_fold_operator (cp_token *token) } } +/* Returns true if CODE indicates a binary expression, which is not allowed in + the LHS of a fold-expression. More codes will need to be added to use this + function in other contexts. */ + +static bool +is_binary_op (tree_code code) +{ + switch (code) + { + case PLUS_EXPR: + case POINTER_PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + case TRUNC_DIV_EXPR: + case TRUNC_MOD_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case BIT_IOR_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + + case MODOP_EXPR: + + case EQ_EXPR: + case NE_EXPR: + case LE_EXPR: + case GE_EXPR: + case LT_EXPR: + case GT_EXPR: + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + + case COMPOUND_EXPR: + + case DOTSTAR_EXPR: + case MEMBER_REF: + return true; + + default: + return false; + } +} + /* If the next token is a suitable fold operator, consume it and return as the function above. */ @@ -4352,41 +4396,6 @@ cp_parser_fold_operator (cp_parser *parser) return code; } -/* Returns true iff we're at the beginning of an N4191 fold-expression, after - the left parenthesis. Rather than do tentative parsing, we scan the tokens - up to the matching right paren for an ellipsis next to a binary - operator. */ - -static bool -cp_parser_fold_expr_p (cp_parser *parser) -{ - /* An ellipsis right after the left paren always indicates a - fold-expression. */ - if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) - { - /* But if there isn't a fold operator after the ellipsis, - give a different error. */ - cp_token *token = cp_lexer_peek_nth_token (parser->lexer, 2); - return (cp_parser_fold_operator (token) != ERROR_MARK); - } - - /* Otherwise, look for an ellipsis. */ - cp_lexer_save_tokens (parser->lexer); - int ret = cp_parser_skip_to_closing_parenthesis_1 (parser, false, - CPP_ELLIPSIS, false); - bool found = (ret == -1); - if (found) - { - /* We found an ellipsis, is the previous token an operator? */ - cp_token *token = cp_lexer_peek_token (parser->lexer); - --token; - if (cp_parser_fold_operator (token) == ERROR_MARK) - found = false; - } - cp_lexer_rollback_tokens (parser->lexer); - return found; -} - /* Parse a fold-expression. fold-expression: @@ -4397,14 +4406,10 @@ cp_parser_fold_expr_p (cp_parser *parser) Note that the '(' and ')' are matched in primary expression. */ static tree -cp_parser_fold_expression (cp_parser *parser) +cp_parser_fold_expression (cp_parser *parser, tree expr1) { cp_id_kind pidk; - if (cxx_dialect < cxx1z && !in_system_header_at (input_location)) - pedwarn (input_location, 0, "fold-expressions only available with " - "-std=c++1z or -std=gnu++1z"); - // Left fold. if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) { @@ -4423,10 +4428,6 @@ cp_parser_fold_expression (cp_parser *parser) return finish_left_unary_fold_expr (expr, op); } - tree expr1 = cp_parser_cast_expression (parser, false, false, false, &pidk); - if (expr1 == error_mark_node) - return error_mark_node; - const cp_token* token = cp_lexer_peek_token (parser->lexer); int op = cp_parser_fold_operator (parser); if (op == ERROR_MARK) @@ -4442,6 +4443,16 @@ cp_parser_fold_expression (cp_parser *parser) } cp_lexer_consume_token (parser->lexer); + /* The operands of a fold-expression are cast-expressions, so binary or + conditional expressions are not allowed. We check this here to avoid + tentative parsing. */ + if (is_binary_op (TREE_CODE (expr1))) + error_at (location_of (expr1), + "binary expression in operand of fold-expression"); + else if (TREE_CODE (expr1) == COND_EXPR) + error_at (location_of (expr1), + "conditional expression in operand of fold-expression"); + // Right fold. if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)) return finish_right_unary_fold_expr (expr1, op); @@ -4668,22 +4679,31 @@ cp_parser_primary_expression (cp_parser *parser, = parser->greater_than_is_operator_p; parser->greater_than_is_operator_p = true; - // Handle a fold-expression. - if (cp_parser_fold_expr_p (parser)) + if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS)) + /* Left fold expression. */ + expr = NULL_TREE; + else + /* Parse the parenthesized expression. */ + expr = cp_parser_expression (parser, idk, cast_p, decltype_p); + + token = cp_lexer_peek_token (parser->lexer); + if (token->type == CPP_ELLIPSIS || cp_parser_fold_operator (token)) { - tree fold = cp_parser_fold_expression (parser); - cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN); - return fold; + expr = cp_parser_fold_expression (parser, expr); + if (expr != error_mark_node + && cxx_dialect < cxx1z + && !in_system_header_at (input_location)) + pedwarn (input_location, 0, "fold-expressions only available " + "with -std=c++1z or -std=gnu++1z"); } + else + /* Let the front end know that this expression was + enclosed in parentheses. This matters in case, for + example, the expression is of the form `A::B', since + `&A::B' might be a pointer-to-member, but `&(A::B)' is + not. */ + expr = finish_parenthesized_expr (expr); - /* Parse the parenthesized expression. */ - expr = cp_parser_expression (parser, idk, cast_p, decltype_p); - /* Let the front end know that this expression was - enclosed in parentheses. This matters in case, for - example, the expression is of the form `A::B', since - `&A::B' might be a pointer-to-member, but `&(A::B)' is - not. */ - expr = finish_parenthesized_expr (expr); /* DR 705: Wrapping an unqualified name in parentheses suppresses arg-dependent lookup. We want to pass back CP_ID_KIND_QUALIFIED for suppressing vtable lookup @@ -8468,6 +8488,10 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, } new_prec = TOKEN_PRECEDENCE (token); + if (new_prec != PREC_NOT_OPERATOR + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) + /* This is a fold-expression; handle it later. */ + new_prec = PREC_NOT_OPERATOR; /* Popping an entry off the stack means we completed a subexpression: - either we found a token which is not an operator (`>' where it is not @@ -8509,6 +8533,9 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, cases such as 3 + 4 + 5 or 3 * 4 + 5. */ token = cp_lexer_peek_token (parser->lexer); lookahead_prec = TOKEN_PRECEDENCE (token); + if (lookahead_prec != PREC_NOT_OPERATOR + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) + lookahead_prec = PREC_NOT_OPERATOR; if (lookahead_prec > new_prec) { /* ... and prepare to parse the RHS of the new, higher priority @@ -8824,6 +8851,11 @@ cp_parser_assignment_operator_opt (cp_parser* parser) op = ERROR_MARK; } + /* An operator followed by ... is a fold-expression, handled elsewhere. */ + if (op != ERROR_MARK + && cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) + op = ERROR_MARK; + /* If it was an assignment operator, consume it. */ if (op != ERROR_MARK) cp_lexer_consume_token (parser->lexer); @@ -8877,9 +8909,10 @@ cp_parser_expression (cp_parser* parser, cp_id_kind * pidk, expression = build_x_compound_expr (loc, expression, assignment_expression, complain_flags (decltype_p)); - /* If the next token is not a comma, then we are done with the - expression. */ - if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA)) + /* If the next token is not a comma, or we're in a fold-expression, then + we are done with the expression. */ + if (cp_lexer_next_token_is_not (parser->lexer, CPP_COMMA) + || cp_lexer_nth_token_is (parser->lexer, 2, CPP_ELLIPSIS)) break; /* Consume the `,'. */ loc = cp_lexer_peek_token (parser->lexer)->location; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6520b8b31e5..6926557dfc6 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10632,6 +10632,8 @@ tsubst_unary_left_fold (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree pack = tsubst_fold_expr_pack (t, args, complain, in_decl); + if (pack == error_mark_node) + return error_mark_node; if (TREE_VEC_LENGTH (pack) == 0) return expand_empty_fold (t, complain); else @@ -10648,7 +10650,11 @@ tsubst_binary_left_fold (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree pack = tsubst_fold_expr_pack (t, args, complain, in_decl); + if (pack == error_mark_node) + return error_mark_node; tree init = tsubst_fold_expr_init (t, args, complain, in_decl); + if (init == error_mark_node) + return error_mark_node; tree vec = make_tree_vec (TREE_VEC_LENGTH (pack) + 1); TREE_VEC_ELT (vec, 0) = init; @@ -10689,6 +10695,8 @@ tsubst_unary_right_fold (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree pack = tsubst_fold_expr_pack (t, args, complain, in_decl); + if (pack == error_mark_node) + return error_mark_node; if (TREE_VEC_LENGTH (pack) == 0) return expand_empty_fold (t, complain); else @@ -10705,7 +10713,11 @@ tsubst_binary_right_fold (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree pack = tsubst_fold_expr_pack (t, args, complain, in_decl); + if (pack == error_mark_node) + return error_mark_node; tree init = tsubst_fold_expr_init (t, args, complain, in_decl); + if (init == error_mark_node) + return error_mark_node; int n = TREE_VEC_LENGTH (pack); tree vec = make_tree_vec (n + 1); diff --git a/gcc/testsuite/g++.dg/cpp1y/var-templ45.C b/gcc/testsuite/g++.dg/cpp1y/var-templ45.C new file mode 100644 index 00000000000..444a8a22a85 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/var-templ45.C @@ -0,0 +1,8 @@ +// PR c++/67810 +// { dg-do compile { target c++14 } } + +template +constexpr bool Test = true; + +template )> +void f(); diff --git a/gcc/testsuite/g++.dg/cpp1z/fold2.C b/gcc/testsuite/g++.dg/cpp1z/fold2.C index e42a39d8890..598e55732a6 100644 --- a/gcc/testsuite/g++.dg/cpp1z/fold2.C +++ b/gcc/testsuite/g++.dg/cpp1z/fold2.C @@ -54,8 +54,8 @@ MAKE_FNS (eq, ==); MAKE_FNS (ne, !=); MAKE_FNS (lt, <); MAKE_FNS (gt, >); -MAKE_FNS (le, <); -MAKE_FNS (ge, >); +MAKE_FNS (le, <=); +MAKE_FNS (ge, >=); MAKE_FNS (land, &&); MAKE_FNS (lor, ||); diff --git a/gcc/testsuite/g++.dg/cpp1z/fold6.C b/gcc/testsuite/g++.dg/cpp1z/fold6.C new file mode 100644 index 00000000000..cc073f90aea --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/fold6.C @@ -0,0 +1,42 @@ +// Test that we reject a fold-expression with an LHS that is not a +// cast-expression. + +// { dg-options -std=c++1z } + +int i; + +template +int f() +{ + (i ? i : Is + ...); // { dg-error "" } + (i + Is + ...); // { dg-error "" } + (i - Is + ...); // { dg-error "" } + (i * Is + ...); // { dg-error "" } + (i / Is + ...); // { dg-error "" } + (i % Is + ...); // { dg-error "" } + (i ^ Is + ...); // { dg-error "" } + (i | Is + ...); // { dg-error "" } + (i & Is + ...); // { dg-error "" } + (i << Is + ...); // { dg-error "" } + (i >> Is + ...); // { dg-error "" } + (i = Is + ...); // { dg-error "" } + (i += Is + ...); // { dg-error "" } + (i -= Is + ...); // { dg-error "" } + (i *= Is + ...); // { dg-error "" } + (i /= Is + ...); // { dg-error "" } + (i %= Is + ...); // { dg-error "" } + (i ^= Is + ...); // { dg-error "" } + (i |= Is + ...); // { dg-error "" } + (i &= Is + ...); // { dg-error "" } + (i <<= Is + ...); // { dg-error "" } + (i >>= Is + ...); // { dg-error "" } + (i == Is + ...); // { dg-error "" } + (i != Is + ...); // { dg-error "" } + (i < Is + ...); // { dg-error "" } + (i > Is + ...); // { dg-error "" } + (i <= Is + ...); // { dg-error "" } + (i >= Is + ...); // { dg-error "" } + (i && Is + ...); // { dg-error "" } + (i || Is + ...); // { dg-error "" } + (i , Is + ...); // { dg-error "" } +} -- 2.30.2