Go patch committed: Copy receiver argument for go/defer of method call

Ian Lance Taylor iant@golang.org
Wed Mar 17 19:18:15 GMT 2021


This patch to the Go frontend copies the receiver argument when
compiling a go or defer statement of a method call.  Otherwise when
using go/defer with a value method the method might incorrectly see a
value that was modified after the go/defer statement.  The test case
for this is https://golang.org/cl/302371.  Bootstrapped and ran Go
testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
-------------- next part --------------
dbbef8a3148234579f2cb712d0196a83f837bf76
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index c0bfa1ff78a..a3eef239eca 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-10b00ad87303d37c68b2d54dd25d655bd316946e
+4bdff733a0c2a9ddc3eff104b1be03df058a79c4
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 101cbe7ac31..5409d269ebf 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -11017,7 +11017,7 @@ Call_expression::do_lower(Gogo* gogo, Named_object* function,
   // 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/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 7ad7339bda8..b0660111ac8 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -2524,6 +2524,8 @@ Thunk_statement::is_constant_function() const
     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 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
   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 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function,
 
   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 @@ Thunk_statement::build_struct(Function_type* fntype)
       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 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
 
   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 @@ Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
       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();


More information about the Gcc-patches mailing list