From patchwork Wed Oct 7 01:46:04 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 527072 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 100F9140D89 for ; Wed, 7 Oct 2015 12:46:25 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=WkFvdvdC; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to:cc :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=ab85dt8rRAC6VY7O1pidJrJvumRdvS6DKUfl/pKMNAWZjXAR1W kMwJQzEWBPCfchQDGLub5AAWeEiDkpI1OmrbRnhX0NpCa9ewd68pwL3UyhIwtBR/ qBS8GM8OcGlHx1gg3QWjt3cGmGCjJWaL2y6xn+LmGPW3XPRZdd9q1ltrw= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to:cc :from:subject:message-id:date:mime-version:content-type; s= default; bh=Edc7i8jfEldpog5OxAhsZ2gUu34=; b=WkFvdvdC4hpaQ+A+83QS LDPJAHr7q/OUutLjPap8H4bzAxRRkk1d1337wMqxZLsi3BxCTMiUEHnDY1zaH14l IWRQo3oJ5atY4Y3EBwQa5bsM9GmfN+lj7RcoYX0dPVkfYPXlAVpiNso1qZ/U8mbk ZmIGd/1DfDOWnOpvxcMTdCU= Received: (qmail 117785 invoked by alias); 7 Oct 2015 01:46:15 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 117741 invoked by uid 89); 7 Oct 2015 01:46:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.5 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Wed, 07 Oct 2015 01:46:07 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (Postfix) with ESMTPS id 57186A04BC; Wed, 7 Oct 2015 01:46:06 +0000 (UTC) Received: from [10.10.116.24] (ovpn-116-24.rdu2.redhat.com [10.10.116.24]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t971k4hZ017769; Tue, 6 Oct 2015 21:46:05 -0400 To: gcc-patches List Cc: Andrew Sutton From: Jason Merrill Subject: C++ PATCH for c++/67810 (wrong fold-expression error) Message-ID: <5614795C.8030806@redhat.com> Date: Tue, 6 Oct 2015 21:46:04 -0400 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.3.0 MIME-Version: 1.0 It seems that my approach of scanning the tokens looking for an ellipsis with an operator next to it wasn't good enough; it got confused by a reference pack expansion in a template argument list. So this patch changes the parsing approach so that within parentheses we always start by parsing an expression, and then pass that expression into cp_parser_fold_expression if what follows the expression looks like part of a fold-expression. At that point we need to complain if the initial expression is a binary or trinary expression, since the operands of fold-expressions can only be cast-expressions. Tested x86_64-pc-linux-gnu, applying to trunk. commit fb86de89602221b589f91b1cfd0fbfb7b6d7e130 Author: Jason Merrill Date: Tue Oct 6 17:31:10 2015 -0400 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. diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index ffed595..1f36b25 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -4339,6 +4339,49 @@ 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 +4395,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 +4405,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 +4427,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 +4442,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 +4678,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 +8487,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 +8532,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 +8850,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 +8908,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 6520b8b..6926557 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 0000000..444a8a2 --- /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 e42a39d..598e557 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 0000000..cc073f9 --- /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 "" } +}