compiler: move Selector_expression up in file

This is a mechanical change to move Selector_expression up in expressions.cc.
This will make it visible to Builtin_call_expression for later work.
This produces a very large "git --diff", but "git diff --minimal" is clear.

Change-Id: I276355aa4d417b29e675077cc4d21153bdf9a980
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/536642
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Ian Lance Taylor 2023-10-19 18:53:22 -07:00
Родитель 3c2a441ef6
Коммит e997b02015
1 изменённых файлов: 281 добавлений и 281 удалений

Просмотреть файл

@ -8426,6 +8426,287 @@ Expression::make_bound_method(Expression* expr, const Method* method,
return new Bound_method_expression(expr, method, function, location);
}
// A general selector. This is a Parser_expression for LEFT.NAME. It
// is lowered after we know the type of the left hand side.
class Selector_expression : public Parser_expression
{
public:
Selector_expression(Expression* left, const std::string& name,
Location location)
: Parser_expression(EXPRESSION_SELECTOR, location),
left_(left), name_(name)
{ }
protected:
int
do_traverse(Traverse* traverse)
{ return Expression::traverse(&this->left_, traverse); }
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_copy()
{
return new Selector_expression(this->left_->copy(), this->name_,
this->location());
}
void
do_dump_expression(Ast_dump_context* ast_dump_context) const;
private:
Expression*
lower_method_expression(Gogo*);
// The expression on the left hand side.
Expression* left_;
// The name on the right hand side.
std::string name_;
};
// Lower a selector expression once we know the real type of the left
// hand side.
Expression*
Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*,
int)
{
Expression* left = this->left_;
if (left->is_type_expression())
return this->lower_method_expression(gogo);
return Type::bind_field_or_method(gogo, left->type(), left, this->name_,
this->location());
}
// Lower a method expression T.M or (*T).M. We turn this into a
// function literal.
Expression*
Selector_expression::lower_method_expression(Gogo* gogo)
{
Location location = this->location();
Type* left_type = this->left_->type();
Type* type = left_type;
const std::string& name(this->name_);
bool is_pointer;
if (type->points_to() == NULL)
is_pointer = false;
else
{
is_pointer = true;
type = type->points_to();
}
Named_type* nt = type->named_type();
Struct_type* st = type->struct_type();
bool is_ambiguous;
Method* method = NULL;
if (nt != NULL)
method = nt->method_function(name, &is_ambiguous);
else if (st != NULL)
method = st->method_function(name, &is_ambiguous);
const Typed_identifier* imethod = NULL;
if (method == NULL && !is_pointer)
{
Interface_type* it = type->interface_type();
if (it != NULL)
imethod = it->find_method(name);
}
if ((method == NULL && imethod == NULL)
|| (left_type->named_type() != NULL && left_type->points_to() != NULL))
{
if (nt != NULL)
{
if (!is_ambiguous)
go_error_at(location, "type %<%s%s%> has no method %<%s%>",
is_pointer ? "*" : "",
nt->message_name().c_str(),
Gogo::message_name(name).c_str());
else
go_error_at(location, "method %<%s%s%> is ambiguous in type %<%s%>",
Gogo::message_name(name).c_str(),
is_pointer ? "*" : "",
nt->message_name().c_str());
}
else
{
if (!is_ambiguous)
go_error_at(location, "type has no method %<%s%>",
Gogo::message_name(name).c_str());
else
go_error_at(location, "method %<%s%> is ambiguous",
Gogo::message_name(name).c_str());
}
return Expression::make_error(location);
}
if (method != NULL && !is_pointer && !method->is_value_method())
{
go_error_at(location, "method requires pointer (use %<(*%s).%s%>)",
nt->message_name().c_str(),
Gogo::message_name(name).c_str());
return Expression::make_error(location);
}
// Build a new function type in which the receiver becomes the first
// argument.
Function_type* method_type;
if (method != NULL)
{
method_type = method->type();
go_assert(method_type->is_method());
}
else
{
method_type = imethod->type()->function_type();
go_assert(method_type != NULL && !method_type->is_method());
}
const char* const receiver_name = "$this";
Typed_identifier_list* parameters = new Typed_identifier_list();
parameters->push_back(Typed_identifier(receiver_name, this->left_->type(),
location));
const Typed_identifier_list* method_parameters = method_type->parameters();
if (method_parameters != NULL)
{
int i = 0;
for (Typed_identifier_list::const_iterator p = method_parameters->begin();
p != method_parameters->end();
++p, ++i)
{
if (!p->name().empty() && !Gogo::is_sink_name(p->name()))
parameters->push_back(*p);
else
{
char buf[20];
snprintf(buf, sizeof buf, "$param%d", i);
parameters->push_back(Typed_identifier(buf, p->type(),
p->location()));
}
}
}
const Typed_identifier_list* method_results = method_type->results();
Typed_identifier_list* results;
if (method_results == NULL)
results = NULL;
else
{
results = new Typed_identifier_list();
for (Typed_identifier_list::const_iterator p = method_results->begin();
p != method_results->end();
++p)
results->push_back(*p);
}
Function_type* fntype = Type::make_function_type(NULL, parameters, results,
location);
if (method_type->is_varargs())
fntype->set_is_varargs();
// We generate methods which always takes a pointer to the receiver
// as their first argument. If this is for a pointer type, we can
// simply reuse the existing function. We use an internal hack to
// get the right type.
// FIXME: This optimization is disabled because it doesn't yet work
// with function descriptors when the method expression is not
// directly called.
if (method != NULL && is_pointer && false)
{
Named_object* mno = (method->needs_stub_method()
? method->stub_object()
: method->named_object());
Expression* f = Expression::make_func_reference(mno, NULL, location);
f = Expression::make_cast(fntype, f, location);
Type_conversion_expression* tce =
static_cast<Type_conversion_expression*>(f);
tce->set_may_convert_function_types();
return f;
}
Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false,
location);
Named_object* vno = gogo->lookup(receiver_name, NULL);
go_assert(vno != NULL);
Expression* ve = Expression::make_var_reference(vno, location);
Expression* bm;
if (method != NULL)
bm = Type::bind_field_or_method(gogo, type, ve, name, location);
else
bm = Expression::make_interface_field_reference(ve, name, location);
// Even though we found the method above, if it has an error type we
// may see an error here.
if (bm->is_error_expression())
{
gogo->finish_function(location);
return bm;
}
Expression_list* args;
if (parameters->size() <= 1)
args = NULL;
else
{
args = new Expression_list();
Typed_identifier_list::const_iterator p = parameters->begin();
++p;
for (; p != parameters->end(); ++p)
{
vno = gogo->lookup(p->name(), NULL);
go_assert(vno != NULL);
args->push_back(Expression::make_var_reference(vno, location));
}
}
gogo->start_block(location);
Call_expression* call = Expression::make_call(bm, args,
method_type->is_varargs(),
location);
Statement* s = Statement::make_return_from_call(call, location);
gogo->add_statement(s);
Block* b = gogo->finish_block(location);
gogo->add_block(b, location);
// Lower the call in case there are multiple results.
gogo->lower_block(no, b);
gogo->flatten_block(no, b);
gogo->finish_function(location);
return Expression::make_func_reference(no, NULL, location);
}
// Dump the ast for a selector expression.
void
Selector_expression::do_dump_expression(Ast_dump_context* ast_dump_context)
const
{
ast_dump_context->dump_expression(this->left_);
ast_dump_context->ostream() << ".";
ast_dump_context->ostream() << this->name_;
}
// Make a selector expression.
Expression*
Expression::make_selector(Expression* left, const std::string& name,
Location location)
{
return new Selector_expression(left, name, location);
}
// Class Builtin_call_expression. This is used for a call to a
// builtin function.
@ -15265,287 +15546,6 @@ Expression::make_interface_field_reference(Expression* expr,
return new Interface_field_reference_expression(expr, field, location);
}
// A general selector. This is a Parser_expression for LEFT.NAME. It
// is lowered after we know the type of the left hand side.
class Selector_expression : public Parser_expression
{
public:
Selector_expression(Expression* left, const std::string& name,
Location location)
: Parser_expression(EXPRESSION_SELECTOR, location),
left_(left), name_(name)
{ }
protected:
int
do_traverse(Traverse* traverse)
{ return Expression::traverse(&this->left_, traverse); }
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_copy()
{
return new Selector_expression(this->left_->copy(), this->name_,
this->location());
}
void
do_dump_expression(Ast_dump_context* ast_dump_context) const;
private:
Expression*
lower_method_expression(Gogo*);
// The expression on the left hand side.
Expression* left_;
// The name on the right hand side.
std::string name_;
};
// Lower a selector expression once we know the real type of the left
// hand side.
Expression*
Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*,
int)
{
Expression* left = this->left_;
if (left->is_type_expression())
return this->lower_method_expression(gogo);
return Type::bind_field_or_method(gogo, left->type(), left, this->name_,
this->location());
}
// Lower a method expression T.M or (*T).M. We turn this into a
// function literal.
Expression*
Selector_expression::lower_method_expression(Gogo* gogo)
{
Location location = this->location();
Type* left_type = this->left_->type();
Type* type = left_type;
const std::string& name(this->name_);
bool is_pointer;
if (type->points_to() == NULL)
is_pointer = false;
else
{
is_pointer = true;
type = type->points_to();
}
Named_type* nt = type->named_type();
Struct_type* st = type->struct_type();
bool is_ambiguous;
Method* method = NULL;
if (nt != NULL)
method = nt->method_function(name, &is_ambiguous);
else if (st != NULL)
method = st->method_function(name, &is_ambiguous);
const Typed_identifier* imethod = NULL;
if (method == NULL && !is_pointer)
{
Interface_type* it = type->interface_type();
if (it != NULL)
imethod = it->find_method(name);
}
if ((method == NULL && imethod == NULL)
|| (left_type->named_type() != NULL && left_type->points_to() != NULL))
{
if (nt != NULL)
{
if (!is_ambiguous)
go_error_at(location, "type %<%s%s%> has no method %<%s%>",
is_pointer ? "*" : "",
nt->message_name().c_str(),
Gogo::message_name(name).c_str());
else
go_error_at(location, "method %<%s%s%> is ambiguous in type %<%s%>",
Gogo::message_name(name).c_str(),
is_pointer ? "*" : "",
nt->message_name().c_str());
}
else
{
if (!is_ambiguous)
go_error_at(location, "type has no method %<%s%>",
Gogo::message_name(name).c_str());
else
go_error_at(location, "method %<%s%> is ambiguous",
Gogo::message_name(name).c_str());
}
return Expression::make_error(location);
}
if (method != NULL && !is_pointer && !method->is_value_method())
{
go_error_at(location, "method requires pointer (use %<(*%s).%s%>)",
nt->message_name().c_str(),
Gogo::message_name(name).c_str());
return Expression::make_error(location);
}
// Build a new function type in which the receiver becomes the first
// argument.
Function_type* method_type;
if (method != NULL)
{
method_type = method->type();
go_assert(method_type->is_method());
}
else
{
method_type = imethod->type()->function_type();
go_assert(method_type != NULL && !method_type->is_method());
}
const char* const receiver_name = "$this";
Typed_identifier_list* parameters = new Typed_identifier_list();
parameters->push_back(Typed_identifier(receiver_name, this->left_->type(),
location));
const Typed_identifier_list* method_parameters = method_type->parameters();
if (method_parameters != NULL)
{
int i = 0;
for (Typed_identifier_list::const_iterator p = method_parameters->begin();
p != method_parameters->end();
++p, ++i)
{
if (!p->name().empty() && !Gogo::is_sink_name(p->name()))
parameters->push_back(*p);
else
{
char buf[20];
snprintf(buf, sizeof buf, "$param%d", i);
parameters->push_back(Typed_identifier(buf, p->type(),
p->location()));
}
}
}
const Typed_identifier_list* method_results = method_type->results();
Typed_identifier_list* results;
if (method_results == NULL)
results = NULL;
else
{
results = new Typed_identifier_list();
for (Typed_identifier_list::const_iterator p = method_results->begin();
p != method_results->end();
++p)
results->push_back(*p);
}
Function_type* fntype = Type::make_function_type(NULL, parameters, results,
location);
if (method_type->is_varargs())
fntype->set_is_varargs();
// We generate methods which always takes a pointer to the receiver
// as their first argument. If this is for a pointer type, we can
// simply reuse the existing function. We use an internal hack to
// get the right type.
// FIXME: This optimization is disabled because it doesn't yet work
// with function descriptors when the method expression is not
// directly called.
if (method != NULL && is_pointer && false)
{
Named_object* mno = (method->needs_stub_method()
? method->stub_object()
: method->named_object());
Expression* f = Expression::make_func_reference(mno, NULL, location);
f = Expression::make_cast(fntype, f, location);
Type_conversion_expression* tce =
static_cast<Type_conversion_expression*>(f);
tce->set_may_convert_function_types();
return f;
}
Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false,
location);
Named_object* vno = gogo->lookup(receiver_name, NULL);
go_assert(vno != NULL);
Expression* ve = Expression::make_var_reference(vno, location);
Expression* bm;
if (method != NULL)
bm = Type::bind_field_or_method(gogo, type, ve, name, location);
else
bm = Expression::make_interface_field_reference(ve, name, location);
// Even though we found the method above, if it has an error type we
// may see an error here.
if (bm->is_error_expression())
{
gogo->finish_function(location);
return bm;
}
Expression_list* args;
if (parameters->size() <= 1)
args = NULL;
else
{
args = new Expression_list();
Typed_identifier_list::const_iterator p = parameters->begin();
++p;
for (; p != parameters->end(); ++p)
{
vno = gogo->lookup(p->name(), NULL);
go_assert(vno != NULL);
args->push_back(Expression::make_var_reference(vno, location));
}
}
gogo->start_block(location);
Call_expression* call = Expression::make_call(bm, args,
method_type->is_varargs(),
location);
Statement* s = Statement::make_return_from_call(call, location);
gogo->add_statement(s);
Block* b = gogo->finish_block(location);
gogo->add_block(b, location);
// Lower the call in case there are multiple results.
gogo->lower_block(no, b);
gogo->flatten_block(no, b);
gogo->finish_function(location);
return Expression::make_func_reference(no, NULL, location);
}
// Dump the ast for a selector expression.
void
Selector_expression::do_dump_expression(Ast_dump_context* ast_dump_context)
const
{
ast_dump_context->dump_expression(this->left_);
ast_dump_context->ostream() << ".";
ast_dump_context->ostream() << this->name_;
}
// Make a selector expression.
Expression*
Expression::make_selector(Expression* left, const std::string& name,
Location location)
{
return new Selector_expression(left, name, location);
}
// Class Allocation_expression.
int