Commit 964c809f by Ian Lance Taylor

compiler: in range, evaluate array if it has receives or calls

    
    The last change was incomplete, in that it did not evaluate the array
    argument in some cases where it had to be evaluated.  This reuses the
    existing code for checking whether len/cap is constant.
    
    Also clean up the use of _ as the second variable in a for/range,
    which was previously inconsistent depending on whether the statement
    used = or :=.
    
    Updates golang/go#22313
    
    Reviewed-on: https://go-review.googlesource.com/91715

From-SVN: r257377
parent 4cff15ea
312af623f48633989e9eb6e559ede84a23998ece 5031f878a761bf83f5f96710d62f83e2dc5ecf04
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -7957,8 +7957,10 @@ class Find_call_expression : public Traverse ...@@ -7957,8 +7957,10 @@ class Find_call_expression : public Traverse
int int
Find_call_expression::expression(Expression** pexpr) Find_call_expression::expression(Expression** pexpr)
{ {
if ((*pexpr)->call_expression() != NULL Expression* expr = *pexpr;
|| (*pexpr)->receive_expression() != NULL) if (!expr->is_constant()
&& (expr->call_expression() != NULL
|| expr->receive_expression() != NULL))
{ {
this->found_ = true; this->found_ = true;
return TRAVERSE_EXIT; return TRAVERSE_EXIT;
...@@ -7966,6 +7968,24 @@ Find_call_expression::expression(Expression** pexpr) ...@@ -7966,6 +7968,24 @@ Find_call_expression::expression(Expression** pexpr)
return TRAVERSE_CONTINUE; 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 // Return whether this is constant: len of a string constant, or len
// or cap of an array, or unsafe.Sizeof, unsafe.Offsetof, // or cap of an array, or unsafe.Sizeof, unsafe.Offsetof,
// unsafe.Alignof. // unsafe.Alignof.
...@@ -7993,19 +8013,9 @@ Builtin_call_expression::do_is_constant() const ...@@ -7993,19 +8013,9 @@ Builtin_call_expression::do_is_constant() const
&& !arg_type->points_to()->is_slice_type()) && !arg_type->points_to()->is_slice_type())
arg_type = arg_type->points_to(); 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 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; return true;
if (this->code_ == BUILTIN_LEN && arg_type->is_string_type()) if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
......
...@@ -2406,6 +2406,11 @@ class Builtin_call_expression : public Call_expression ...@@ -2406,6 +2406,11 @@ class Builtin_call_expression : public Call_expression
is_builtin() is_builtin()
{ return true; } { 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: protected:
// This overrides Call_expression::do_lower. // This overrides Call_expression::do_lower.
Expression* Expression*
......
...@@ -5459,8 +5459,7 @@ Parse::range_clause_decl(const Typed_identifier_list* til, ...@@ -5459,8 +5459,7 @@ Parse::range_clause_decl(const Typed_identifier_list* til,
no->var_value()->set_type_from_range_value(); no->var_value()->set_type_from_range_value();
if (is_new) if (is_new)
any_new = true; 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) if (!any_new)
......
...@@ -5311,11 +5311,12 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, ...@@ -5311,11 +5311,12 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
// constant, then we do not evaluate the range variable. len(x) is // 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 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 // 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; 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->array_type() != NULL
&& !range_type->is_slice_type()) && !range_type->is_slice_type()
&& Builtin_call_expression::array_len_is_constant(this->range_))
eval = false; eval = false;
Location loc = this->location(); Location loc = this->location();
...@@ -5341,7 +5342,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, ...@@ -5341,7 +5342,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
temp_block->add_statement(index_temp); temp_block->add_statement(index_temp);
Temporary_statement* value_temp = NULL; 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); value_temp = Statement::make_temporary(value_type, NULL, loc);
temp_block->add_statement(value_temp); temp_block->add_statement(value_temp);
...@@ -5393,7 +5394,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing, ...@@ -5393,7 +5394,7 @@ For_range_statement::do_lower(Gogo* gogo, Named_object*, Block* enclosing,
Statement* assign; Statement* assign;
Expression* index_ref = Expression* index_ref =
Expression::make_temporary_reference(index_temp, loc); 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); assign = Statement::make_assignment(this->index_var_, index_ref, loc);
else else
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment