compiler: Add support for method values.

From-SVN: r200379
This commit is contained in:
Ian Lance Taylor 2013-06-24 22:11:12 +00:00
parent 39953c7972
commit 571d3f918f
7 changed files with 700 additions and 162 deletions

View File

@ -1090,6 +1090,15 @@ Set_and_use_temporary_expression::do_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.
void
@ -6626,20 +6635,49 @@ Bound_method_expression::do_traverse(Traverse* 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
// object is really the type of the method with no receiver. We
// should be able to get away with just returning the type of the
// method.
// object is simply the type of the method with no receiver.
Type*
Bound_method_expression::do_type()
{
if (this->method_->is_function())
return this->method_->func_value()->type();
else if (this->method_->is_function_declaration())
return this->method_->func_declaration_value()->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
return Type::make_error_type();
return fntype->copy_without_receiver();
}
// Determine the types of a method expression.
@ -6647,7 +6685,14 @@ Bound_method_expression::do_type()
void
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())
this->expr_->determine_type_no_context();
else
@ -6662,31 +6707,278 @@ Bound_method_expression::do_determine_type(const Type_context*)
void
Bound_method_expression::do_check_types(Gogo*)
{
if (!this->method_->is_function()
&& !this->method_->is_function_declaration())
this->report_error(_("object is not a method"));
else
Named_object* fn = this->method_->named_object();
if (!fn->is_function() && !fn->is_function_declaration())
{
Type* rtype = this->type()->function_type()->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"));
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"));
}
// Get the tree for a method expression. There is no standard tree
// 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.
// 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
{
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(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 value.
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");
return error_mark_node;
Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(),
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.
@ -6705,16 +6997,16 @@ Bound_method_expression::do_dump_expression(Ast_dump_context* ast_dump_context)
ast_dump_context->ostream() << ")";
}
ast_dump_context->ostream() << "." << this->method_->name();
ast_dump_context->ostream() << "." << this->function_->name();
}
// Make a method expression.
Bound_method_expression*
Expression::make_bound_method(Expression* expr, Named_object* method,
Location location)
Expression::make_bound_method(Expression* expr, const Method* method,
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
@ -8921,7 +9213,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
Bound_method_expression* bme = this->fn_->bound_method_expression();
if (bme != NULL)
{
Named_object* method = bme->method();
Named_object* methodfn = bme->function();
Expression* first_arg = bme->first_argument();
// We always pass a pointer when calling a method.
@ -8962,7 +9254,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
// old arguments, because we may be traversing them up in some
// caller. FIXME.
this->args_ = new_args;
this->fn_ = Expression::make_func_reference(method, NULL,
this->fn_ = Expression::make_func_reference(methodfn, NULL,
bme->location());
}
@ -11158,6 +11450,28 @@ Interface_field_reference_expression::do_traverse(Traverse* 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.
Type*
@ -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
// standard tree type representation for this: it's a function
// attached to its first argument, like a Bound_method_expression.
// 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.
// If an interface field reference is not simply called, then it is
// represented as a closure. The closure will hold a single variable,
// the value of the interface on which the method should be called.
// The function will be a simple thunk that pulls the 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
// 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
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");
return error_mark_node;
Interface_type* type = this->expr_->type()->interface_type();
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.
@ -11485,22 +11969,7 @@ Selector_expression::lower_method_expression(Gogo* gogo)
method_type->is_varargs(),
location);
size_t count = call->result_count();
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);
}
Statement* s = Statement::make_return_from_call(call, location);
gogo->add_statement(s);
Block* b = gogo->finish_block(location);

View File

@ -16,6 +16,7 @@ class Translate_context;
class Traverse;
class Statement_inserter;
class Type;
class Method;
struct Type_context;
class Integer_type;
class Float_type;
@ -224,9 +225,11 @@ class Expression
make_call_result(Call_expression*, unsigned int index);
// 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*
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
// which represents LEFT[START:END]. END may be NULL, meaning an
@ -1079,8 +1082,7 @@ class Set_and_use_temporary_expression : public Expression
do_type();
void
do_determine_type(const Type_context*)
{ }
do_determine_type(const Type_context*);
Expression*
do_copy()
@ -1852,10 +1854,10 @@ class Map_index_expression : public Expression
class Bound_method_expression : public Expression
{
public:
Bound_method_expression(Expression* expr, Named_object* method,
Location location)
Bound_method_expression(Expression* expr, const Method *method,
Named_object* function, Location 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.
@ -1870,20 +1872,33 @@ class Bound_method_expression : public Expression
first_argument_type() const
{ return this->expr_type_; }
// Return the method function.
Named_object*
method()
// Return the method.
const Method*
method() const
{ return this->method_; }
// Return the function to call.
Named_object*
function() const
{ return this->function_; }
// Set the implicit type of the expression.
void
set_first_argument_type(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:
int
do_traverse(Traverse*);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Type*
do_type();
@ -1897,7 +1912,7 @@ class Bound_method_expression : public Expression
do_copy()
{
return new Bound_method_expression(this->expr_->copy(), this->method_,
this->location());
this->function_, this->location());
}
tree
@ -1907,6 +1922,11 @@ class Bound_method_expression : public Expression
do_dump_expression(Ast_dump_context*) const;
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
// as the first argument.
Expression* expr_;
@ -1914,8 +1934,12 @@ class Bound_method_expression : public Expression
// NULL in the normal case, non-NULL when using a method from an
// anonymous field which does not require a stub.
Type* expr_type_;
// The method itself.
Named_object* method_;
// The 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.
@ -2031,6 +2055,11 @@ class Interface_field_reference_expression : public Expression
name() const
{ 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
// tree for the expression.
tree
@ -2046,6 +2075,9 @@ class Interface_field_reference_expression : public Expression
int
do_traverse(Traverse* traverse);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Type*
do_type();
@ -2070,6 +2102,13 @@ class Interface_field_reference_expression : public Expression
do_dump_expression(Ast_dump_context*) const;
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
// of interface or pointer to interface.
Expression* expr_;

View File

@ -1795,11 +1795,36 @@ Create_function_descriptors::expression(Expression** pexpr)
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();
if (ce != NULL)
{
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.
Expression_list* args = ce->args();
@ -2806,22 +2831,7 @@ Build_recover_thunks::function(Named_object* orig_no)
// Any varargs call has already been lowered.
call->set_varargs_are_lowered();
Statement* s;
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);
}
Statement* s = Statement::make_return_from_call(call, location);
s->determine_types();
gogo->add_statement(s);
@ -3557,42 +3567,8 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
{
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());
new_params->push_back(Typed_identifier("closure.0", vt, loc));
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);
Function_type* new_fntype = orig_fntype->copy_with_closure(vt);
std::string name = no->name() + "$descriptorfn";
Named_object* dno = gogo->start_function(name, new_fntype, false, loc);
@ -3602,13 +3578,16 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
Expression* fn = Expression::make_func_reference(no, NULL, loc);
// Call the wrapper function, passing all of the arguments except
// for the last one (the last argument is the ignored closure).
// Call the function begin wrapped, passing all of the arguments
// except for the last one (the last argument is the ignored
// closure).
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();
@ -3627,23 +3606,7 @@ Function::make_descriptor_wrapper(Gogo* gogo, Named_object* no,
loc);
call->set_varargs_are_lowered();
Statement* s;
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);
}
Statement* s = Statement::make_return_from_call(call, loc);
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);

View File

@ -2815,6 +2815,28 @@ Statement::make_return_statement(Expression_list* vals,
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.
class Bc_statement : public Statement

View File

@ -207,6 +207,13 @@ class Statement
static Return_statement*
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.
static Statement*
make_break_statement(Unnamed_label* label, Location);

View File

@ -3396,7 +3396,8 @@ Function_type::do_get_backend(Gogo* gogo)
// passed when invoking the function indirectly, via the struct.
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);
Backend::Btyped_identifier breceiver;
@ -3835,6 +3836,49 @@ Function_type::copy_with_receiver(Type* receiver_type) const
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.
Function_type*
@ -7580,7 +7624,7 @@ Method::bind_method(Expression* expr, Location location) const
// the child class.
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
@ -7623,8 +7667,8 @@ Expression*
Named_method::do_bind_method(Expression* expr, Location location) const
{
Named_object* no = this->named_object_;
Bound_method_expression* bme = Expression::make_bound_method(expr, no,
location);
Bound_method_expression* bme = Expression::make_bound_method(expr, this,
no, location);
// 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
// first argument.
@ -9002,28 +9046,16 @@ Type::build_one_stub_method(Gogo* gogo, Method* method,
Call_expression* call = Expression::make_call(func, arguments, is_varargs,
location);
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
// necessary if the method is itself hidden.
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

View File

@ -1789,6 +1789,12 @@ class Function_type : public Type
Function_type*
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*
make_function_type_descriptor_type();