* 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
+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.
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
#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.
{ }
};
+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
// 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.
// 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.
return new Bexpression(t);
}
+Bfunction*
+tree_to_function(tree t)
+{
+ return new Bfunction(t);
+}
+
tree
statement_to_tree(Bstatement* bs)
{
// 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;
// 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.
// interface.
extern Bexpression* tree_to_expr(tree);
+extern Bfunction* tree_to_function(tree);
extern tree statement_to_tree(Bstatement*);
#endif // !defined(GO_BACKEND_H)
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);
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_));
{
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),
}
}
- function->create_named_result_variables(this);
+ function->create_result_variables(this);
const std::string* pname;
std::string nested_name;
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);
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;
}
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;
++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);
+ }
}
}
// 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);
}
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_);
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
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;
// 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.
// 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.
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 ] .
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);
}
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.
// 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;
+
+ 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)
+ {
+ if (results_count > 0 && !function->func_value()->results_are_named())
+ {
+ this->report_error(_("not enough arguments to return"));
+ return this;
+ }
+ return this;
+ }
+
+ if (results_count == 0)
+ {
+ this->report_error(_("return with value in function "
+ "with no return type"));
+ return this;
+ }
// 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)
+ && vals->size() == 1
+ && vals->front()->call_expression() != NULL)
{
- 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)
+ 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));
- delete this->vals_;
- this->vals_ = vals;
+ vals_count = results_count;
}
- if (results->front().name().empty())
- return this;
-
- if (results_count != this->vals_->size())
+ if (vals_count < results_count)
{
- // Presumably an error which will be reported in check_types.
+ this->report_error(_("not enough arguments to return"));
return this;
}
- // Assign to named return values and then return them.
-
- source_location loc = this->location();
- const Block* top = enclosing;
- while (top->enclosing() != NULL)
- top = top->enclosing();
+ 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
else
b->add_statement(Statement::make_tuple_assignment(lhs, rhs, loc));
- b->add_statement(Statement::make_return_statement(this->results_, NULL,
- loc));
-
- 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.
+ b->add_statement(this);
-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;
- }
+ delete vals;
- 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"));
+ return Statement::make_block_statement(b, loc);
}
-// 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)
- {
- 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
+ Function::Results* results = function->result_variables();
+ std::vector<Bexpression*> retvals;
+ if (results != NULL && !results->empty())
{
- 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.
// 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*
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.
Statement*
do_lower(Gogo*, Named_object*, Block*);
- void
- do_determine_types();
-
- void
- do_check_types(Gogo*);
-
bool
do_may_fall_through() const
{ return false; }
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.
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);
}
}