Skip to content

Commit

Permalink
compiler: traverse func subexprs when creating func descriptors
Browse files Browse the repository at this point in the history
Fix the Create_func_descriptors pass to traverse the subexpressions of
the function in a Call_expression.  There are no subexpressions in the
normal case of calling a function a method directly, but there are
subexpressions when in code like F().M() when F returns an interface type.

Forgetting to traverse the function subexpressions was almost entirely
hidden by the fact that we also created the necessary thunks in
Bound_method_expression::do_flatten and
Interface_field_reference_expression::do_get_backend.  However, when
the thunks were created there, they did not go through the
order_evaluations pass.  This almost always worked, but failed in the
case in which the function being thunked returned multiple results, as
order_evaluations takes the necessary step of moving the
Call_expression into its own statement, and that would not happen when
order_evaluations was not called.  Avoid hiding errors like this by
changing those methods to only lookup the previously created thunk,
rather than creating it if it was not already created.

The test case for this is https://golang.org/cl/363156.

Fixes golang/go#49512

Change-Id: I6b273156f16ed6ed22e600097628a1cb6499de64
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/363274
Trust: Ian Lance Taylor <iant@golang.org>
Trust: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Than McIntosh <thanm@google.com>
  • Loading branch information
ianlancetaylor committed Nov 11, 2021
1 parent 128ea3d commit 3e9f4ee
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 10 deletions.
57 changes: 47 additions & 10 deletions go/expressions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7981,7 +7981,7 @@ Bound_method_expression::do_check_types(Gogo*)
Bound_method_expression::Method_value_thunks
Bound_method_expression::method_value_thunks;

// Find or create the thunk for METHOD.
// Find or create the thunk for FN.

Named_object*
Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
Expand Down Expand Up @@ -8078,14 +8078,28 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method,
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);

// This is called after lowering but before determine_types.
gogo->lower_block(new_no, b);
gogo->flatten_block(new_no, b);

gogo->finish_function(loc);

ins.first->second = new_no;
return new_no;
}

// Look up a thunk for FN.

Named_object*
Bound_method_expression::lookup_thunk(Named_object* fn)
{
Method_value_thunks::const_iterator p =
Bound_method_expression::method_value_thunks.find(fn);
if (p == Bound_method_expression::method_value_thunks.end())
return NULL;
return p->second;
}

// 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
Expand Down Expand Up @@ -8129,10 +8143,11 @@ Bound_method_expression::do_flatten(Gogo* gogo, Named_object*,
{
Location loc = this->location();

Named_object* thunk = Bound_method_expression::create_thunk(gogo,
this->method_,
this->function_);
if (thunk->is_erroneous())
Named_object* thunk = Bound_method_expression::lookup_thunk(this->function_);

// The thunk should have been created during the
// create_function_descriptors pass.
if (thunk == NULL || thunk->is_erroneous())
{
go_assert(saw_errors());
return Expression::make_error(loc);
Expand Down Expand Up @@ -14757,14 +14772,34 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo,
gogo->add_statement(s);
Block* b = gogo->finish_block(loc);
gogo->add_block(b, loc);

// This is called after lowering but before determine_types.
gogo->lower_block(new_no, b);
gogo->flatten_block(new_no, b);

gogo->finish_function(loc);

ins.first->second->push_back(std::make_pair(name, new_no));
return new_no;
}

// Lookup a thunk to call method NAME on TYPE.

Named_object*
Interface_field_reference_expression::lookup_thunk(Interface_type* type,
const std::string& name)
{
Interface_method_thunks::const_iterator p =
Interface_field_reference_expression::interface_method_thunks.find(type);
if (p == Interface_field_reference_expression::interface_method_thunks.end())
return NULL;
for (Method_thunks::const_iterator pm = p->second->begin();
pm != p->second->end();
++pm)
if (pm->first == name)
return pm->second;
return NULL;
}

// Get the backend representation for a method value.

Bexpression*
Expand All @@ -14778,9 +14813,11 @@ Interface_field_reference_expression::do_get_backend(Translate_context* context)
}

Named_object* thunk =
Interface_field_reference_expression::create_thunk(context->gogo(),
type, this->name_);
if (thunk->is_erroneous())
Interface_field_reference_expression::lookup_thunk(type, this->name_);

// The thunk should have been created during the
// create_function_descriptors pass.
if (thunk == NULL || thunk->is_erroneous())
{
go_assert(saw_errors());
return context->backend()->error_expression();
Expand Down
8 changes: 8 additions & 0 deletions go/expressions.h
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,10 @@ class Bound_method_expression : public Expression
static Named_object*
create_thunk(Gogo*, const Method* method, Named_object* function);

// Look up a thunk.
static Named_object*
lookup_thunk(Named_object* function);

protected:
int
do_traverse(Traverse*);
Expand Down Expand Up @@ -3578,6 +3582,10 @@ class Interface_field_reference_expression : public Expression
static Named_object*
create_thunk(Gogo*, Interface_type* type, const std::string& name);

// Look up a thunk.
static Named_object*
lookup_thunk(Interface_type* type, const std::string& name);

// Return an expression for the pointer to the function to call.
Expression*
get_function();
Expand Down
5 changes: 5 additions & 0 deletions go/gogo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3430,6 +3430,11 @@ Create_function_descriptors::expression(Expression** pexpr)
if (args->traverse(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
}

// Traverse the subexpressions of the function, if any.
if (fn->traverse_subexpressions(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;

return TRAVERSE_SKIP_COMPONENTS;
}
}
Expand Down

0 comments on commit 3e9f4ee

Please sign in to comment.