Commit 571d3f91 by Ian Lance Taylor

compiler: Add support for method values.

From-SVN: r200379
parent 39953c79
...@@ -1090,6 +1090,15 @@ Set_and_use_temporary_expression::do_type() ...@@ -1090,6 +1090,15 @@ Set_and_use_temporary_expression::do_type()
return this->statement_->type(); return this->statement_->type();
} }
// Determine the type of the expression.
void
Set_and_use_temporary_expression::do_determine_type(
const Type_context* context)
{
this->expr_->determine_type(context);
}
// Take the address. // Take the address.
void void
...@@ -6626,20 +6635,49 @@ Bound_method_expression::do_traverse(Traverse* traverse) ...@@ -6626,20 +6635,49 @@ Bound_method_expression::do_traverse(Traverse* traverse)
return Expression::traverse(&this->expr_, traverse); return Expression::traverse(&this->expr_, traverse);
} }
// Lower the expression. If this is a method value rather than being
// called, and the method is accessed via a pointer, we may need to
// add nil checks. Introduce a temporary variable so that those nil
// checks do not cause multiple evaluation.
Expression*
Bound_method_expression::do_lower(Gogo*, Named_object*,
Statement_inserter* inserter, int)
{
// For simplicity we use a temporary for every call to an embedded
// method, even though some of them might be pure value methods and
// not require a temporary.
if (this->expr_->var_expression() == NULL
&& this->expr_->temporary_reference_expression() == NULL
&& this->expr_->set_and_use_temporary_expression() == NULL
&& (this->method_->field_indexes() != NULL
|| (this->method_->is_value_method()
&& this->expr_->type()->points_to() != NULL)))
{
Temporary_statement* temp =
Statement::make_temporary(this->expr_->type(), NULL, this->location());
inserter->insert(temp);
this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_,
this->location());
}
return this;
}
// Return the type of a bound method expression. The type of this // Return the type of a bound method expression. The type of this
// object is really the type of the method with no receiver. We // object is simply the type of the method with no receiver.
// should be able to get away with just returning the type of the
// method.
Type* Type*
Bound_method_expression::do_type() Bound_method_expression::do_type()
{ {
if (this->method_->is_function()) Named_object* fn = this->method_->named_object();
return this->method_->func_value()->type(); Function_type* fntype;
else if (this->method_->is_function_declaration()) if (fn->is_function())
return this->method_->func_declaration_value()->type(); fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
fntype = fn->func_declaration_value()->type();
else else
return Type::make_error_type(); return Type::make_error_type();
return fntype->copy_without_receiver();
} }
// Determine the types of a method expression. // Determine the types of a method expression.
...@@ -6647,7 +6685,14 @@ Bound_method_expression::do_type() ...@@ -6647,7 +6685,14 @@ Bound_method_expression::do_type()
void void
Bound_method_expression::do_determine_type(const Type_context*) Bound_method_expression::do_determine_type(const Type_context*)
{ {
Function_type* fntype = this->type()->function_type(); Named_object* fn = this->method_->named_object();
Function_type* fntype;
if (fn->is_function())
fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
fntype = fn->func_declaration_value()->type();
else
fntype = NULL;
if (fntype == NULL || !fntype->is_method()) if (fntype == NULL || !fntype->is_method())
this->expr_->determine_type_no_context(); this->expr_->determine_type_no_context();
else else
...@@ -6662,31 +6707,278 @@ Bound_method_expression::do_determine_type(const Type_context*) ...@@ -6662,31 +6707,278 @@ Bound_method_expression::do_determine_type(const Type_context*)
void void
Bound_method_expression::do_check_types(Gogo*) Bound_method_expression::do_check_types(Gogo*)
{ {
if (!this->method_->is_function() Named_object* fn = this->method_->named_object();
&& !this->method_->is_function_declaration()) if (!fn->is_function() && !fn->is_function_declaration())
this->report_error(_("object is not a method")); {
this->report_error(_("object is not a method"));
return;
}
Function_type* fntype;
if (fn->is_function())
fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
fntype = fn->func_declaration_value()->type();
else
go_unreachable();
Type* rtype = fntype->receiver()->type()->deref();
Type* etype = (this->expr_type_ != NULL
? this->expr_type_
: this->expr_->type());
etype = etype->deref();
if (!Type::are_identical(rtype, etype, true, NULL))
this->report_error(_("method type does not match object type"));
}
// If a bound method expression is not simply called, then it is
// represented as a closure. The closure will hold a single variable,
// the receiver to pass to the method. The function will be a simple
// thunk that pulls that value from the closure and calls the method
// with the remaining arguments.
//
// Because method values are not common, we don't build all thunks for
// every methods, but instead only build them as we need them. In
// particular, we even build them on demand for methods defined in
// other packages.
Bound_method_expression::Method_value_thunks
Bound_method_expression::method_value_thunks;
// Find or create the thunk for METHOD.
Named_object*
Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
Named_object* fn)
{
std::pair<Named_object*, Named_object*> val(fn, NULL);
std::pair<Method_value_thunks::iterator, bool> ins =
Bound_method_expression::method_value_thunks.insert(val);
if (!ins.second)
{
// We have seen this method before.
go_assert(ins.first->second != NULL);
return ins.first->second;
}
Location loc = fn->location();
Function_type* orig_fntype;
if (fn->is_function())
orig_fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
orig_fntype = fn->func_declaration_value()->type();
else
orig_fntype = NULL;
if (orig_fntype == NULL || !orig_fntype->is_method())
{
ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name());
return ins.first->second;
}
Struct_field_list* sfl = new Struct_field_list();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
sfl->push_back(Struct_field(Typed_identifier("val.1",
orig_fntype->receiver()->type(),
loc)));
Type* closure_type = Type::make_struct_type(sfl, loc);
closure_type = Type::make_pointer_type(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type);
Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype,
false, loc);
gogo->start_block(loc);
Named_object* cp = gogo->lookup("closure.0", NULL);
go_assert(cp != NULL
&& cp->is_variable()
&& cp->var_value()->is_parameter());
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
Expression* arg = Expression::make_var_reference(cp, loc);
arg = Expression::make_unary(OPERATOR_MULT, arg, loc);
arg = Expression::make_field_reference(arg, 1, loc);
Expression* bme = Expression::make_bound_method(arg, method, fn, loc);
const Typed_identifier_list* orig_params = orig_fntype->parameters();
Expression_list* args;
if (orig_params == NULL || orig_params->empty())
args = NULL;
else else
{ {
Type* rtype = this->type()->function_type()->receiver()->type()->deref(); const Typed_identifier_list* new_params = new_fntype->parameters();
Type* etype = (this->expr_type_ != NULL args = new Expression_list();
? this->expr_type_ for (Typed_identifier_list::const_iterator p = new_params->begin();
: this->expr_->type()); p + 1 != new_params->end();
etype = etype->deref(); ++p)
if (!Type::are_identical(rtype, etype, true, NULL)) {
this->report_error(_("method type does not match object type")); Named_object* p_no = gogo->lookup(p->name(), NULL);
go_assert(p_no != NULL
&& p_no->is_variable()
&& p_no->var_value()->is_parameter());
args->push_back(Expression::make_var_reference(p_no, loc));
}
}
Call_expression* call = Expression::make_call(bme, args,
orig_fntype->is_varargs(),
loc);
call->set_varargs_are_lowered();
Statement* s = Statement::make_return_from_call(call, loc);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);
gogo->lower_block(new_no, b);
gogo->finish_function(loc);
ins.first->second = new_no;
return new_no;
}
// Return an expression to check *REF for nil while dereferencing
// according to FIELD_INDEXES. Update *REF to build up the field
// reference. This is a static function so that we don't have to
// worry about declaring Field_indexes in expressions.h.
static Expression*
bme_check_nil(const Method::Field_indexes* field_indexes, Location loc,
Expression** ref)
{
if (field_indexes == NULL)
return Expression::make_boolean(false, loc);
Expression* cond = bme_check_nil(field_indexes->next, loc, ref);
Struct_type* stype = (*ref)->type()->deref()->struct_type();
go_assert(stype != NULL
&& field_indexes->field_index < stype->field_count());
if ((*ref)->type()->struct_type() == NULL)
{
go_assert((*ref)->type()->points_to() != NULL);
Expression* n = Expression::make_binary(OPERATOR_EQEQ, *ref,
Expression::make_nil(loc),
loc);
cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc);
*ref = Expression::make_unary(OPERATOR_MULT, *ref, loc);
go_assert((*ref)->type()->struct_type() == stype);
} }
*ref = Expression::make_field_reference(*ref, field_indexes->field_index,
loc);
return cond;
} }
// Get the tree for a method expression. There is no standard tree // Get the tree for a method value.
// representation for this. The only places it may currently be used
// are in a Call_expression or a Go_statement, which will take it
// apart directly. So this has nothing to do at present.
tree tree
Bound_method_expression::do_get_tree(Translate_context*) Bound_method_expression::do_get_tree(Translate_context* context)
{ {
error_at(this->location(), "reference to method other than calling it"); Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(),
return error_mark_node; this->method_,
this->function_);
if (thunk->is_erroneous())
{
go_assert(saw_errors());
return error_mark_node;
}
// FIXME: We should lower this earlier, but we can't lower it in the
// lowering pass because at that point we don't know whether we need
// to create the thunk or not. If the expression is called, we
// don't need the thunk.
Location loc = this->location();
// If the method expects a value, and we have a pointer, we need to
// dereference the pointer.
Named_object* fn = this->method_->named_object();
Function_type* fntype;
if (fn->is_function())
fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
fntype = fn->func_declaration_value()->type();
else
go_unreachable();
Expression* val = this->expr_;
if (fntype->receiver()->type()->points_to() == NULL
&& val->type()->points_to() != NULL)
val = Expression::make_unary(OPERATOR_MULT, val, loc);
// Note that we are ignoring this->expr_type_ here. The thunk will
// expect a closure whose second field has type this->expr_type_ (if
// that is not NULL). We are going to pass it a closure whose
// second field has type this->expr_->type(). Since
// this->expr_type_ is only not-NULL for pointer types, we can get
// away with this.
Struct_field_list* fields = new Struct_field_list();
fields->push_back(Struct_field(Typed_identifier("fn.0",
thunk->func_value()->type(),
loc)));
fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc)));
Struct_type* st = Type::make_struct_type(fields, loc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_func_code_reference(thunk, loc));
vals->push_back(val);
Expression* ret = Expression::make_struct_composite_literal(st, vals, loc);
ret = Expression::make_heap_composite(ret, loc);
tree ret_tree = ret->get_tree(context);
Expression* nil_check = NULL;
// See whether the expression or any embedded pointers are nil.
Expression* expr = this->expr_;
if (this->method_->field_indexes() != NULL)
{
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
Expression* ref = expr;
nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref);
expr = ref;
}
if (this->method_->is_value_method() && expr->type()->points_to() != NULL)
{
Expression* n = Expression::make_binary(OPERATOR_EQEQ, expr,
Expression::make_nil(loc),
loc);
if (nil_check == NULL)
nil_check = n;
else
nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc);
}
if (nil_check != NULL)
{
tree nil_check_tree = nil_check->get_tree(context);
tree crash =
context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc);
if (ret_tree == error_mark_node
|| nil_check_tree == error_mark_node
|| crash == error_mark_node)
return error_mark_node;
ret_tree = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(ret_tree),
build3_loc(loc.gcc_location(), COND_EXPR,
void_type_node, nil_check_tree,
crash, NULL_TREE),
ret_tree);
}
return ret_tree;
} }
// Dump ast representation of a bound method expression. // Dump ast representation of a bound method expression.
...@@ -6705,16 +6997,16 @@ Bound_method_expression::do_dump_expression(Ast_dump_context* ast_dump_context) ...@@ -6705,16 +6997,16 @@ Bound_method_expression::do_dump_expression(Ast_dump_context* ast_dump_context)
ast_dump_context->ostream() << ")"; ast_dump_context->ostream() << ")";
} }
ast_dump_context->ostream() << "." << this->method_->name(); ast_dump_context->ostream() << "." << this->function_->name();
} }
// Make a method expression. // Make a method expression.
Bound_method_expression* Bound_method_expression*
Expression::make_bound_method(Expression* expr, Named_object* method, Expression::make_bound_method(Expression* expr, const Method* method,
Location location) Named_object* function, Location location)
{ {
return new Bound_method_expression(expr, method, location); return new Bound_method_expression(expr, method, function, location);
} }
// Class Builtin_call_expression. This is used for a call to a // Class Builtin_call_expression. This is used for a call to a
...@@ -8921,7 +9213,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, ...@@ -8921,7 +9213,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
Bound_method_expression* bme = this->fn_->bound_method_expression(); Bound_method_expression* bme = this->fn_->bound_method_expression();
if (bme != NULL) if (bme != NULL)
{ {
Named_object* method = bme->method(); Named_object* methodfn = bme->function();
Expression* first_arg = bme->first_argument(); Expression* first_arg = bme->first_argument();
// We always pass a pointer when calling a method. // We always pass a pointer when calling a method.
...@@ -8962,7 +9254,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function, ...@@ -8962,7 +9254,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
// old arguments, because we may be traversing them up in some // old arguments, because we may be traversing them up in some
// caller. FIXME. // caller. FIXME.
this->args_ = new_args; this->args_ = new_args;
this->fn_ = Expression::make_func_reference(method, NULL, this->fn_ = Expression::make_func_reference(methodfn, NULL,
bme->location()); bme->location());
} }
...@@ -11158,6 +11450,28 @@ Interface_field_reference_expression::do_traverse(Traverse* traverse) ...@@ -11158,6 +11450,28 @@ Interface_field_reference_expression::do_traverse(Traverse* traverse)
return Expression::traverse(&this->expr_, traverse); return Expression::traverse(&this->expr_, traverse);
} }
// Lower the expression. If this expression is not called, we need to
// evaluate the expression twice when converting to the backend
// interface. So introduce a temporary variable if necessary.
Expression*
Interface_field_reference_expression::do_lower(Gogo*, Named_object*,
Statement_inserter* inserter,
int)
{
if (this->expr_->var_expression() == NULL
&& this->expr_->temporary_reference_expression() == NULL
&& this->expr_->set_and_use_temporary_expression() == NULL)
{
Temporary_statement* temp =
Statement::make_temporary(this->expr_->type(), NULL, this->location());
inserter->insert(temp);
this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_,
this->location());
}
return this;
}
// Return the type of an interface field reference. // Return the type of an interface field reference.
Type* Type*
...@@ -11218,18 +11532,188 @@ Interface_field_reference_expression::do_check_types(Gogo*) ...@@ -11218,18 +11532,188 @@ Interface_field_reference_expression::do_check_types(Gogo*)
} }
} }
// Get a tree for a reference to a field in an interface. There is no // If an interface field reference is not simply called, then it is
// standard tree type representation for this: it's a function // represented as a closure. The closure will hold a single variable,
// attached to its first argument, like a Bound_method_expression. // the value of the interface on which the method should be called.
// The only places it may currently be used are in a Call_expression // The function will be a simple thunk that pulls the value from the
// or a Go_statement, which will take it apart directly. So this has // closure and calls the method with the remaining arguments.
// nothing to do at present.
// Because method values are not common, we don't build all thunks for
// all possible interface methods, but instead only build them as we
// need them. In particular, we even build them on demand for
// interface methods defined in other packages.
Interface_field_reference_expression::Interface_method_thunks
Interface_field_reference_expression::interface_method_thunks;
// Find or create the thunk to call method NAME on TYPE.
Named_object*
Interface_field_reference_expression::create_thunk(Gogo* gogo,
Interface_type* type,
const std::string& name)
{
std::pair<Interface_type*, Method_thunks*> val(type, NULL);
std::pair<Interface_method_thunks::iterator, bool> ins =
Interface_field_reference_expression::interface_method_thunks.insert(val);
if (ins.second)
{
// This is the first time we have seen this interface.
ins.first->second = new Method_thunks();
}
for (Method_thunks::const_iterator p = ins.first->second->begin();
p != ins.first->second->end();
p++)
if (p->first == name)
return p->second;
Location loc = type->location();
const Typed_identifier* method_id = type->find_method(name);
if (method_id == NULL)
return Named_object::make_erroneous_name(Gogo::thunk_name());
Function_type* orig_fntype = method_id->type()->function_type();
if (orig_fntype == NULL)
return Named_object::make_erroneous_name(Gogo::thunk_name());
Struct_field_list* sfl = new Struct_field_list();
// The type here is wrong--it should be new_fntype. But we don't
// have new_fntype yet, and it doesn't really matter.
Type* vt = Type::make_pointer_type(Type::make_void_type());
sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc)));
sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc)));
Type* closure_type = Type::make_struct_type(sfl, loc);
closure_type = Type::make_pointer_type(closure_type);
Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type);
Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype,
false, loc);
gogo->start_block(loc);
Named_object* cp = gogo->lookup("closure.0", NULL);
go_assert(cp != NULL
&& cp->is_variable()
&& cp->var_value()->is_parameter());
// Field 0 of the closure is the function code pointer, field 1 is
// the value on which to invoke the method.
Expression* arg = Expression::make_var_reference(cp, loc);
arg = Expression::make_unary(OPERATOR_MULT, arg, loc);
arg = Expression::make_field_reference(arg, 1, loc);
Expression *ifre = Expression::make_interface_field_reference(arg, name,
loc);
const Typed_identifier_list* orig_params = orig_fntype->parameters();
Expression_list* args;
if (orig_params == NULL || orig_params->empty())
args = NULL;
else
{
const Typed_identifier_list* new_params = new_fntype->parameters();
args = new Expression_list();
for (Typed_identifier_list::const_iterator p = new_params->begin();
p + 1 != new_params->end();
++p)
{
Named_object* p_no = gogo->lookup(p->name(), NULL);
go_assert(p_no != NULL
&& p_no->is_variable()
&& p_no->var_value()->is_parameter());
args->push_back(Expression::make_var_reference(p_no, loc));
}
}
Call_expression* call = Expression::make_call(ifre, args,
orig_fntype->is_varargs(),
loc);
call->set_varargs_are_lowered();
Statement* s = Statement::make_return_from_call(call, loc);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);
gogo->lower_block(new_no, b);
gogo->finish_function(loc);
ins.first->second->push_back(std::make_pair(name, new_no));
return new_no;
}
// Get a tree for a method value.
tree tree
Interface_field_reference_expression::do_get_tree(Translate_context*) Interface_field_reference_expression::do_get_tree(Translate_context* context)
{ {
error_at(this->location(), "reference to method other than calling it"); Interface_type* type = this->expr_->type()->interface_type();
return error_mark_node; if (type == NULL)
{
go_assert(saw_errors());
return error_mark_node;
}
Named_object* thunk =
Interface_field_reference_expression::create_thunk(context->gogo(),
type, this->name_);
if (thunk->is_erroneous())
{
go_assert(saw_errors());
return error_mark_node;
}
// FIXME: We should lower this earlier, but we can't it lower it in
// the lowering pass because at that point we don't know whether we
// need to create the thunk or not. If the expression is called, we
// don't need the thunk.
Location loc = this->location();
Struct_field_list* fields = new Struct_field_list();
fields->push_back(Struct_field(Typed_identifier("fn.0",
thunk->func_value()->type(),
loc)));
fields->push_back(Struct_field(Typed_identifier("val.1",
this->expr_->type(),
loc)));
Struct_type* st = Type::make_struct_type(fields, loc);
Expression_list* vals = new Expression_list();
vals->push_back(Expression::make_func_code_reference(thunk, loc));
vals->push_back(this->expr_);
Expression* expr = Expression::make_struct_composite_literal(st, vals, loc);
expr = Expression::make_heap_composite(expr, loc);
tree closure_tree = expr->get_tree(context);
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
tree expr_tree = this->expr_->get_tree(context);
tree nil_check_tree = Expression::comparison_tree(context,
Type::lookup_bool_type(),
OPERATOR_EQEQ,
this->expr_->type(),
expr_tree,
Type::make_nil_type(),
null_pointer_node,
loc);
tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc);
if (closure_tree == error_mark_node
|| nil_check_tree == error_mark_node
|| crash == error_mark_node)
return error_mark_node;
return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR,
TREE_TYPE(closure_tree),
build3_loc(loc.gcc_location(), COND_EXPR,
void_type_node, nil_check_tree, crash,
NULL_TREE),
closure_tree);
} }
// Dump ast representation for an interface field reference. // Dump ast representation for an interface field reference.
...@@ -11485,22 +11969,7 @@ Selector_expression::lower_method_expression(Gogo* gogo) ...@@ -11485,22 +11969,7 @@ Selector_expression::lower_method_expression(Gogo* gogo)
method_type->is_varargs(), method_type->is_varargs(),
location); location);
size_t count = call->result_count(); Statement* s = Statement::make_return_from_call(call, location);
Statement* s;
if (count == 0)
s = Statement::make_statement(call, true);
else
{
Expression_list* retvals = new Expression_list();
if (count <= 1)
retvals->push_back(call);
else
{
for (size_t i = 0; i < count; ++i)
retvals->push_back(Expression::make_call_result(call, i));
}
s = Statement::make_return_statement(retvals, location);
}
gogo->add_statement(s); gogo->add_statement(s);
Block* b = gogo->finish_block(location); Block* b = gogo->finish_block(location);
......
...@@ -16,6 +16,7 @@ class Translate_context; ...@@ -16,6 +16,7 @@ class Translate_context;
class Traverse; class Traverse;
class Statement_inserter; class Statement_inserter;
class Type; class Type;
class Method;
struct Type_context; struct Type_context;
class Integer_type; class Integer_type;
class Float_type; class Float_type;
...@@ -224,9 +225,11 @@ class Expression ...@@ -224,9 +225,11 @@ class Expression
make_call_result(Call_expression*, unsigned int index); make_call_result(Call_expression*, unsigned int index);
// Make an expression which is a method bound to its first // Make an expression which is a method bound to its first
// parameter. // parameter. METHOD is the method being called, FUNCTION is the
// function to call.
static Bound_method_expression* static Bound_method_expression*
make_bound_method(Expression* object, Named_object* method, Location); make_bound_method(Expression* object, const Method* method,
Named_object* function, Location);
// Make an index or slice expression. This is a parser expression // Make an index or slice expression. This is a parser expression
// which represents LEFT[START:END]. END may be NULL, meaning an // which represents LEFT[START:END]. END may be NULL, meaning an
...@@ -1079,8 +1082,7 @@ class Set_and_use_temporary_expression : public Expression ...@@ -1079,8 +1082,7 @@ class Set_and_use_temporary_expression : public Expression
do_type(); do_type();
void void
do_determine_type(const Type_context*) do_determine_type(const Type_context*);
{ }
Expression* Expression*
do_copy() do_copy()
...@@ -1852,10 +1854,10 @@ class Map_index_expression : public Expression ...@@ -1852,10 +1854,10 @@ class Map_index_expression : public Expression
class Bound_method_expression : public Expression class Bound_method_expression : public Expression
{ {
public: public:
Bound_method_expression(Expression* expr, Named_object* method, Bound_method_expression(Expression* expr, const Method *method,
Location location) Named_object* function, Location location)
: Expression(EXPRESSION_BOUND_METHOD, location), : Expression(EXPRESSION_BOUND_METHOD, location),
expr_(expr), expr_type_(NULL), method_(method) expr_(expr), expr_type_(NULL), method_(method), function_(function)
{ } { }
// Return the object which is the first argument. // Return the object which is the first argument.
...@@ -1870,20 +1872,33 @@ class Bound_method_expression : public Expression ...@@ -1870,20 +1872,33 @@ class Bound_method_expression : public Expression
first_argument_type() const first_argument_type() const
{ return this->expr_type_; } { return this->expr_type_; }
// Return the method function. // Return the method.
Named_object* const Method*
method() method() const
{ return this->method_; } { return this->method_; }
// Return the function to call.
Named_object*
function() const
{ return this->function_; }
// Set the implicit type of the expression. // Set the implicit type of the expression.
void void
set_first_argument_type(Type* type) set_first_argument_type(Type* type)
{ this->expr_type_ = type; } { this->expr_type_ = type; }
// Create a thunk to call FUNCTION, for METHOD, when it is used as
// part of a method value.
static Named_object*
create_thunk(Gogo*, const Method* method, Named_object* function);
protected: protected:
int int
do_traverse(Traverse*); do_traverse(Traverse*);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Type* Type*
do_type(); do_type();
...@@ -1897,7 +1912,7 @@ class Bound_method_expression : public Expression ...@@ -1897,7 +1912,7 @@ class Bound_method_expression : public Expression
do_copy() do_copy()
{ {
return new Bound_method_expression(this->expr_->copy(), this->method_, return new Bound_method_expression(this->expr_->copy(), this->method_,
this->location()); this->function_, this->location());
} }
tree tree
...@@ -1907,6 +1922,11 @@ class Bound_method_expression : public Expression ...@@ -1907,6 +1922,11 @@ class Bound_method_expression : public Expression
do_dump_expression(Ast_dump_context*) const; do_dump_expression(Ast_dump_context*) const;
private: private:
// A mapping from method functions to the thunks we have created for
// them.
typedef Unordered_map(Named_object*, Named_object*) Method_value_thunks;
static Method_value_thunks method_value_thunks;
// The object used to find the method. This is passed to the method // The object used to find the method. This is passed to the method
// as the first argument. // as the first argument.
Expression* expr_; Expression* expr_;
...@@ -1914,8 +1934,12 @@ class Bound_method_expression : public Expression ...@@ -1914,8 +1934,12 @@ class Bound_method_expression : public Expression
// NULL in the normal case, non-NULL when using a method from an // NULL in the normal case, non-NULL when using a method from an
// anonymous field which does not require a stub. // anonymous field which does not require a stub.
Type* expr_type_; Type* expr_type_;
// The method itself. // The method.
Named_object* method_; const Method* method_;
// The function to call. This is not the same as
// method_->named_object() when the method has a stub. This will be
// the real function rather than the stub.
Named_object* function_;
}; };
// A reference to a field in a struct. // A reference to a field in a struct.
...@@ -2031,6 +2055,11 @@ class Interface_field_reference_expression : public Expression ...@@ -2031,6 +2055,11 @@ class Interface_field_reference_expression : public Expression
name() const name() const
{ return this->name_; } { return this->name_; }
// Create a thunk to call the method NAME in TYPE when it is used as
// part of a method value.
static Named_object*
create_thunk(Gogo*, Interface_type* type, const std::string& name);
// Return a tree for the pointer to the function to call, given a // Return a tree for the pointer to the function to call, given a
// tree for the expression. // tree for the expression.
tree tree
...@@ -2046,6 +2075,9 @@ class Interface_field_reference_expression : public Expression ...@@ -2046,6 +2075,9 @@ class Interface_field_reference_expression : public Expression
int int
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Type* Type*
do_type(); do_type();
...@@ -2070,6 +2102,13 @@ class Interface_field_reference_expression : public Expression ...@@ -2070,6 +2102,13 @@ class Interface_field_reference_expression : public Expression
do_dump_expression(Ast_dump_context*) const; do_dump_expression(Ast_dump_context*) const;
private: private:
// A mapping from interface types to a list of thunks we have
// created for methods.
typedef std::vector<std::pair<std::string, Named_object*> > Method_thunks;
typedef Unordered_map(Interface_type*, Method_thunks*)
Interface_method_thunks;
static Interface_method_thunks interface_method_thunks;
// The expression for the interface object. This should have a type // The expression for the interface object. This should have a type
// of interface or pointer to interface. // of interface or pointer to interface.
Expression* expr_; Expression* expr_;
......
...@@ -1795,11 +1795,36 @@ Create_function_descriptors::expression(Expression** pexpr) ...@@ -1795,11 +1795,36 @@ Create_function_descriptors::expression(Expression** pexpr)
return TRAVERSE_CONTINUE; return TRAVERSE_CONTINUE;
} }
Bound_method_expression* bme = expr->bound_method_expression();
if (bme != NULL)
{
// We would not get here for a call to this method, so this is a
// method value. We need to create a thunk.
Bound_method_expression::create_thunk(this->gogo_, bme->method(),
bme->function());
return TRAVERSE_CONTINUE;
}
Interface_field_reference_expression* ifre =
expr->interface_field_reference_expression();
if (ifre != NULL)
{
// We would not get here for a call to this interface method, so
// this is a method value. We need to create a thunk.
Interface_type* type = ifre->expr()->type()->interface_type();
if (type != NULL)
Interface_field_reference_expression::create_thunk(this->gogo_, type,
ifre->name());
return TRAVERSE_CONTINUE;
}
Call_expression* ce = expr->call_expression(); Call_expression* ce = expr->call_expression();
if (ce != NULL) if (ce != NULL)
{ {
Expression* fn = ce->fn(); Expression* fn = ce->fn();
if (fn->func_expression() != NULL) if (fn->func_expression() != NULL
|| fn->bound_method_expression() != NULL
|| fn->interface_field_reference_expression() != NULL)
{ {
// Traverse the arguments but not the function. // Traverse the arguments but not the function.
Expression_list* args = ce->args(); Expression_list* args = ce->args();
...@@ -2806,22 +2831,7 @@ Build_recover_thunks::function(Named_object* orig_no) ...@@ -2806,22 +2831,7 @@ Build_recover_thunks::function(Named_object* orig_no)
// Any varargs call has already been lowered. // Any varargs call has already been lowered.
call->set_varargs_are_lowered(); call->set_varargs_are_lowered();
Statement* s; Statement* s = Statement::make_return_from_call(call, location);
if (orig_fntype->results() == NULL || orig_fntype->results()->empty())
s = Statement::make_statement(call, true);
else
{
Expression_list* vals = new Expression_list();
size_t rc = orig_fntype->results()->size();
if (rc == 1)
vals->push_back(call);
else
{
for (size_t i = 0; i < rc; ++i)
vals->push_back(Expression::make_call_result(call, i));
}
s = Statement::make_return_statement(vals, location);
}
s->determine_types(); s->determine_types();
gogo->add_statement(s); gogo->add_statement(s);
...@@ -3557,42 +3567,8 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no, ...@@ -3557,42 +3567,8 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
{ {
Location loc = no->location(); Location loc = no->location();
Typed_identifier_list* new_params = new Typed_identifier_list();
const Typed_identifier_list* orig_params = orig_fntype->parameters();
if (orig_params != NULL && !orig_params->empty())
{
static int count;
char buf[50];
for (Typed_identifier_list::const_iterator p = orig_params->begin();
p != orig_params->end();
++p)
{
snprintf(buf, sizeof buf, "pt.%u", count);
++count;
new_params->push_back(Typed_identifier(buf, p->type(),
p->location()));
}
}
Type* vt = Type::make_pointer_type(Type::make_void_type()); Type* vt = Type::make_pointer_type(Type::make_void_type());
new_params->push_back(Typed_identifier("closure.0", vt, loc)); Function_type* new_fntype = orig_fntype->copy_with_closure(vt);
const Typed_identifier_list* orig_results = orig_fntype->results();
Typed_identifier_list* new_results;
if (orig_results == NULL || orig_results->empty())
new_results = NULL;
else
{
new_results = new Typed_identifier_list();
for (Typed_identifier_list::const_iterator p = orig_results->begin();
p != orig_results->end();
++p)
new_results->push_back(Typed_identifier("", p->type(),
p->location()));
}
Function_type* new_fntype = Type::make_function_type(NULL, new_params,
new_results,
loc);
std::string name = no->name() + "$descriptorfn"; std::string name = no->name() + "$descriptorfn";
Named_object* dno = gogo->start_function(name, new_fntype, false, loc); Named_object* dno = gogo->start_function(name, new_fntype, false, loc);
...@@ -3602,13 +3578,16 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no, ...@@ -3602,13 +3578,16 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
Expression* fn = Expression::make_func_reference(no, NULL, loc); Expression* fn = Expression::make_func_reference(no, NULL, loc);
// Call the wrapper function, passing all of the arguments except // Call the function begin wrapped, passing all of the arguments
// for the last one (the last argument is the ignored closure). // except for the last one (the last argument is the ignored
// closure).
const Typed_identifier_list* orig_params = orig_fntype->parameters();
Expression_list* args; Expression_list* args;
if (orig_params == NULL || orig_params->empty()) if (orig_params == NULL || orig_params->empty())
args = NULL; args = NULL;
else else
{ {
const Typed_identifier_list* new_params = new_fntype->parameters();
args = new Expression_list(); args = new Expression_list();
for (Typed_identifier_list::const_iterator p = new_params->begin(); for (Typed_identifier_list::const_iterator p = new_params->begin();
p + 1 != new_params->end(); p + 1 != new_params->end();
...@@ -3627,23 +3606,7 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no, ...@@ -3627,23 +3606,7 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
loc); loc);
call->set_varargs_are_lowered(); call->set_varargs_are_lowered();
Statement* s; Statement* s = Statement::make_return_from_call(call, loc);
if (orig_results == NULL || orig_results->empty())
s = Statement::make_statement(call, true);
else
{
Expression_list* vals = new Expression_list();
size_t rc = orig_results->size();
if (rc == 1)
vals->push_back(call);
else
{
for (size_t i = 0; i < rc; ++i)
vals->push_back(Expression::make_call_result(call, i));
}
s = Statement::make_return_statement(vals, loc);
}
gogo->add_statement(s); gogo->add_statement(s);
Block* b = gogo->finish_block(loc); Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc); gogo->add_block(b, loc);
......
...@@ -2815,6 +2815,28 @@ Statement::make_return_statement(Expression_list* vals, ...@@ -2815,6 +2815,28 @@ Statement::make_return_statement(Expression_list* vals,
return new Return_statement(vals, location); return new Return_statement(vals, location);
} }
// Make a statement that returns the result of a call expression.
Statement*
Statement::make_return_from_call(Call_expression* call, Location location)
{
size_t rc = call->result_count();
if (rc == 0)
return Statement::make_statement(call, true);
else
{
Expression_list* vals = new Expression_list();
if (rc == 1)
vals->push_back(call);
else
{
for (size_t i = 0; i < rc; ++i)
vals->push_back(Expression::make_call_result(call, i));
}
return Statement::make_return_statement(vals, location);
}
}
// A break or continue statement. // A break or continue statement.
class Bc_statement : public Statement class Bc_statement : public Statement
......
...@@ -207,6 +207,13 @@ class Statement ...@@ -207,6 +207,13 @@ class Statement
static Return_statement* static Return_statement*
make_return_statement(Expression_list*, Location); make_return_statement(Expression_list*, Location);
// Make a statement that returns the result of a call expression.
// If the call does not return any results, this just returns the
// call expression as a statement, assuming that the function will
// end immediately afterward.
static Statement*
make_return_from_call(Call_expression*, Location);
// Make a break statement. // Make a break statement.
static Statement* static Statement*
make_break_statement(Unnamed_label* label, Location); make_break_statement(Unnamed_label* label, Location);
......
...@@ -3396,7 +3396,8 @@ Function_type::do_get_backend(Gogo* gogo) ...@@ -3396,7 +3396,8 @@ Function_type::do_get_backend(Gogo* gogo)
// passed when invoking the function indirectly, via the struct. // passed when invoking the function indirectly, via the struct.
Location loc = this->location(); Location loc = this->location();
Btype* struct_type = gogo->backend()->placeholder_struct_type("", loc); Btype* struct_type =
gogo->backend()->placeholder_struct_type("__go_descriptor", loc);
Btype* ptr_struct_type = gogo->backend()->pointer_type(struct_type); Btype* ptr_struct_type = gogo->backend()->pointer_type(struct_type);
Backend::Btyped_identifier breceiver; Backend::Btyped_identifier breceiver;
...@@ -3835,6 +3836,49 @@ Function_type::copy_with_receiver(Type* receiver_type) const ...@@ -3835,6 +3836,49 @@ Function_type::copy_with_receiver(Type* receiver_type) const
return ret; return ret;
} }
// Make a copy of a function type ignoring any receiver and adding a
// closure parameter.
Function_type*
Function_type::copy_with_closure(Type* closure_type) const
{
Typed_identifier_list* new_params = new Typed_identifier_list();
const Typed_identifier_list* orig_params = this->parameters_;
if (orig_params != NULL && !orig_params->empty())
{
static int count;
char buf[50];
for (Typed_identifier_list::const_iterator p = orig_params->begin();
p != orig_params->end();
++p)
{
snprintf(buf, sizeof buf, "pt.%u", count);
++count;
new_params->push_back(Typed_identifier(buf, p->type(),
p->location()));
}
}
new_params->push_back(Typed_identifier("closure.0", closure_type,
this->location_));
const Typed_identifier_list* orig_results = this->results_;
Typed_identifier_list* new_results;
if (orig_results == NULL || orig_results->empty())
new_results = NULL;
else
{
new_results = new Typed_identifier_list();
for (Typed_identifier_list::const_iterator p = orig_results->begin();
p != orig_results->end();
++p)
new_results->push_back(Typed_identifier("", p->type(),
p->location()));
}
return Type::make_function_type(NULL, new_params, new_results,
this->location());
}
// Make a function type. // Make a function type.
Function_type* Function_type*
...@@ -7580,7 +7624,7 @@ Method::bind_method(Expression* expr, Location location) const ...@@ -7580,7 +7624,7 @@ Method::bind_method(Expression* expr, Location location) const
// the child class. // the child class.
return this->do_bind_method(expr, location); return this->do_bind_method(expr, location);
} }
return Expression::make_bound_method(expr, this->stub_, location); return Expression::make_bound_method(expr, this, this->stub_, location);
} }
// Return the named object associated with a method. This may only be // Return the named object associated with a method. This may only be
...@@ -7623,8 +7667,8 @@ Expression* ...@@ -7623,8 +7667,8 @@ Expression*
Named_method::do_bind_method(Expression* expr, Location location) const Named_method::do_bind_method(Expression* expr, Location location) const
{ {
Named_object* no = this->named_object_; Named_object* no = this->named_object_;
Bound_method_expression* bme = Expression::make_bound_method(expr, no, Bound_method_expression* bme = Expression::make_bound_method(expr, this,
location); no, location);
// If this is not a local method, and it does not use a stub, then // If this is not a local method, and it does not use a stub, then
// the real method expects a different type. We need to cast the // the real method expects a different type. We need to cast the
// first argument. // first argument.
...@@ -9002,28 +9046,16 @@ Type::build_one_stub_method(Gogo* gogo, Method* method, ...@@ -9002,28 +9046,16 @@ Type::build_one_stub_method(Gogo* gogo, Method* method,
Call_expression* call = Expression::make_call(func, arguments, is_varargs, Call_expression* call = Expression::make_call(func, arguments, is_varargs,
location); location);
call->set_hidden_fields_are_ok(); call->set_hidden_fields_are_ok();
size_t count = call->result_count();
if (count == 0)
gogo->add_statement(Statement::make_statement(call, true));
else
{
Expression_list* retvals = new Expression_list();
if (count <= 1)
retvals->push_back(call);
else
{
for (size_t i = 0; i < count; ++i)
retvals->push_back(Expression::make_call_result(call, i));
}
Return_statement* retstat = Statement::make_return_statement(retvals,
location);
Statement* s = Statement::make_return_from_call(call, location);
Return_statement* retstat = s->return_statement();
if (retstat != NULL)
{
// We can return values with hidden fields from a stub. This is // We can return values with hidden fields from a stub. This is
// necessary if the method is itself hidden. // necessary if the method is itself hidden.
retstat->set_hidden_fields_are_ok(); retstat->set_hidden_fields_are_ok();
gogo->add_statement(retstat);
} }
gogo->add_statement(s);
} }
// Apply FIELD_INDEXES to EXPR. The field indexes have to be applied // Apply FIELD_INDEXES to EXPR. The field indexes have to be applied
......
...@@ -1789,6 +1789,12 @@ class Function_type : public Type ...@@ -1789,6 +1789,12 @@ class Function_type : public Type
Function_type* Function_type*
copy_with_receiver(Type*) const; copy_with_receiver(Type*) const;
// Return a copy of this type ignoring any receiver and adding a
// final closure parameter of type CLOSURE_TYPE. This is used when
// creating descriptors.
Function_type*
copy_with_closure(Type* closure_type) const;
static Type* static Type*
make_function_type_descriptor_type(); make_function_type_descriptor_type();
......
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