Patchwork [C++0x] implement range-based for loops

login
register
mail settings
Submitter Ed Smith-Rowland
Date Sept. 5, 2010, 3:53 p.m.
Message ID <4C83BD10.2030901@verizon.net>
Download mbox | patch
Permalink /patch/63848/
State New
Headers show

Comments

Ed Smith-Rowland - Sept. 5, 2010, 3:53 p.m.
Greetings Rodrigo,

Thank you for your contribution of range-for.  I was considering the 
same thing myself but you beat me to it. ;-)

I did notice one simplification.  Since std::begin and std::end are 
available for C-style arrays you can collapse the logic in parser.c to 
just call those.

Here is a revised patch and a toy program

Also, in the latest draft n3126.pdf on is allowed to use an initilizer list:

for (int a : {1, 2, 3, 4, 5})
   a += 2;

Thank you again for your work,

Ed Smith-Rowland

Patch

Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 163798)
+++ gcc/cp/pt.c	(working copy)
@@ -12523,7 +12523,7 @@ 
 	       into a non-dependent call.  */
 	    && type_dependent_expression_p_push (t)
 	    && !any_type_dependent_arguments_p (call_args))
-	  function = perform_koenig_lookup (function, call_args);
+	  function = perform_koenig_lookup (function, call_args, false);
 
 	if (TREE_CODE (function) == IDENTIFIER_NODE)
 	  {
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	(revision 163798)
+++ gcc/cp/semantics.c	(working copy)
@@ -1839,11 +1839,12 @@ 
 
 /* Perform Koenig lookup.  FN is the postfix-expression representing
    the function (or functions) to call; ARGS are the arguments to the
-   call.  Returns the functions to be considered by overload
-   resolution.  */
+   call; if INCLUDE_STD then the `std' namespace is automatically
+   considered an associated namespace (used in range-based for loops).
+   Returns the functions to be considered by overload resolution.  */
 
 tree
-perform_koenig_lookup (tree fn, VEC(tree,gc) *args)
+perform_koenig_lookup (tree fn, VEC(tree,gc) *args, bool include_std)
 {
   tree identifier = NULL_TREE;
   tree functions = NULL_TREE;
@@ -1879,7 +1880,7 @@ 
   if (!any_type_dependent_arguments_p (args)
       && !any_dependent_template_arguments_p (tmpl_args))
     {
-      fn = lookup_arg_dependent (identifier, functions, args);
+      fn = lookup_arg_dependent (identifier, functions, args, include_std);
       if (!fn)
 	/* The unqualified name could not be resolved.  */
 	fn = unqualified_fn_lookup_error (identifier);
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 163798)
+++ gcc/cp/parser.c	(working copy)
@@ -1825,6 +1825,10 @@ 
   (cp_parser *);
 static void cp_parser_for_init_statement
   (cp_parser *);
+static bool cp_parser_for_range
+  (cp_parser *, tree *, tree *);
+static void cp_finish_range_based_for
+  (tree, tree, tree);
 static tree cp_parser_jump_statement
   (cp_parser *);
 static void cp_parser_declaration_statement
@@ -5167,7 +5171,7 @@ 
 			koenig_p = true;
 			if (!any_type_dependent_arguments_p (args))
 			  postfix_expression
-			    = perform_koenig_lookup (postfix_expression, args);
+			    = perform_koenig_lookup (postfix_expression, args, false);
 		      }
 		    else
 		      postfix_expression
@@ -5191,7 +5195,7 @@ 
 			koenig_p = true;
 			if (!any_type_dependent_arguments_p (args))
 			  postfix_expression
-			    = perform_koenig_lookup (postfix_expression, args);
+			    = perform_koenig_lookup (postfix_expression, args, false);
 		      }
 		  }
 	      }
@@ -8576,6 +8580,172 @@ 
   return cp_parser_expression (parser, /*cast_p=*/false, NULL);
 }
 
+/* Tries to parse a range-based for:
+
+  range-based-for:
+    type-specifier-seq declarator : expression
+
+  If succesful, assigns to *DECL the DECLARATOR and to *EXPR the
+  expression. Note that the *DECL is returned unfinished, so
+  later it should call cp_finish_decl().
+
+  Returns TRUE iff a range-based for is parsed. */
+
+static bool
+cp_parser_for_range(cp_parser *parser, tree *decl, tree *expr)
+{
+  cp_decl_specifier_seq type_specifiers;
+  const char *saved_message;
+  cp_declarator *declarator;
+  tree attributes, pushed_scope;
+
+  cp_parser_parse_tentatively (parser);
+  /* New types are not allowed in the type-specifier-seq for a
+     range-based for loop.  */
+  saved_message = parser->type_definition_forbidden_message;
+  parser->type_definition_forbidden_message
+    = G_("types may not be defined in range-based for loops");
+  /* Parse the type-specifier-seq.  */
+  cp_parser_type_specifier_seq (parser, /*is_declaration==*/true,
+				/*is_trailing_return=*/false,
+				&type_specifiers);
+  /* Restore the saved message.  */
+  parser->type_definition_forbidden_message = saved_message;
+  /* If all is well, we might be looking at a declaration.  */
+  if (cp_parser_error_occurred (parser))
+  {
+    cp_parser_abort_tentative_parse (parser);
+    return false;
+  }
+  /* Parse the declarator.  */
+  declarator = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED,
+          /*ctor_dtor_or_conv_p=*/NULL,
+          /*parenthesized_p=*/NULL,
+          /*member_p=*/false);
+  /* Parse the attributes.  */
+  attributes = cp_parser_attributes_opt (parser);
+  /* The next token should be `:'. */
+  if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+      cp_parser_simulate_error (parser);
+
+  /* Check if it is a range-based for */
+  if (!cp_parser_parse_definitely (parser))
+      return false;
+
+  cp_parser_require (parser, CPP_COLON, RT_COLON);
+  *expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
+
+  /* Create the declaration.  */
+  *decl = start_decl (declarator, &type_specifiers,
+          /*initialized_p=*/SD_INITIALIZED,
+          attributes, /*prefix_attributes=*/NULL_TREE,
+          &pushed_scope);
+  if (pushed_scope) /* No scopes allowed here */
+      pop_scope(pushed_scope);
+  return true;
+}
+
+/* Builds a range-based for loop.
+   The parameters RANGE_DECL and RANGE_EXPR are as returned
+   from the cp_parser_for_range() function.
+
+      for (RANGE_DECL : RANGE_EXPR)
+  	BLOCK
+
+   should be built as:
+      {
+  	auto &&__range = RANGE_EXPR;
+  	for (auto __begin = BEGIN_EXPR, end = END_EXPR;
+  	      __begin != __end;
+  	      ++__begin)
+  	{
+  	    RANGE_DECL = *__begin;
+  	    BLOCK
+  	}
+      }
+   If RANGE_EXPR is an array:
+       BEGIN_EXPR = __range
+       END_EXPR = __range + countof(__range)
+   Else:
+  	BEGIN_EXPR = begin(__range)
+  	END_EXPR = end(__range);
+
+   When calling begin()/end() we must use argument dependent
+   lookup, but always considering 'std' as an associated namespace.
+*/
+static void
+cp_finish_range_based_for (tree statement, tree range_decl, tree range_expr)
+{
+  tree range_type, range_temp;
+  tree begin, end;
+  tree iter_type, begin_expr, end_expr;
+  tree condition, expression;
+  VEC(tree,gc) *vec;
+
+  /* Find out the type deduced by the declaration `auto &&__range = range_expr' */
+  range_type = cp_build_reference_type (make_auto (), true);
+  range_type = do_auto_deduction (range_type, range_expr, type_uses_auto (range_type));
+
+  /* Create the __range variable */
+  range_temp = create_temporary_var (range_type);
+  add_decl_expr (range_temp);
+  finish_expr_stmt (cp_build_modify_expr (range_temp, INIT_EXPR, range_expr,
+	tf_warning_or_error)); /* tf_none */
+
+  /* We must call begin(__range)/end__range() */
+  begin_expr = get_identifier ("begin");
+  vec = make_tree_vector ();
+  VEC_safe_push (tree, gc, vec, range_expr);
+  begin_expr = perform_koenig_lookup (begin_expr, vec, /*include_std=*/true);
+  begin_expr = finish_call_expr (begin_expr, &vec, false, true, tf_warning_or_error);
+  release_tree_vector (vec);
+
+  end_expr = get_identifier ("end");
+  vec = make_tree_vector ();
+  VEC_safe_push (tree, gc, vec, range_expr);
+  end_expr = perform_koenig_lookup (end_expr, vec, /*include_std=*/true);
+  end_expr = finish_call_expr (end_expr, &vec, false, true, tf_warning_or_error);
+  release_tree_vector (vec);
+
+  /* The type of the __begin and __end temporaries should be the same
+   * as required by the multiple auto declaration */
+  if (!same_type_p (TREE_TYPE (begin_expr), TREE_TYPE (end_expr)))
+  {
+    error ("inconsistent begin/end types in range-based for: %qT and %qT",
+	TREE_TYPE (begin_expr), TREE_TYPE (end_expr));
+  }
+  iter_type = TREE_TYPE (begin_expr);
+
+  /* The new for initialization statement */
+  begin = create_temporary_var (iter_type);
+  add_decl_expr (begin);
+  finish_expr_stmt (cp_build_modify_expr (begin, INIT_EXPR, begin_expr,
+	tf_warning_or_error));
+  end = create_temporary_var (iter_type);
+  add_decl_expr (end);
+  finish_expr_stmt (cp_build_modify_expr (end, INIT_EXPR, end_expr,
+	tf_warning_or_error));
+  finish_for_init_stmt (statement);
+
+/* The new for condition */
+  condition = build_x_binary_op (NE_EXPR,
+      begin, ERROR_MARK,
+      end, ERROR_MARK,
+      NULL, tf_warning_or_error);
+  finish_for_cond (condition, statement);
+
+  /* The new increment expression */
+  expression = finish_unary_op_expr (PREINCREMENT_EXPR, begin);
+  finish_for_expr (expression, statement);
+
+  /* The declaration is initialized with *__begin inside the loop body */
+  cp_finish_decl (range_decl,
+      build_x_indirect_ref (begin, RO_NULL, tf_warning_or_error),
+      /*is_constant_init*/false, NULL_TREE,
+      LOOKUP_ONLYCONVERTING);
+}
+
+
 /* Parse an iteration-statement.
 
    iteration-statement:
@@ -8657,31 +8827,41 @@ 
 
     case RID_FOR:
       {
-	tree condition = NULL_TREE;
-	tree expression = NULL_TREE;
+        tree range_decl, range_expr;
 
 	/* Begin the for-statement.  */
 	statement = begin_for_stmt ();
 	/* Look for the `('.  */
 	cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
-	/* Parse the initialization.  */
-	cp_parser_for_init_statement (parser);
-	finish_for_init_stmt (statement);
 
-	/* If there's a condition, process it.  */
-	if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
-	  condition = cp_parser_condition (parser);
-	finish_for_cond (condition, statement);
-	/* Look for the `;'.  */
-	cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
+	if (cxx_dialect == cxx0x && cp_parser_for_range (parser, &range_decl, &range_expr))
+	{
+	  cp_finish_range_based_for (statement, range_decl, range_expr);
+	}
+	else
+	{
+	  tree condition = NULL_TREE;
+	  tree expression = NULL_TREE;
 
-	/* If there's an expression, process it.  */
-	if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
-	  expression = cp_parser_expression (parser, /*cast_p=*/false, NULL);
-	finish_for_expr (expression, statement);
-	/* Look for the `)'.  */
-	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+	  /* Parse the initialization.  */
+	  cp_parser_for_init_statement (parser);
+	  finish_for_init_stmt (statement);
 
+	  /* If there's a condition, process it.  */
+	  if (cp_lexer_next_token_is_not (parser->lexer, CPP_SEMICOLON))
+	    condition = cp_parser_condition (parser);
+	  finish_for_cond (condition, statement);
+	  /* Look for the `;'.  */
+	  cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
+
+	  /* If there's an expression, process it.  */
+	  if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
+	    expression = cp_parser_expression (parser, /*cast_p=*/false, NULL);
+	  finish_for_expr (expression, statement);
+	}
+        /* Look for the `)'.  */
+        cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+
 	/* Parse the body of the for-statement.  */
 	parser->in_statement = IN_ITERATION_STMT;
 	cp_parser_already_scoped_statement (parser);
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 163798)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -5229,7 +5229,7 @@ 
 extern tree finish_stmt_expr			(tree, bool);
 extern tree stmt_expr_value_expr		(tree);
 bool empty_expr_stmt_p				(tree);
-extern tree perform_koenig_lookup		(tree, VEC(tree,gc) *);
+extern tree perform_koenig_lookup		(tree, VEC(tree,gc) *, bool);
 extern tree finish_call_expr			(tree, VEC(tree,gc) **, bool,
 						 bool, tsubst_flags_t);
 extern tree finish_increment_expr		(tree, enum tree_code);
Index: gcc/cp/name-lookup.c
===================================================================
--- gcc/cp/name-lookup.c	(revision 163798)
+++ gcc/cp/name-lookup.c	(working copy)
@@ -4389,7 +4389,7 @@ 
     lookup_arg_dependent (name,
 			  lookup_name_real (name, 0, 1, block_p, 0,
 					    LOOKUP_COMPLAIN),
-			  args);
+			  args, false);
 }
 
 tree
@@ -5063,7 +5063,7 @@ 
    are the functions found in normal lookup.  */
 
 tree
-lookup_arg_dependent (tree name, tree fns, VEC(tree,gc) *args)
+lookup_arg_dependent (tree name, tree fns, VEC(tree,gc) *args, bool include_std)
 {
   struct arg_lookup k;
 
@@ -5086,6 +5086,8 @@ 
      picking up later definitions) in the second stage. */
   k.namespaces = make_tree_vector ();
 
+  if (include_std)
+      arg_assoc_namespace(&k, std_node);
   arg_assoc_args_vec (&k, args);
 
   fns = k.functions;
Index: gcc/cp/name-lookup.h
===================================================================
--- gcc/cp/name-lookup.h	(revision 163798)
+++ gcc/cp/name-lookup.h	(working copy)
@@ -342,7 +342,7 @@ 
 extern void do_local_using_decl (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern void do_using_directive (tree);
-extern tree lookup_arg_dependent (tree, tree, VEC(tree,gc) *);
+extern tree lookup_arg_dependent (tree, tree, VEC(tree,gc) *, bool);
 extern bool is_associated_namespace (tree, tree);
 extern void parse_using_directive (tree, tree);
 extern tree innermost_non_namespace_value (tree);