Use backend interface for return statements.
* go-gcc.cc: #include "tree-iterator.h", "gimple.h", and "gogo.h". (class Bfunction): Define. (Gcc_backend::assignment_statement): Rename from assignment. Check for errors. (Gcc_backend::return_statement): New function. (tree_to_function): New function. * Make-lang.in (go/go-gcc.o): Depend on tree-iterator.h, $(GIMPLE_H), and $(GO_GOGO_H). From-SVN: r171959
This commit is contained in:
parent
69387b92c1
commit
9403944776
@ -1,3 +1,14 @@
|
||||
2011-04-04 Ian Lance Taylor <iant@google.com>
|
||||
|
||||
* go-gcc.cc: #include "tree-iterator.h", "gimple.h", and "gogo.h".
|
||||
(class Bfunction): Define.
|
||||
(Gcc_backend::assignment_statement): Rename from assignment.
|
||||
Check for errors.
|
||||
(Gcc_backend::return_statement): New function.
|
||||
(tree_to_function): New function.
|
||||
* Make-lang.in (go/go-gcc.o): Depend on tree-iterator.h,
|
||||
$(GIMPLE_H), and $(GO_GOGO_H).
|
||||
|
||||
2011-04-03 Ian Lance Taylor <iant@google.com>
|
||||
|
||||
* go-gcc.cc: New file.
|
||||
|
@ -236,7 +236,8 @@ go/go-lang.o: go/go-lang.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(OPTS_H) \
|
||||
|
||||
GOINCLUDES = -I $(srcdir)/go -I $(srcdir)/go/gofrontend
|
||||
|
||||
go/go-gcc.o: go/go-gcc.cc $(GO_SYSTEM_H) $(TREE_H) go/gofrontend/backend.h
|
||||
go/go-gcc.o: go/go-gcc.cc $(GO_SYSTEM_H) $(TREE_H) tree-iterator.h \
|
||||
$(GIMPLE_H) $(GO_GOGO_H) go/gofrontend/backend.h
|
||||
$(CXX) -c $(GOINCLUDES) $(ALL_CPPFLAGS) $(ALL_CXXFLAGS) $< $(OUTPUT_OPTION)
|
||||
|
||||
go/%.o: go/gofrontend/%.cc
|
||||
|
@ -30,11 +30,14 @@ extern "C"
|
||||
#endif
|
||||
|
||||
#include "tree.h"
|
||||
#include "tree-iterator.h"
|
||||
#include "gimple.h"
|
||||
|
||||
#ifndef ENABLE_BUILD_WITH_CXX
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "gogo.h"
|
||||
#include "backend.h"
|
||||
|
||||
// A class wrapping a tree.
|
||||
@ -79,6 +82,14 @@ class Bstatement : public Gcc_tree
|
||||
{ }
|
||||
};
|
||||
|
||||
class Bfunction : public Gcc_tree
|
||||
{
|
||||
public:
|
||||
Bfunction(tree t)
|
||||
: Gcc_tree(t)
|
||||
{ }
|
||||
};
|
||||
|
||||
// This file implements the interface between the Go frontend proper
|
||||
// and the gcc IR. This implements specific instantiations of
|
||||
// abstract classes defined by the Go frontend proper. The Go
|
||||
@ -149,8 +160,12 @@ class Gcc_backend : public Backend
|
||||
|
||||
// Create an assignment statement.
|
||||
Bstatement*
|
||||
assignment(Bexpression* lhs, Bexpression* rhs,
|
||||
source_location location);
|
||||
assignment_statement(Bexpression* lhs, Bexpression* rhs, source_location);
|
||||
|
||||
// Create a return statement.
|
||||
Bstatement*
|
||||
return_statement(Bfunction*, const std::vector<Bexpression*>&,
|
||||
source_location);
|
||||
|
||||
private:
|
||||
// Make a Bstatement from a tree.
|
||||
@ -162,13 +177,76 @@ class Gcc_backend : public Backend
|
||||
// Assignment.
|
||||
|
||||
Bstatement*
|
||||
Gcc_backend::assignment(Bexpression* lhs, Bexpression* rhs,
|
||||
source_location location)
|
||||
Gcc_backend::assignment_statement(Bexpression* lhs, Bexpression* rhs,
|
||||
source_location location)
|
||||
{
|
||||
tree lhs_tree = lhs->get_tree();
|
||||
tree rhs_tree = rhs->get_tree();
|
||||
if (lhs_tree == error_mark_node || rhs_tree == error_mark_node)
|
||||
return this->make_statement(error_mark_node);
|
||||
return this->make_statement(fold_build2_loc(location, MODIFY_EXPR,
|
||||
void_type_node,
|
||||
lhs->get_tree(),
|
||||
rhs->get_tree()));
|
||||
lhs_tree, rhs_tree));
|
||||
}
|
||||
|
||||
// Return.
|
||||
|
||||
Bstatement*
|
||||
Gcc_backend::return_statement(Bfunction* bfunction,
|
||||
const std::vector<Bexpression*>& vals,
|
||||
source_location location)
|
||||
{
|
||||
tree fntree = bfunction->get_tree();
|
||||
if (fntree == error_mark_node)
|
||||
return this->make_statement(error_mark_node);
|
||||
tree result = DECL_RESULT(fntree);
|
||||
if (result == error_mark_node)
|
||||
return this->make_statement(error_mark_node);
|
||||
tree ret;
|
||||
if (vals.empty())
|
||||
ret = fold_build1_loc(location, RETURN_EXPR, void_type_node, NULL_TREE);
|
||||
else if (vals.size() == 1)
|
||||
{
|
||||
tree val = vals.front()->get_tree();
|
||||
if (val == error_mark_node)
|
||||
return this->make_statement(error_mark_node);
|
||||
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
|
||||
result, vals.front()->get_tree());
|
||||
ret = fold_build1_loc(location, RETURN_EXPR, void_type_node, set);
|
||||
}
|
||||
else
|
||||
{
|
||||
// To return multiple values, copy the values into a temporary
|
||||
// variable of the right structure type, and then assign the
|
||||
// temporary variable to the DECL_RESULT in the return
|
||||
// statement.
|
||||
tree stmt_list = NULL_TREE;
|
||||
tree rettype = TREE_TYPE(result);
|
||||
tree rettmp = create_tmp_var(rettype, "RESULT");
|
||||
tree field = TYPE_FIELDS(rettype);
|
||||
for (std::vector<Bexpression*>::const_iterator p = vals.begin();
|
||||
p != vals.end();
|
||||
p++, field = DECL_CHAIN(field))
|
||||
{
|
||||
gcc_assert(field != NULL_TREE);
|
||||
tree ref = fold_build3_loc(location, COMPONENT_REF, TREE_TYPE(field),
|
||||
rettmp, field, NULL_TREE);
|
||||
tree val = (*p)->get_tree();
|
||||
if (val == error_mark_node)
|
||||
return this->make_statement(error_mark_node);
|
||||
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
|
||||
ref, (*p)->get_tree());
|
||||
append_to_statement_list(set, &stmt_list);
|
||||
}
|
||||
gcc_assert(field == NULL_TREE);
|
||||
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
|
||||
result, rettmp);
|
||||
tree ret_expr = fold_build1_loc(location, RETURN_EXPR, void_type_node,
|
||||
set);
|
||||
append_to_statement_list(ret_expr, &stmt_list);
|
||||
ret = stmt_list;
|
||||
}
|
||||
return this->make_statement(ret);
|
||||
}
|
||||
|
||||
// The single backend.
|
||||
@ -192,6 +270,12 @@ tree_to_expr(tree t)
|
||||
return new Bexpression(t);
|
||||
}
|
||||
|
||||
Bfunction*
|
||||
tree_to_function(tree t)
|
||||
{
|
||||
return new Bfunction(t);
|
||||
}
|
||||
|
||||
tree
|
||||
statement_to_tree(Bstatement* bs)
|
||||
{
|
||||
|
@ -24,6 +24,9 @@ class Bexpression;
|
||||
// The backend representation of a statement.
|
||||
class Bstatement;
|
||||
|
||||
// The backend representation of a function definition.
|
||||
class Bfunction;
|
||||
|
||||
// A list of backend types.
|
||||
typedef std::vector<Btype*> Btypes;
|
||||
|
||||
@ -103,7 +106,14 @@ class Backend
|
||||
|
||||
// Create an assignment statement.
|
||||
virtual Bstatement*
|
||||
assignment(Bexpression* lhs, Bexpression* rhs, source_location location) = 0;
|
||||
assignment_statement(Bexpression* lhs, Bexpression* rhs,
|
||||
source_location) = 0;
|
||||
|
||||
// Create a return statement, passing the representation of the
|
||||
// function and the list of values to return.
|
||||
virtual Bstatement*
|
||||
return_statement(Bfunction*, const std::vector<Bexpression*>&,
|
||||
source_location) = 0;
|
||||
};
|
||||
|
||||
// The backend interface has to define this function.
|
||||
@ -114,6 +124,7 @@ extern Backend* go_get_backend();
|
||||
// interface.
|
||||
|
||||
extern Bexpression* tree_to_expr(tree);
|
||||
extern Bfunction* tree_to_function(tree);
|
||||
extern tree statement_to_tree(Bstatement*);
|
||||
|
||||
#endif // !defined(GO_BACKEND_H)
|
||||
|
@ -10415,8 +10415,7 @@ Selector_expression::lower_method_expression(Gogo* gogo)
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
retvals->push_back(Expression::make_call_result(call, i));
|
||||
}
|
||||
s = Statement::make_return_statement(no->func_value()->type()->results(),
|
||||
retvals, location);
|
||||
s = Statement::make_return_statement(retvals, location);
|
||||
}
|
||||
gogo->add_statement(s);
|
||||
|
||||
|
@ -1761,27 +1761,16 @@ Function::return_value(Gogo* gogo, Named_object* named_function,
|
||||
if (results == NULL || results->empty())
|
||||
return NULL_TREE;
|
||||
|
||||
// In the case of an exception handler created for functions with
|
||||
// defer statements, the result variables may be unnamed.
|
||||
bool is_named = !results->front().name().empty();
|
||||
if (is_named)
|
||||
gcc_assert(this->results_ != NULL);
|
||||
if (this->results_->size() != results->size())
|
||||
{
|
||||
gcc_assert(this->named_results_ != NULL);
|
||||
if (this->named_results_->size() != results->size())
|
||||
{
|
||||
gcc_assert(saw_errors());
|
||||
return error_mark_node;
|
||||
}
|
||||
gcc_assert(saw_errors());
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
tree retval;
|
||||
if (results->size() == 1)
|
||||
{
|
||||
if (is_named)
|
||||
return this->named_results_->front()->get_tree(gogo, named_function);
|
||||
else
|
||||
return results->front().type()->get_init_tree(gogo, false);
|
||||
}
|
||||
return this->results_->front()->get_tree(gogo, named_function);
|
||||
else
|
||||
{
|
||||
tree rettype = TREE_TYPE(DECL_RESULT(this->fndecl_));
|
||||
@ -1794,11 +1783,7 @@ Function::return_value(Gogo* gogo, Named_object* named_function,
|
||||
{
|
||||
gcc_assert(field != NULL);
|
||||
tree val;
|
||||
if (is_named)
|
||||
val = (*this->named_results_)[index]->get_tree(gogo,
|
||||
named_function);
|
||||
else
|
||||
val = pr->type()->get_init_tree(gogo, false);
|
||||
val = (*this->results_)[index]->get_tree(gogo, named_function);
|
||||
tree set = fold_build2_loc(location, MODIFY_EXPR, void_type_node,
|
||||
build3(COMPONENT_REF, TREE_TYPE(field),
|
||||
retval, field, NULL_TREE),
|
||||
|
@ -642,7 +642,7 @@ Gogo::start_function(const std::string& name, Function_type* type,
|
||||
}
|
||||
}
|
||||
|
||||
function->create_named_result_variables(this);
|
||||
function->create_result_variables(this);
|
||||
|
||||
const std::string* pname;
|
||||
std::string nested_name;
|
||||
@ -2195,8 +2195,7 @@ Build_recover_thunks::function(Named_object* orig_no)
|
||||
for (size_t i = 0; i < rc; ++i)
|
||||
vals->push_back(Expression::make_call_result(call, i));
|
||||
}
|
||||
s = Statement::make_return_statement(new_func->type()->results(),
|
||||
vals, location);
|
||||
s = Statement::make_return_statement(vals, location);
|
||||
}
|
||||
s->determine_types();
|
||||
gogo->add_statement(s);
|
||||
@ -2252,8 +2251,8 @@ Build_recover_thunks::function(Named_object* orig_no)
|
||||
new_func->traverse(&convert_recover);
|
||||
|
||||
// Update the function pointers in any named results.
|
||||
new_func->update_named_result_variables();
|
||||
orig_func->update_named_result_variables();
|
||||
new_func->update_result_variables();
|
||||
orig_func->update_result_variables();
|
||||
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
@ -2619,26 +2618,27 @@ Gogo::convert_named_types_in_bindings(Bindings* bindings)
|
||||
|
||||
Function::Function(Function_type* type, Function* enclosing, Block* block,
|
||||
source_location location)
|
||||
: type_(type), enclosing_(enclosing), named_results_(NULL),
|
||||
: type_(type), enclosing_(enclosing), results_(NULL),
|
||||
closure_var_(NULL), block_(block), location_(location), fndecl_(NULL),
|
||||
defer_stack_(NULL), calls_recover_(false), is_recover_thunk_(false),
|
||||
has_recover_thunk_(false)
|
||||
defer_stack_(NULL), results_are_named_(false), calls_recover_(false),
|
||||
is_recover_thunk_(false), has_recover_thunk_(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Create the named result variables.
|
||||
|
||||
void
|
||||
Function::create_named_result_variables(Gogo* gogo)
|
||||
Function::create_result_variables(Gogo* gogo)
|
||||
{
|
||||
const Typed_identifier_list* results = this->type_->results();
|
||||
if (results == NULL
|
||||
|| results->empty()
|
||||
|| results->front().name().empty())
|
||||
if (results == NULL || results->empty())
|
||||
return;
|
||||
|
||||
this->named_results_ = new Named_results();
|
||||
this->named_results_->reserve(results->size());
|
||||
if (!results->front().name().empty())
|
||||
this->results_are_named_ = true;
|
||||
|
||||
this->results_ = new Results();
|
||||
this->results_->reserve(results->size());
|
||||
|
||||
Block* block = this->block_;
|
||||
int index = 0;
|
||||
@ -2647,18 +2647,29 @@ Function::create_named_result_variables(Gogo* gogo)
|
||||
++p, ++index)
|
||||
{
|
||||
std::string name = p->name();
|
||||
if (Gogo::is_sink_name(name))
|
||||
if (name.empty() || Gogo::is_sink_name(name))
|
||||
{
|
||||
static int unnamed_result_counter;
|
||||
static int result_counter;
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof buf, "_$%d", unnamed_result_counter);
|
||||
++unnamed_result_counter;
|
||||
snprintf(buf, sizeof buf, "$ret%d", result_counter);
|
||||
++result_counter;
|
||||
name = gogo->pack_hidden_name(buf, false);
|
||||
}
|
||||
Result_variable* result = new Result_variable(p->type(), this, index);
|
||||
Named_object* no = block->bindings()->add_result_variable(name, result);
|
||||
if (no->is_result_variable())
|
||||
this->named_results_->push_back(no);
|
||||
this->results_->push_back(no);
|
||||
else
|
||||
{
|
||||
static int dummy_result_count;
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof buf, "$dret%d", dummy_result_count);
|
||||
++dummy_result_count;
|
||||
name = gogo->pack_hidden_name(buf, false);
|
||||
no = block->bindings()->add_result_variable(name, result);
|
||||
gcc_assert(no->is_result_variable());
|
||||
this->results_->push_back(no);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2666,13 +2677,13 @@ Function::create_named_result_variables(Gogo* gogo)
|
||||
// calls recover.
|
||||
|
||||
void
|
||||
Function::update_named_result_variables()
|
||||
Function::update_result_variables()
|
||||
{
|
||||
if (this->named_results_ == NULL)
|
||||
if (this->results_ == NULL)
|
||||
return;
|
||||
|
||||
for (Named_results::iterator p = this->named_results_->begin();
|
||||
p != this->named_results_->end();
|
||||
for (Results::iterator p = this->results_->begin();
|
||||
p != this->results_->end();
|
||||
++p)
|
||||
(*p)->result_var_value()->set_function(this);
|
||||
}
|
||||
@ -2819,7 +2830,7 @@ void
|
||||
Function::swap_for_recover(Function *x)
|
||||
{
|
||||
gcc_assert(this->enclosing_ == x->enclosing_);
|
||||
std::swap(this->named_results_, x->named_results_);
|
||||
std::swap(this->results_, x->results_);
|
||||
std::swap(this->closure_var_, x->closure_var_);
|
||||
std::swap(this->block_, x->block_);
|
||||
gcc_assert(this->location_ == x->location_);
|
||||
|
@ -822,14 +822,27 @@ class Function
|
||||
this->enclosing_ = enclosing;
|
||||
}
|
||||
|
||||
// Create the named result variables in the outer block.
|
||||
// The result variables.
|
||||
typedef std::vector<Named_object*> Results;
|
||||
|
||||
// Create the result variables in the outer block.
|
||||
void
|
||||
create_named_result_variables(Gogo*);
|
||||
create_result_variables(Gogo*);
|
||||
|
||||
// Update the named result variables when cloning a function which
|
||||
// calls recover.
|
||||
void
|
||||
update_named_result_variables();
|
||||
update_result_variables();
|
||||
|
||||
// Return the result variables.
|
||||
Results*
|
||||
result_variables()
|
||||
{ return this->results_; }
|
||||
|
||||
// Whether the result variables have names.
|
||||
bool
|
||||
results_are_named() const
|
||||
{ return this->results_are_named_; }
|
||||
|
||||
// Add a new field to the closure variable.
|
||||
void
|
||||
@ -992,8 +1005,6 @@ class Function
|
||||
void
|
||||
build_defer_wrapper(Gogo*, Named_object*, tree*, tree*);
|
||||
|
||||
typedef std::vector<Named_object*> Named_results;
|
||||
|
||||
typedef std::vector<std::pair<Named_object*,
|
||||
source_location> > Closure_fields;
|
||||
|
||||
@ -1002,8 +1013,8 @@ class Function
|
||||
// The enclosing function. This is NULL when there isn't one, which
|
||||
// is the normal case.
|
||||
Function* enclosing_;
|
||||
// The named result variables, if any.
|
||||
Named_results* named_results_;
|
||||
// The result variables, if any.
|
||||
Results* results_;
|
||||
// If there is a closure, this is the list of variables which appear
|
||||
// in the closure. This is created by the parser, and then resolved
|
||||
// to a real type when we lower parse trees.
|
||||
@ -1022,6 +1033,8 @@ class Function
|
||||
// A variable holding the defer stack variable. This is NULL unless
|
||||
// we actually need a defer stack.
|
||||
tree defer_stack_;
|
||||
// True if the result variables are named.
|
||||
bool results_are_named_;
|
||||
// True if this function calls the predeclared recover function.
|
||||
bool calls_recover_;
|
||||
// True if this a thunk built for a function which calls recover.
|
||||
|
@ -3732,10 +3732,7 @@ Parse::return_stat()
|
||||
Expression_list* vals = NULL;
|
||||
if (this->expression_may_start_here())
|
||||
vals = this->expression_list(NULL, false);
|
||||
const Function* function = this->gogo_->current_function()->func_value();
|
||||
const Typed_identifier_list* results = function->type()->results();
|
||||
this->gogo_->add_statement(Statement::make_return_statement(results, vals,
|
||||
location));
|
||||
this->gogo_->add_statement(Statement::make_return_statement(vals, location));
|
||||
}
|
||||
|
||||
// IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" Statement ] .
|
||||
|
@ -561,9 +561,10 @@ Assignment_statement::do_get_tree(Translate_context* context)
|
||||
if (rhs_tree == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
Bstatement* ret = context->backend()->assignment(tree_to_expr(lhs_tree),
|
||||
tree_to_expr(rhs_tree),
|
||||
this->location());
|
||||
Bstatement* ret;
|
||||
ret = context->backend()->assignment_statement(tree_to_expr(lhs_tree),
|
||||
tree_to_expr(rhs_tree),
|
||||
this->location());
|
||||
return statement_to_tree(ret);
|
||||
}
|
||||
|
||||
@ -2289,10 +2290,7 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
|
||||
|
||||
Expression_list* vals = new Expression_list();
|
||||
vals->push_back(Expression::make_boolean(false, location));
|
||||
const Typed_identifier_list* results =
|
||||
function->func_value()->type()->results();
|
||||
gogo->add_statement(Statement::make_return_statement(results, vals,
|
||||
location));
|
||||
gogo->add_statement(Statement::make_return_statement(vals, location));
|
||||
}
|
||||
|
||||
// That is all the thunk has to do.
|
||||
@ -2442,69 +2440,76 @@ Return_statement::do_traverse_assignments(Traverse_assignments* tassign)
|
||||
// panic/recover work correctly.
|
||||
|
||||
Statement*
|
||||
Return_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
|
||||
Return_statement::do_lower(Gogo*, Named_object* function, Block* enclosing)
|
||||
{
|
||||
if (this->vals_ == NULL)
|
||||
if (this->is_lowered_)
|
||||
return this;
|
||||
|
||||
const Typed_identifier_list* results = this->results_;
|
||||
if (results == NULL || results->empty())
|
||||
return this;
|
||||
Expression_list* vals = this->vals_;
|
||||
this->vals_ = NULL;
|
||||
this->is_lowered_ = true;
|
||||
|
||||
// If the current function has multiple return values, and we are
|
||||
// returning a single call expression, split up the call expression.
|
||||
size_t results_count = results->size();
|
||||
if (results_count > 1
|
||||
&& this->vals_->size() == 1
|
||||
&& this->vals_->front()->call_expression() != NULL)
|
||||
source_location loc = this->location();
|
||||
|
||||
size_t vals_count = vals == NULL ? 0 : vals->size();
|
||||
Function::Results* results = function->func_value()->result_variables();
|
||||
size_t results_count = results == NULL ? 0 : results->size();
|
||||
|
||||
if (vals_count == 0)
|
||||
{
|
||||
Call_expression* call = this->vals_->front()->call_expression();
|
||||
size_t count = results->size();
|
||||
Expression_list* vals = new Expression_list;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
vals->push_back(Expression::make_call_result(call, i));
|
||||
delete this->vals_;
|
||||
this->vals_ = vals;
|
||||
}
|
||||
|
||||
if (results->front().name().empty())
|
||||
return this;
|
||||
|
||||
if (results_count != this->vals_->size())
|
||||
{
|
||||
// Presumably an error which will be reported in check_types.
|
||||
if (results_count > 0 && !function->func_value()->results_are_named())
|
||||
{
|
||||
this->report_error(_("not enough arguments to return"));
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Assign to named return values and then return them.
|
||||
if (results_count == 0)
|
||||
{
|
||||
this->report_error(_("return with value in function "
|
||||
"with no return type"));
|
||||
return this;
|
||||
}
|
||||
|
||||
source_location loc = this->location();
|
||||
const Block* top = enclosing;
|
||||
while (top->enclosing() != NULL)
|
||||
top = top->enclosing();
|
||||
// If the current function has multiple return values, and we are
|
||||
// returning a single call expression, split up the call expression.
|
||||
if (results_count > 1
|
||||
&& vals->size() == 1
|
||||
&& vals->front()->call_expression() != NULL)
|
||||
{
|
||||
Call_expression* call = vals->front()->call_expression();
|
||||
delete vals;
|
||||
vals = new Expression_list;
|
||||
for (size_t i = 0; i < results_count; ++i)
|
||||
vals->push_back(Expression::make_call_result(call, i));
|
||||
vals_count = results_count;
|
||||
}
|
||||
|
||||
if (vals_count < results_count)
|
||||
{
|
||||
this->report_error(_("not enough arguments to return"));
|
||||
return this;
|
||||
}
|
||||
|
||||
if (vals_count > results_count)
|
||||
{
|
||||
this->report_error(_("too many values in return statement"));
|
||||
return this;
|
||||
}
|
||||
|
||||
const Bindings *bindings = top->bindings();
|
||||
Block* b = new Block(enclosing, loc);
|
||||
|
||||
Expression_list* lhs = new Expression_list();
|
||||
Expression_list* rhs = new Expression_list();
|
||||
|
||||
Expression_list::const_iterator pe = this->vals_->begin();
|
||||
Expression_list::const_iterator pe = vals->begin();
|
||||
int i = 1;
|
||||
for (Typed_identifier_list::const_iterator pr = results->begin();
|
||||
for (Function::Results::const_iterator pr = results->begin();
|
||||
pr != results->end();
|
||||
++pr, ++pe, ++i)
|
||||
{
|
||||
Named_object* rv = bindings->lookup_local(pr->name());
|
||||
if (rv == NULL || !rv->is_result_variable())
|
||||
{
|
||||
// Presumably an error.
|
||||
delete b;
|
||||
delete lhs;
|
||||
delete rhs;
|
||||
return this;
|
||||
}
|
||||
|
||||
Named_object* rv = *pr;
|
||||
Expression* e = *pe;
|
||||
|
||||
// Check types now so that we give a good error message. The
|
||||
@ -2546,187 +2551,48 @@ Return_statement::do_lower(Gogo*, Named_object*, Block* enclosing)
|
||||
else
|
||||
b->add_statement(Statement::make_tuple_assignment(lhs, rhs, loc));
|
||||
|
||||
b->add_statement(Statement::make_return_statement(this->results_, NULL,
|
||||
loc));
|
||||
b->add_statement(this);
|
||||
|
||||
delete vals;
|
||||
|
||||
return Statement::make_block_statement(b, loc);
|
||||
}
|
||||
|
||||
// Determine types.
|
||||
|
||||
void
|
||||
Return_statement::do_determine_types()
|
||||
{
|
||||
if (this->vals_ == NULL)
|
||||
return;
|
||||
const Typed_identifier_list* results = this->results_;
|
||||
|
||||
Typed_identifier_list::const_iterator pt;
|
||||
if (results != NULL)
|
||||
pt = results->begin();
|
||||
for (Expression_list::iterator pe = this->vals_->begin();
|
||||
pe != this->vals_->end();
|
||||
++pe)
|
||||
{
|
||||
if (results == NULL || pt == results->end())
|
||||
(*pe)->determine_type_no_context();
|
||||
else
|
||||
{
|
||||
Type_context context(pt->type(), false);
|
||||
(*pe)->determine_type(&context);
|
||||
++pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check types.
|
||||
|
||||
void
|
||||
Return_statement::do_check_types(Gogo*)
|
||||
{
|
||||
const Typed_identifier_list* results = this->results_;
|
||||
if (this->vals_ == NULL)
|
||||
{
|
||||
if (results != NULL
|
||||
&& !results->empty()
|
||||
&& results->front().name().empty())
|
||||
{
|
||||
// The result parameters are not named, which means that we
|
||||
// need to supply values for them.
|
||||
this->report_error(_("not enough arguments to return"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (results == NULL)
|
||||
{
|
||||
this->report_error(_("return with value in function "
|
||||
"with no return type"));
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
Typed_identifier_list::const_iterator pt = results->begin();
|
||||
for (Expression_list::const_iterator pe = this->vals_->begin();
|
||||
pe != this->vals_->end();
|
||||
++pe, ++pt, ++i)
|
||||
{
|
||||
if (pt == results->end())
|
||||
{
|
||||
this->report_error(_("too many values in return statement"));
|
||||
return;
|
||||
}
|
||||
std::string reason;
|
||||
if (!Type::are_assignable(pt->type(), (*pe)->type(), &reason))
|
||||
{
|
||||
if (reason.empty())
|
||||
error_at(this->location(),
|
||||
"incompatible type for return value %d",
|
||||
i);
|
||||
else
|
||||
error_at(this->location(),
|
||||
"incompatible type for return value %d (%s)",
|
||||
i, reason.c_str());
|
||||
this->set_is_error();
|
||||
}
|
||||
else if (pt->type()->is_error() || (*pe)->type()->is_error())
|
||||
this->set_is_error();
|
||||
}
|
||||
|
||||
if (pt != results->end())
|
||||
this->report_error(_("not enough arguments to return"));
|
||||
}
|
||||
|
||||
// Build a RETURN_EXPR tree.
|
||||
// Convert a return statement to the backend representation.
|
||||
|
||||
tree
|
||||
Return_statement::do_get_tree(Translate_context* context)
|
||||
{
|
||||
Function* function = context->function()->func_value();
|
||||
tree fndecl = function->get_decl();
|
||||
if (fndecl == error_mark_node || DECL_RESULT(fndecl) == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
const Typed_identifier_list* results = this->results_;
|
||||
|
||||
if (this->vals_ == NULL)
|
||||
Function::Results* results = function->result_variables();
|
||||
std::vector<Bexpression*> retvals;
|
||||
if (results != NULL && !results->empty())
|
||||
{
|
||||
tree stmt_list = NULL_TREE;
|
||||
tree retval = function->return_value(context->gogo(),
|
||||
context->function(),
|
||||
this->location(),
|
||||
&stmt_list);
|
||||
tree set;
|
||||
if (retval == NULL_TREE)
|
||||
set = NULL_TREE;
|
||||
else if (retval == error_mark_node)
|
||||
return error_mark_node;
|
||||
else
|
||||
set = fold_build2_loc(this->location(), MODIFY_EXPR, void_type_node,
|
||||
DECL_RESULT(fndecl), retval);
|
||||
append_to_statement_list(this->build_stmt_1(RETURN_EXPR, set),
|
||||
&stmt_list);
|
||||
return stmt_list;
|
||||
}
|
||||
else if (this->vals_->size() == 1)
|
||||
{
|
||||
gcc_assert(!VOID_TYPE_P(TREE_TYPE(TREE_TYPE(fndecl))));
|
||||
tree val = (*this->vals_->begin())->get_tree(context);
|
||||
gcc_assert(results != NULL && results->size() == 1);
|
||||
val = Expression::convert_for_assignment(context,
|
||||
results->begin()->type(),
|
||||
(*this->vals_->begin())->type(),
|
||||
val, this->location());
|
||||
if (val == error_mark_node)
|
||||
return error_mark_node;
|
||||
tree set = build2(MODIFY_EXPR, void_type_node,
|
||||
DECL_RESULT(fndecl), val);
|
||||
SET_EXPR_LOCATION(set, this->location());
|
||||
return this->build_stmt_1(RETURN_EXPR, set);
|
||||
}
|
||||
else
|
||||
{
|
||||
gcc_assert(!VOID_TYPE_P(TREE_TYPE(TREE_TYPE(fndecl))));
|
||||
tree stmt_list = NULL_TREE;
|
||||
tree rettype = TREE_TYPE(DECL_RESULT(fndecl));
|
||||
tree retvar = create_tmp_var(rettype, "RESULT");
|
||||
gcc_assert(results != NULL && results->size() == this->vals_->size());
|
||||
Expression_list::const_iterator pv = this->vals_->begin();
|
||||
Typed_identifier_list::const_iterator pr = results->begin();
|
||||
for (tree field = TYPE_FIELDS(rettype);
|
||||
field != NULL_TREE;
|
||||
++pv, ++pr, field = DECL_CHAIN(field))
|
||||
retvals.reserve(results->size());
|
||||
for (Function::Results::const_iterator p = results->begin();
|
||||
p != results->end();
|
||||
p++)
|
||||
{
|
||||
gcc_assert(pv != this->vals_->end());
|
||||
tree val = (*pv)->get_tree(context);
|
||||
val = Expression::convert_for_assignment(context, pr->type(),
|
||||
(*pv)->type(), val,
|
||||
this->location());
|
||||
if (val == error_mark_node)
|
||||
return error_mark_node;
|
||||
tree set = build2(MODIFY_EXPR, void_type_node,
|
||||
build3(COMPONENT_REF, TREE_TYPE(field),
|
||||
retvar, field, NULL_TREE),
|
||||
val);
|
||||
SET_EXPR_LOCATION(set, this->location());
|
||||
append_to_statement_list(set, &stmt_list);
|
||||
tree rv = (*p)->get_tree(context->gogo(), context->function());
|
||||
retvals.push_back(tree_to_expr(rv));
|
||||
}
|
||||
tree set = build2(MODIFY_EXPR, void_type_node, DECL_RESULT(fndecl),
|
||||
retvar);
|
||||
append_to_statement_list(this->build_stmt_1(RETURN_EXPR, set),
|
||||
&stmt_list);
|
||||
return stmt_list;
|
||||
}
|
||||
|
||||
Bstatement* ret;
|
||||
ret = context->backend()->return_statement(tree_to_function(fndecl),
|
||||
retvals, this->location());
|
||||
return statement_to_tree(ret);
|
||||
}
|
||||
|
||||
// Make a return statement.
|
||||
|
||||
Statement*
|
||||
Statement::make_return_statement(const Typed_identifier_list* results,
|
||||
Expression_list* vals,
|
||||
Statement::make_return_statement(Expression_list* vals,
|
||||
source_location location)
|
||||
{
|
||||
return new Return_statement(results, vals, location);
|
||||
return new Return_statement(vals, location);
|
||||
}
|
||||
|
||||
// A break or continue statement.
|
||||
|
@ -200,8 +200,7 @@ class Statement
|
||||
|
||||
// Make a return statement.
|
||||
static Statement*
|
||||
make_return_statement(const Typed_identifier_list*, Expression_list*,
|
||||
source_location);
|
||||
make_return_statement(Expression_list*, source_location);
|
||||
|
||||
// Make a break statement.
|
||||
static Statement*
|
||||
@ -556,10 +555,9 @@ class Variable_declaration_statement : public Statement
|
||||
class Return_statement : public Statement
|
||||
{
|
||||
public:
|
||||
Return_statement(const Typed_identifier_list* results, Expression_list* vals,
|
||||
source_location location)
|
||||
Return_statement(Expression_list* vals, source_location location)
|
||||
: Statement(STATEMENT_RETURN, location),
|
||||
results_(results), vals_(vals)
|
||||
vals_(vals), is_lowered_(false)
|
||||
{ }
|
||||
|
||||
// The list of values being returned. This may be NULL.
|
||||
@ -578,12 +576,6 @@ class Return_statement : public Statement
|
||||
Statement*
|
||||
do_lower(Gogo*, Named_object*, Block*);
|
||||
|
||||
void
|
||||
do_determine_types();
|
||||
|
||||
void
|
||||
do_check_types(Gogo*);
|
||||
|
||||
bool
|
||||
do_may_fall_through() const
|
||||
{ return false; }
|
||||
@ -592,12 +584,10 @@ class Return_statement : public Statement
|
||||
do_get_tree(Translate_context*);
|
||||
|
||||
private:
|
||||
// The result types of the function we are returning from. This is
|
||||
// here because in some of the traversals it is inconvenient to get
|
||||
// it.
|
||||
const Typed_identifier_list* results_;
|
||||
// Return values. This may be NULL.
|
||||
Expression_list* vals_;
|
||||
// True if this statement has been lowered.
|
||||
bool is_lowered_;
|
||||
};
|
||||
|
||||
// A send statement.
|
||||
|
@ -7924,10 +7924,7 @@ Type::build_one_stub_method(Gogo* gogo, Method* method,
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
retvals->push_back(Expression::make_call_result(call, i));
|
||||
}
|
||||
const Function* function = gogo->current_function()->func_value();
|
||||
const Typed_identifier_list* results = function->type()->results();
|
||||
Statement* retstat = Statement::make_return_statement(results, retvals,
|
||||
location);
|
||||
Statement* retstat = Statement::make_return_statement(retvals, location);
|
||||
gogo->add_statement(retstat);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user