diff options
Diffstat (limited to 'gcc/go/gofrontend/expressions.cc')
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 224 |
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); } |