re PR c++/67810 (Non-expression recognized as fold expression)
authorJason Merrill <jason@redhat.com>
Wed, 7 Oct 2015 01:46:54 +0000 (21:46 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 7 Oct 2015 01:46:54 +0000 (21:46 -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.

From-SVN: r228556

gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp1y/var-templ45.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/fold2.C
gcc/testsuite/g++.dg/cpp1z/fold6.C [new file with mode: 0644]

index 623c3fcc55cfa07f4bc14e6105168d36d863447e..1b2f6b4cd74cd834b004eb372dfa998ec8b0591e 100644 (file)
@@ -1,3 +1,16 @@
+2015-10-06  Jason Merrill  <jason@redhat.com>
+
+       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  <polacek@redhat.com>
 
        PR c++/67863
index ffed595ac0e01f46b4a39450867e2e6f972fe10d..f9b668ab0cbd70522c65a2646e1ae21901ff9000 100644 (file)
@@ -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;
index 6520b8b31e53ae410c40c9c7a3350aab0d1c7bd7..6926557dfc6d2dd7b2e1f7ee900270b5929ea79a 100644 (file)
@@ -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 (file)
index 0000000..444a8a2
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/67810
+// { dg-do compile { target c++14 } }
+
+template <class...>
+constexpr bool Test = true;
+
+template <typename...Ts, bool = (Test<Ts&&...>)>
+void f();
index e42a39d889027c1181b08d4813f79432661965a0..598e55732a612a83fd2b88b8619cb024dcde7949 100644 (file)
@@ -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 (file)
index 0000000..cc073f9
--- /dev/null
@@ -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... Is>
+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 "" }
+}