diff mbox series

Go patch committed: In range, evaluate array if it has receives or calls

Message ID CAOyqgcWERq8yBAYw_0s5bP1Vuq7taAZ_J9Oz6m8hUSsC63ksig@mail.gmail.com
State New
Headers show
Series Go patch committed: In range, evaluate array if it has receives or calls | expand

Commit Message

Ian Lance Taylor Feb. 5, 2018, 1:50 a.m. UTC
The recent patch to the Go frontend to not evaluate an array value in
a range statement with a single iteration variable was too aggressive.
The array value should be evaluated if the expression has any calls or
receive expressions.  This patch fixes that, reusing the existing code
that checks whether len/cap of an array is a constant.  This patch
also cleans up the use of _ as the second variable in a forrange; it
was previous inconsistent depending on whether the statement used = or
:=.  This is a follow-on to https://golang.org/issue/22313.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
diff mbox series

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 257376)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-312af623f48633989e9eb6e559ede84a23998ece
+5031f878a761bf83f5f96710d62f83e2dc5ecf04
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 257376)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -7957,8 +7957,10 @@  class Find_call_expression : public Trav
 int
 Find_call_expression::expression(Expression** pexpr)
 {
-  if ((*pexpr)->call_expression() != NULL
-      || (*pexpr)->receive_expression() != NULL)
+  Expression* expr = *pexpr;
+  if (!expr->is_constant()
+      && (expr->call_expression() != NULL
+	  || expr->receive_expression() != NULL))
     {
       this->found_ = true;
       return TRAVERSE_EXIT;
@@ -7966,6 +7968,24 @@  Find_call_expression::expression(Express
   return TRAVERSE_CONTINUE;
 }
 
+// Return whether calling len or cap on EXPR, of array type, is a
+// constant.  The language spec says "the expressions len(s) and
+// cap(s) are constants if the type of s is an array or pointer to an
+// array and the expression s does not contain channel receives or
+// (non-constant) function calls."
+
+bool
+Builtin_call_expression::array_len_is_constant(Expression* expr)
+{
+  go_assert(expr->type()->deref()->array_type() != NULL
+	    && !expr->type()->deref()->is_slice_type());
+  if (expr->is_constant())
+    return true;
+  Find_call_expression find_call;
+  Expression::traverse(&expr, &find_call);
+  return !find_call.found();
+}
+
 // Return whether this is constant: len of a string constant, or len
 // or cap of an array, or unsafe.Sizeof, unsafe.Offsetof,
 // unsafe.Alignof.
@@ -7993,19 +8013,9 @@  Builtin_call_expression::do_is_constant(
 	    && !arg_type->points_to()->is_slice_type())
 	  arg_type = arg_type->points_to();
 
-	// The len and cap functions are only constant if there are no
-	// function calls or channel operations in the arguments.
-	// Otherwise we have to make the call.
-	if (!arg->is_constant())
-	  {
-	    Find_call_expression find_call;
-	    Expression::traverse(&arg, &find_call);
-	    if (find_call.found())
-	      return false;
-	  }
-
 	if (arg_type->array_type() != NULL
-	    && arg_type->array_type()->length() != NULL)
+	    && arg_type->array_type()->length() != NULL
+	    && Builtin_call_expression::array_len_is_constant(arg))
 	  return true;
 
 	if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h	(revision 257357)
+++ gcc/go/gofrontend/expressions.h	(working copy)
@@ -2406,6 +2406,11 @@  class Builtin_call_expression : public C
   is_builtin()
   { return true; }
 
+  // Return whether EXPR, of array type, is a constant if passed to
+  // len or cap.
+  static bool
+  array_len_is_constant(Expression* expr);
+
  protected:
   // This overrides Call_expression::do_lower.
   Expression*
Index: gcc/go/gofrontend/parse.cc
===================================================================
--- gcc/go/gofrontend/parse.cc	(revision 257374)
+++ gcc/go/gofrontend/parse.cc	(working copy)
@@ -5459,8 +5459,7 @@  Parse::range_clause_decl(const Typed_ide
 	no->var_value()->set_type_from_range_value();
       if (is_new)
 	any_new = true;
-      if (!Gogo::is_sink_name(pti->name()))
-        p_range_clause->value = Expression::make_var_reference(no, location);
+      p_range_clause->value = Expression::make_var_reference(no, location);
     }
 
   if (!any_new)
Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc	(revision 257357)
+++ gcc/go/gofrontend/statements.cc	(working copy)
@@ -5311,11 +5311,12 @@  For_range_statement::do_lower(Gogo* gogo
   // constant, then we do not evaluate the range variable.  len(x) is
   // a contant if x is a string constant or if x is an array.  If x is
   // a constant then evaluating it won't make any difference, so the
-  // only case to consider is when x is an array.
+  // only case to consider is when x is an array whose length is constant.
   bool eval = true;
-  if (this->value_var_ == NULL
+  if ((this->value_var_ == NULL || this->value_var_->is_sink_expression())
       && range_type->array_type() != NULL
-      && !range_type->is_slice_type())
+      && !range_type->is_slice_type()
+      && Builtin_call_expression::array_len_is_constant(this->range_))
     eval = false;
 
   Location loc = this->location();
@@ -5341,7 +5342,7 @@  For_range_statement::do_lower(Gogo* gogo
   temp_block->add_statement(index_temp);
 
   Temporary_statement* value_temp = NULL;
-  if (this->value_var_ != NULL)
+  if (this->value_var_ != NULL && !this->value_var_->is_sink_expression())
     {
       value_temp = Statement::make_temporary(value_type, NULL, loc);
       temp_block->add_statement(value_temp);
@@ -5393,7 +5394,7 @@  For_range_statement::do_lower(Gogo* gogo
       Statement* assign;
       Expression* index_ref =
 	Expression::make_temporary_reference(index_temp, loc);
-      if (this->value_var_ == NULL)
+      if (this->value_var_ == NULL || this->value_var_->is_sink_expression())
 	assign = Statement::make_assignment(this->index_var_, index_ref, loc);
       else
 	{