diff mbox

Go patch committed: Implement method values

Message ID mcrwqpjkvlc.fsf@iant-glaptop.roam.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor June 24, 2013, 10:11 p.m. UTC
Go 1.1 introduced a new language concept: method values.  If a value v
is of some type that implements method M, you can now write v.M and get
a function f.  Calling f with some arguments is exactly like calling v.M
with those same arguments.  In other words, v.M keeps a copy of v and
uses in later uses of f.  This patch adds method values to gccgo.
Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline and 4.8 branch.

Ian
diff mbox

Patch

diff -r 281fd093661c go/expressions.cc
--- a/go/expressions.cc	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/expressions.cc	Mon Jun 24 15:02:23 2013 -0700
@@ -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);
diff -r 281fd093661c go/expressions.h
--- a/go/expressions.h	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/expressions.h	Mon Jun 24 15:02:23 2013 -0700
@@ -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_;
diff -r 281fd093661c go/gogo.cc
--- a/go/gogo.cc	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/gogo.cc	Mon Jun 24 15:02:23 2013 -0700
@@ -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);
diff -r 281fd093661c go/statements.cc
--- a/go/statements.cc	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/statements.cc	Mon Jun 24 15:02:23 2013 -0700
@@ -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
diff -r 281fd093661c go/statements.h
--- a/go/statements.h	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/statements.h	Mon Jun 24 15:02:23 2013 -0700
@@ -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);
diff -r 281fd093661c go/types.cc
--- a/go/types.cc	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/types.cc	Mon Jun 24 15:02:23 2013 -0700
@@ -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
diff -r 281fd093661c go/types.h
--- a/go/types.h	Fri Jun 21 10:53:50 2013 -0700
+++ b/go/types.h	Mon Jun 24 15:02:23 2013 -0700
@@ -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();