This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH for c++/67810 (wrong fold-expression error)


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 <jason@redhat.com>
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 <class...>
+constexpr bool Test = true;
+
+template <typename...Ts, bool = (Test<Ts&&...>)>
+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... 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 "" }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]