@@ -1090,6 +1090,15 @@
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 @@
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 @@
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 @@
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
- {
- 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"));
- }
-}
-
-// 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.
+ Named_object* fn = this->method_->named_object();
+ if (!fn->is_function() && !fn->is_function_declaration())
+ {
+ 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
+ {
+ 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*)
-{
- error_at(this->location(), "reference to method other than calling it");
- return error_mark_node;
+Bound_method_expression::do_get_tree(Translate_context* context)
+{
+ 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 @@
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)
-{
- return new Bound_method_expression(expr, method, location);
+Expression::make_bound_method(Expression* expr, const Method* method,
+ Named_object* function, Location 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 @@
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 @@
// 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 @@
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 @@
}
}
-// 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*)
-{
- error_at(this->location(), "reference to method other than calling it");
- return error_mark_node;
+Interface_field_reference_expression::do_get_tree(Translate_context* context)
+{
+ 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 @@
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);
@@ -16,6 +16,7 @@
class Traverse;
class Statement_inserter;
class Type;
+class Method;
struct Type_context;
class Integer_type;
class Float_type;
@@ -224,9 +225,11 @@
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 @@
do_type();
void
- do_determine_type(const Type_context*)
- { }
+ do_determine_type(const Type_context*);
Expression*
do_copy()
@@ -1852,10 +1854,10 @@
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 @@
first_argument_type() const
{ return this->expr_type_; }
- // Return the method function.
+ // Return the method.
+ const Method*
+ method() const
+ { return this->method_; }
+
+ // Return the function to call.
Named_object*
- method()
- { return this->method_; }
+ 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 @@
do_copy()
{
return new Bound_method_expression(this->expr_->copy(), this->method_,
- this->location());
+ this->function_, this->location());
}
tree
@@ -1907,6 +1922,11 @@
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 @@
// 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 @@
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 @@
int
do_traverse(Traverse* traverse);
+ Expression*
+ do_lower(Gogo*, Named_object*, Statement_inserter*, int);
+
Type*
do_type();
@@ -2070,6 +2102,13 @@
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_;
@@ -1795,11 +1795,36 @@
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 @@
// 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 @@
{
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 @@
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 @@
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);
@@ -2815,6 +2815,28 @@
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
@@ -207,6 +207,13 @@
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);
@@ -3396,7 +3396,8 @@
// 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 @@
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 @@
// 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 @@
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 @@
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
@@ -1789,6 +1789,12 @@
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();