compiler: copy receiver argument for go/defer of method call

Test case is https://golang.org/cl/302371.

Change-Id: I7b18afadee66e65b16afe840e83a5860a197b7fa
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/302270
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 101cbe7..5409d26 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -11017,7 +11017,7 @@
   // If this is call to a method, call the method directly passing the
   // object as the first parameter.
   Bound_method_expression* bme = this->fn_->bound_method_expression();
-  if (bme != NULL)
+  if (bme != NULL && !this->is_deferred_ && !this->is_concurrent_)
     {
       Named_object* methodfn = bme->function();
       Function_type* mft = (methodfn->is_function()
diff --git a/go/statements.cc b/go/statements.cc
index 7ad7339..b066011 100644
--- a/go/statements.cc
+++ b/go/statements.cc
@@ -2524,6 +2524,8 @@
     return fn->func_expression()->closure() == NULL;
   if (fn->interface_field_reference_expression() != NULL)
     return true;
+  if (fn->bound_method_expression() != NULL)
+    return true;
   return false;
 }
 
@@ -2566,6 +2568,7 @@
   Expression* fn = ce->fn();
   Interface_field_reference_expression* interface_method =
     fn->interface_field_reference_expression();
+  Bound_method_expression* bme = fn->bound_method_expression();
 
   Location location = this->location();
 
@@ -2594,6 +2597,8 @@
 
   if (interface_method != NULL)
     vals->push_back(interface_method->expr());
+  if (bme != NULL)
+    vals->push_back(bme->first_argument());
 
   if (ce->args() != NULL)
     {
@@ -2714,6 +2719,16 @@
       fields->push_back(Struct_field(tid));
     }
 
+  // If this thunk statement calls a bound method expression, as in
+  // "go s.m()", we pass the bound method argument to the thunk,
+  // to ensure that we make a copy of it if needed.
+  Bound_method_expression* bme = fn->bound_method_expression();
+  if (bme != NULL)
+    {
+      Typed_identifier tid("object", bme->first_argument()->type(), location);
+      fields->push_back(Struct_field(tid));
+    }
+
   // The predeclared recover function has no argument.  However, we
   // add an argument when building recover thunks.  Handle that here.
   if (ce->is_recover_call())
@@ -2843,6 +2858,7 @@
 
   Interface_field_reference_expression* interface_method =
     ce->fn()->interface_field_reference_expression();
+  Bound_method_expression* bme = ce->fn()->bound_method_expression();
 
   Expression* func_to_call;
   unsigned int next_index;
@@ -2870,6 +2886,17 @@
       next_index = 1;
     }
 
+  if (bme != NULL)
+    {
+      // This is a call to a method.
+      go_assert(next_index == 0);
+      Expression* r = Expression::make_field_reference(thunk_parameter, 0,
+						       location);
+      func_to_call = Expression::make_bound_method(r, bme->method(),
+						   bme->function(), location);
+      next_index = 1;
+    }
+
   Expression_list* call_params = new Expression_list();
   const Struct_field_list* fields = this->struct_type_->fields();
   Struct_field_list::const_iterator p = fields->begin();