summaryrefslogtreecommitdiff
path: root/gcc/go/gofrontend/expressions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/go/gofrontend/expressions.cc')
-rw-r--r--gcc/go/gofrontend/expressions.cc224
1 files changed, 162 insertions, 62 deletions
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index bd437c4ce4c..433a6d7d431 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -926,7 +926,8 @@ Parser_expression::do_type()
// if necessary.
Expression*
-Var_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Var_expression::do_lower(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter, int)
{
if (this->variable_->is_variable())
{
@@ -935,8 +936,11 @@ Var_expression::do_lower(Gogo* gogo, Named_object* function, int)
// reference to a variable which is local to an enclosing
// function will be a reference to a field in a closure.
if (var->is_global())
- function = NULL;
- var->lower_init_expression(gogo, function);
+ {
+ function = NULL;
+ inserter = NULL;
+ }
+ var->lower_init_expression(gogo, function, inserter);
}
return this;
}
@@ -1061,7 +1065,9 @@ Temporary_reference_expression::do_get_tree(Translate_context* context)
// that here by adding a type cast. We need to use base() to push
// the circularity down one level.
tree ret = var_to_tree(bvar);
- if (POINTER_TYPE_P(TREE_TYPE(ret)) && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret))))
+ if (!this->is_lvalue_
+ && POINTER_TYPE_P(TREE_TYPE(ret))
+ && VOID_TYPE_P(TREE_TYPE(TREE_TYPE(ret))))
{
Btype* type_btype = this->type()->base()->get_backend(context->gogo());
tree type_tree = type_to_tree(type_btype);
@@ -1072,7 +1078,7 @@ Temporary_reference_expression::do_get_tree(Translate_context* context)
// Make a reference to a temporary variable.
-Expression*
+Temporary_reference_expression*
Expression::make_temporary_reference(Temporary_statement* statement,
source_location location)
{
@@ -1302,7 +1308,7 @@ Unknown_expression::name() const
// Lower a reference to an unknown name.
Expression*
-Unknown_expression::do_lower(Gogo*, Named_object*, int)
+Unknown_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
source_location location = this->location();
Named_object* no = this->named_object_;
@@ -2394,7 +2400,7 @@ class Const_expression : public Expression
do_traverse(Traverse*);
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const
@@ -2462,7 +2468,8 @@ Const_expression::do_traverse(Traverse* traverse)
// predeclared constant iota into an integer value.
Expression*
-Const_expression::do_lower(Gogo* gogo, Named_object*, int iota_value)
+Const_expression::do_lower(Gogo* gogo, Named_object*,
+ Statement_inserter*, int iota_value)
{
if (this->constant_->const_value()->expr()->classification()
== EXPRESSION_IOTA)
@@ -2931,7 +2938,7 @@ class Iota_expression : public Parser_expression
protected:
Expression*
- do_lower(Gogo*, Named_object*, int)
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{ go_unreachable(); }
// There should only ever be one of these.
@@ -2988,7 +2995,7 @@ class Type_conversion_expression : public Expression
do_traverse(Traverse* traverse);
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const
@@ -3057,7 +3064,8 @@ Type_conversion_expression::do_traverse(Traverse* traverse)
// Convert to a constant at lowering time.
Expression*
-Type_conversion_expression::do_lower(Gogo*, Named_object*, int)
+Type_conversion_expression::do_lower(Gogo*, Named_object*,
+ Statement_inserter*, int)
{
Type* type = this->type_;
Expression* val = this->expr_;
@@ -3753,7 +3761,7 @@ class Unary_expression : public Expression
{ return Expression::traverse(&this->expr_, traverse); }
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const;
@@ -3808,7 +3816,7 @@ class Unary_expression : public Expression
// instead.
Expression*
-Unary_expression::do_lower(Gogo*, Named_object*, int)
+Unary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
source_location loc = this->location();
Operator op = this->op_;
@@ -5137,7 +5145,7 @@ Binary_expression::eval_complex(Operator op, Type* left_type,
// constants.
Expression*
-Binary_expression::do_lower(Gogo*, Named_object*, int)
+Binary_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
source_location location = this->location();
Operator op = this->op_;
@@ -6656,7 +6664,7 @@ class Builtin_call_expression : public Call_expression
protected:
// This overrides Call_expression::do_lower.
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
bool
do_is_constant() const;
@@ -6864,7 +6872,8 @@ Find_call_expression::expression(Expression** pexpr)
// specific expressions. We also convert to a constant if we can.
Expression*
-Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter, int)
{
if (this->classification() == EXPRESSION_ERROR)
return this;
@@ -6974,7 +6983,7 @@ Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, int)
this->set_is_error();
return this;
}
- return this->lower_varargs(gogo, function, slice_type, 2);
+ return this->lower_varargs(gogo, function, inserter, slice_type, 2);
}
return this;
@@ -8553,9 +8562,10 @@ Call_expression::do_traverse(Traverse* traverse)
// Lower a call statement.
Expression*
-Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Call_expression::do_lower(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter, int)
{
- // A type case can look like a function call.
+ // A type cast can look like a function call.
if (this->fn_->is_type_expression()
&& this->args_ != NULL
&& this->args_->size() == 1)
@@ -8597,6 +8607,29 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
}
}
+ // If this call returns multiple results, create a temporary
+ // variable for each result.
+ size_t rc = this->result_count();
+ if (rc > 1 && this->results_ == NULL)
+ {
+ std::vector<Temporary_statement*>* temps =
+ new std::vector<Temporary_statement*>;
+ temps->reserve(rc);
+ const Typed_identifier_list* results =
+ this->fn_->type()->function_type()->results();
+ for (Typed_identifier_list::const_iterator p = results->begin();
+ p != results->end();
+ ++p)
+ {
+ Temporary_statement* temp = Statement::make_temporary(p->type(),
+ NULL,
+ p->location());
+ inserter->insert(temp);
+ temps->push_back(temp);
+ }
+ this->results_ = temps;
+ }
+
// Handle a call to a varargs function by packaging up the extra
// parameters.
if (this->fn_->type()->function_type() != NULL
@@ -8606,7 +8639,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
const Typed_identifier_list* parameters = fntype->parameters();
go_assert(parameters != NULL && !parameters->empty());
Type* varargs_type = parameters->back().type();
- return this->lower_varargs(gogo, function, varargs_type,
+ return this->lower_varargs(gogo, function, inserter, varargs_type,
parameters->size());
}
@@ -8622,6 +8655,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, int)
Expression*
Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter,
Type* varargs_type, size_t param_count)
{
if (this->varargs_are_lowered_)
@@ -8702,13 +8736,12 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
// Lower all the new subexpressions.
Expression* ret = this;
- gogo->lower_expression(function, &ret);
+ gogo->lower_expression(function, inserter, &ret);
go_assert(ret == this);
return ret;
}
-// Get the function type. Returns NULL if we don't know the type. If
-// this returns NULL, and if_ERROR is true, issues an error.
+// Get the function type. This can return NULL in error cases.
Function_type*
Call_expression::get_function_type() const
@@ -8729,6 +8762,16 @@ Call_expression::result_count() const
return fntype->results()->size();
}
+// Return the temporary which holds a result.
+
+Temporary_statement*
+Call_expression::result(size_t i) const
+{
+ go_assert(this->results_ != NULL
+ && this->results_->size() > i);
+ return (*this->results_)[i];
+}
+
// Return whether this is a call to the predeclared function recover.
bool
@@ -8759,6 +8802,21 @@ Call_expression::do_set_recover_arg(Expression*)
go_unreachable();
}
+// We have found an error with this call expression; return true if
+// we should report it.
+
+bool
+Call_expression::issue_error()
+{
+ if (this->issued_error_)
+ return false;
+ else
+ {
+ this->issued_error_ = true;
+ return true;
+ }
+}
+
// Get the type.
Type*
@@ -8941,15 +8999,12 @@ Call_expression::do_check_types(Gogo*)
// Return whether we have to use a temporary variable to ensure that
// we evaluate this call expression in order. If the call returns no
-// results then it will inevitably be executed last. If the call
-// returns more than one result then it will be used with Call_result
-// expressions. So we only have to use a temporary variable if the
-// call returns exactly one result.
+// results then it will inevitably be executed last.
bool
Call_expression::do_must_eval_in_order() const
{
- return this->result_count() == 1;
+ return this->result_count() > 0;
}
// Get the function and the first argument to use when calling a bound
@@ -9193,16 +9248,56 @@ Call_expression::do_get_tree(Translate_context* context)
ret = build1(NOP_EXPR, rettype, ret);
}
- // If there is more than one result, we will refer to the call
- // multiple times.
- if (fntype->results() != NULL && fntype->results()->size() > 1)
- ret = save_expr(ret);
+ if (this->results_ != NULL)
+ ret = this->set_results(context, ret);
this->tree_ = ret;
return ret;
}
+// Set the result variables if this call returns multiple results.
+
+tree
+Call_expression::set_results(Translate_context* context, tree call_tree)
+{
+ tree stmt_list = NULL_TREE;
+
+ call_tree = save_expr(call_tree);
+
+ if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE)
+ {
+ go_assert(saw_errors());
+ return call_tree;
+ }
+
+ source_location loc = this->location();
+ tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
+ size_t rc = this->result_count();
+ for (size_t i = 0; i < rc; ++i, field = DECL_CHAIN(field))
+ {
+ go_assert(field != NULL_TREE);
+
+ Temporary_statement* temp = this->result(i);
+ Temporary_reference_expression* ref =
+ Expression::make_temporary_reference(temp, loc);
+ ref->set_is_lvalue();
+ tree temp_tree = ref->get_tree(context);
+ if (temp_tree == error_mark_node)
+ continue;
+
+ tree val_tree = build3_loc(loc, COMPONENT_REF, TREE_TYPE(field),
+ call_tree, field, NULL_TREE);
+ tree set_tree = build2_loc(loc, MODIFY_EXPR, void_type_node, temp_tree,
+ val_tree);
+
+ append_to_statement_list(set_tree, &stmt_list);
+ }
+ go_assert(field == NULL_TREE);
+
+ return save_expr(stmt_list);
+}
+
// Make a call expression.
Call_expression*
@@ -9292,10 +9387,11 @@ Call_result_expression::do_type()
return Type::make_error_type();
}
const Typed_identifier_list* results = fntype->results();
- if (results == NULL)
+ if (results == NULL || results->size() < 2)
{
- this->report_error(_("number of results does not match "
- "number of values"));
+ if (ce->issue_error())
+ this->report_error(_("number of results does not match "
+ "number of values"));
return Type::make_error_type();
}
Typed_identifier_list::const_iterator pr = results->begin();
@@ -9307,8 +9403,9 @@ Call_result_expression::do_type()
}
if (pr == results->end())
{
- this->report_error(_("number of results does not match "
- "number of values"));
+ if (ce->issue_error())
+ this->report_error(_("number of results does not match "
+ "number of values"));
return Type::make_error_type();
}
return pr->type();
@@ -9332,27 +9429,18 @@ Call_result_expression::do_determine_type(const Type_context*)
this->call_->determine_type_no_context();
}
-// Return the tree.
+// Return the tree. We just refer to the temporary set by the call
+// expression. We don't do this at lowering time because it makes it
+// hard to evaluate the call at the right time.
tree
Call_result_expression::do_get_tree(Translate_context* context)
{
- tree call_tree = this->call_->get_tree(context);
- if (call_tree == error_mark_node)
- return error_mark_node;
- if (TREE_CODE(TREE_TYPE(call_tree)) != RECORD_TYPE)
- {
- go_assert(saw_errors());
- return error_mark_node;
- }
- tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
- for (unsigned int i = 0; i < this->index_; ++i)
- {
- go_assert(field != NULL_TREE);
- field = DECL_CHAIN(field);
- }
- go_assert(field != NULL_TREE);
- return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+ Call_expression* ce = this->call_->call_expression();
+ go_assert(ce != NULL);
+ Temporary_statement* ts = ce->result(this->index_);
+ Expression* ref = Expression::make_temporary_reference(ts, this->location());
+ return ref->get_tree(context);
}
// Make a reference to a single result of a call which returns
@@ -9383,7 +9471,7 @@ Index_expression::do_traverse(Traverse* traverse)
// expression into an array index, a string index, or a map index.
Expression*
-Index_expression::do_lower(Gogo*, Named_object*, int)
+Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{
source_location location = this->location();
Expression* left = this->left_;
@@ -10542,7 +10630,7 @@ class Selector_expression : public Parser_expression
{ return Expression::traverse(&this->left_, traverse); }
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_copy()
@@ -10565,7 +10653,8 @@ class Selector_expression : public Parser_expression
// hand side.
Expression*
-Selector_expression::do_lower(Gogo* gogo, Named_object*, int)
+Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*,
+ int)
{
Expression* left = this->left_;
if (left->is_type_expression())
@@ -10734,6 +10823,8 @@ Selector_expression::lower_method_expression(Gogo* gogo)
}
}
+ gogo->start_block(location);
+
Call_expression* call = Expression::make_call(bm, args,
method_type->is_varargs(),
location);
@@ -10756,6 +10847,13 @@ Selector_expression::lower_method_expression(Gogo* gogo)
}
gogo->add_statement(s);
+ Block* b = gogo->finish_block(location);
+
+ gogo->add_block(b, location);
+
+ // Lower the call in case there are multiple results.
+ gogo->lower_block(no, b);
+
gogo->finish_function(location);
return Expression::make_func_reference(no, NULL, location);
@@ -11860,7 +11958,7 @@ class Composite_literal_expression : public Parser_expression
do_traverse(Traverse* traverse);
Expression*
- do_lower(Gogo*, Named_object*, int);
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_copy()
@@ -11884,7 +11982,7 @@ class Composite_literal_expression : public Parser_expression
make_array(Type*, Expression_list*);
Expression*
- lower_map(Gogo*, Named_object*, Type*);
+ lower_map(Gogo*, Named_object*, Statement_inserter*, Type*);
// The type of the composite literal.
Type* type_;
@@ -11913,7 +12011,8 @@ Composite_literal_expression::do_traverse(Traverse* traverse)
// the type.
Expression*
-Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int)
+Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter, int)
{
Type* type = this->type_;
@@ -11940,7 +12039,7 @@ Composite_literal_expression::do_lower(Gogo* gogo, Named_object* function, int)
else if (type->array_type() != NULL)
return this->lower_array(type);
else if (type->map_type() != NULL)
- return this->lower_map(gogo, function, type);
+ return this->lower_map(gogo, function, inserter, type);
else
{
error_at(this->location(),
@@ -12244,6 +12343,7 @@ Composite_literal_expression::make_array(Type* type, Expression_list* vals)
Expression*
Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function,
+ Statement_inserter* inserter,
Type* type)
{
source_location location = this->location();
@@ -12272,7 +12372,7 @@ Composite_literal_expression::lower_map(Gogo* gogo, Named_object* function,
if ((*p)->unknown_expression() != NULL)
{
(*p)->unknown_expression()->clear_is_composite_literal_key();
- gogo->lower_expression(function, &*p);
+ gogo->lower_expression(function, inserter, &*p);
go_assert((*p)->is_error_expression());
return Expression::make_error(location);
}