int
Bound_method_expression::do_traverse(Traverse* traverse)
{
- if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT)
- return TRAVERSE_EXIT;
- return Expression::traverse(&this->method_, traverse);
+ return Expression::traverse(&this->expr_, traverse);
}
// Return the type of a bound method expression. The type of this
Type*
Bound_method_expression::do_type()
{
- return this->method_->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();
+ else
+ return Type::make_error_type();
}
// Determine the types of a method expression.
void
Bound_method_expression::do_determine_type(const Type_context*)
{
- this->method_->determine_type_no_context();
- Type* mtype = this->method_->type();
- Function_type* fntype = mtype == NULL ? NULL : mtype->function_type();
+ Function_type* fntype = this->type()->function_type();
if (fntype == NULL || !fntype->is_method())
this->expr_->determine_type_no_context();
else
void
Bound_method_expression::do_check_types(Gogo*)
{
- Type* type = this->method_->type()->deref();
- if (type == NULL
- || type->function_type() == NULL
- || !type->function_type()->is_method())
+ if (!this->method_->is_function()
+ && !this->method_->is_function_declaration())
this->report_error(_("object is not a method"));
else
{
- Type* rtype = type->function_type()->receiver()->type()->deref();
+ Type* rtype = this->type()->function_type()->receiver()->type()->deref();
Type* etype = (this->expr_type_ != NULL
? this->expr_type_
: this->expr_->type());
ast_dump_context->ostream() << ")";
}
- ast_dump_context->ostream() << ".";
- ast_dump_context->dump_expression(method_);
+ ast_dump_context->ostream() << "." << this->method_->name();
}
// Make a method expression.
Bound_method_expression*
-Expression::make_bound_method(Expression* expr, Expression* method,
+Expression::make_bound_method(Expression* expr, Named_object* method,
source_location location)
{
return new Bound_method_expression(expr, method, location);
Bound_method_expression* bound_method,
tree* first_arg_ptr)
{
+ Gogo* gogo = context->gogo();
+ source_location loc = this->location();
+
Expression* first_argument = bound_method->first_argument();
tree first_arg = first_argument->get_tree(context);
if (first_arg == error_mark_node)
|| TREE_CODE(first_arg) == INDIRECT_REF
|| TREE_CODE(first_arg) == COMPONENT_REF)
{
- first_arg = build_fold_addr_expr(first_arg);
+ first_arg = build_fold_addr_expr_loc(loc, first_arg);
if (DECL_P(first_arg))
TREE_ADDRESSABLE(first_arg) = 1;
}
get_name(first_arg));
DECL_IGNORED_P(tmp) = 0;
DECL_INITIAL(tmp) = first_arg;
- first_arg = build2(COMPOUND_EXPR, pointer_to_arg_type,
- build1(DECL_EXPR, void_type_node, tmp),
- build_fold_addr_expr(tmp));
+ first_arg = build2_loc(loc, COMPOUND_EXPR, pointer_to_arg_type,
+ build1_loc(loc, DECL_EXPR, void_type_node,
+ tmp),
+ build_fold_addr_expr_loc(loc, tmp));
TREE_ADDRESSABLE(tmp) = 1;
}
if (first_arg == error_mark_node)
{
if (fatype->points_to() == NULL)
fatype = Type::make_pointer_type(fatype);
- Btype* bfatype = fatype->get_backend(context->gogo());
- first_arg = fold_convert(type_to_tree(bfatype), first_arg);
+ Btype* bfatype = fatype->get_backend(gogo);
+ first_arg = fold_convert_loc(loc, type_to_tree(bfatype), first_arg);
if (first_arg == error_mark_node
|| TREE_TYPE(first_arg) == error_mark_node)
return error_mark_node;
*first_arg_ptr = first_arg;
- return bound_method->method()->get_tree(context);
+ Named_object* method = bound_method->method();
+ tree id = method->get_id(gogo);
+ if (id == error_mark_node)
+ return error_mark_node;
+
+ tree fndecl;
+ if (method->is_function())
+ fndecl = method->func_value()->get_or_make_decl(gogo, method, id);
+ else if (method->is_function_declaration())
+ fndecl = method->func_declaration_value()->get_or_make_decl(gogo, method,
+ id);
+ else
+ go_unreachable();
+
+ return build_fold_addr_expr_loc(loc, fndecl);
}
// Get the function and the first argument to use when calling an
// Make an expression which is a method bound to its first
// parameter.
static Bound_method_expression*
- make_bound_method(Expression* object, Expression* method, source_location);
+ make_bound_method(Expression* object, Named_object* method, source_location);
// Make an index or slice expression. This is a parser expression
// which represents LEFT[START:END]. END may be NULL, meaning an
class Bound_method_expression : public Expression
{
public:
- Bound_method_expression(Expression* expr, Expression* method,
+ Bound_method_expression(Expression* expr, Named_object* method,
source_location location)
: Expression(EXPRESSION_BOUND_METHOD, location),
expr_(expr), expr_type_(NULL), method_(method)
first_argument_type() const
{ return this->expr_type_; }
- // Return the reference to the method function.
- Expression*
+ // Return the method function.
+ Named_object*
method()
{ return this->method_; }
Expression*
do_copy()
{
- return new Bound_method_expression(this->expr_->copy(),
- this->method_->copy(),
+ return new Bound_method_expression(this->expr_->copy(), this->method_,
this->location());
}
// 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. This is a Func_expression.
- Expression* method_;
+ // The method itself.
+ Named_object* method_;
};
// A reference to a field in a struct.
// Class Thunk_statement. This is the base class for go and defer
// statements.
-const char* const Thunk_statement::thunk_field_fn = "fn";
-
-const char* const Thunk_statement::thunk_field_receiver = "receiver";
-
// Constructor.
Thunk_statement::Thunk_statement(Statement_classification classification,
this->traverse(&thunk_traverse);
}
+// Return true if the thunk function is a constant, which means that
+// it does not need to be passed to the thunk routine.
+
+bool
+Thunk_statement::is_constant_function() const
+{
+ Call_expression* ce = this->call_->call_expression();
+ Function_type* fntype = ce->get_function_type();
+ if (fntype == NULL)
+ {
+ go_assert(saw_errors());
+ return false;
+ }
+ if (fntype->is_builtin())
+ return true;
+ Expression* fn = ce->fn();
+ if (fn->func_expression() != NULL)
+ return fn->func_expression()->closure() == NULL;
+ if (fn->bound_method_expression() != NULL
+ || fn->interface_field_reference_expression() != NULL)
+ return true;
+ return false;
+}
+
// Simplify complex thunk statements into simple ones. A complicated
// thunk statement is one which takes anything other than zero
// parameters or a single pointer parameter. We rewrite it into code
Bound_method_expression* bound_method = fn->bound_method_expression();
Interface_field_reference_expression* interface_method =
fn->interface_field_reference_expression();
- const bool is_method = bound_method != NULL || interface_method != NULL;
source_location location = this->location();
std::string thunk_name = Gogo::thunk_name();
// Build the thunk.
- this->build_thunk(gogo, thunk_name, fntype);
+ this->build_thunk(gogo, thunk_name);
// Generate code to call the thunk.
// argument to the thunk.
Expression_list* vals = new Expression_list();
- if (fntype->is_builtin())
- ;
- else if (!is_method)
+ if (!this->is_constant_function())
vals->push_back(fn);
- else if (interface_method != NULL)
+
+ if (interface_method != NULL)
vals->push_back(interface_method->expr());
- else if (bound_method != NULL)
+
+ if (bound_method != NULL)
{
- vals->push_back(bound_method->method());
Expression* first_arg = bound_method->first_argument();
// We always pass a pointer when calling a method.
vals->push_back(first_arg);
}
- else
- go_unreachable();
if (ce->args() != NULL)
{
Call_expression* ce = this->call_->call_expression();
Expression* fn = ce->fn();
+ if (!this->is_constant_function())
+ {
+ // The function to call.
+ fields->push_back(Struct_field(Typed_identifier("fn", fntype,
+ location)));
+ }
+
+ // If this thunk statement calls a method on an interface, we pass
+ // the interface object to the thunk.
Interface_field_reference_expression* interface_method =
fn->interface_field_reference_expression();
if (interface_method != NULL)
{
- // If this thunk statement calls a method on an interface, we
- // pass the interface object to the thunk.
- Typed_identifier tid(Thunk_statement::thunk_field_fn,
- interface_method->expr()->type(),
+ Typed_identifier tid("object", interface_method->expr()->type(),
location);
fields->push_back(Struct_field(tid));
}
- else if (!fntype->is_builtin())
- {
- // The function to call.
- Typed_identifier tid(Go_statement::thunk_field_fn, fntype, location);
- fields->push_back(Struct_field(tid));
- }
- else if (ce->is_recover_call())
- {
- // The predeclared recover function has no argument. However,
- // we add an argument when building recover thunks. Handle that
- // here.
- fields->push_back(Struct_field(Typed_identifier("can_recover",
- Type::lookup_bool_type(),
- location)));
- }
+ // If this is a method call, pass down the expression we are
+ // calling.
if (fn->bound_method_expression() != NULL)
{
go_assert(fntype->is_method());
// We always pass the receiver as a pointer.
if (rtype->points_to() == NULL)
rtype = Type::make_pointer_type(rtype);
- Typed_identifier tid(Thunk_statement::thunk_field_receiver, rtype,
- location);
+ Typed_identifier tid("receiver", rtype, 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())
+ {
+ fields->push_back(Struct_field(Typed_identifier("can_recover",
+ Type::lookup_bool_type(),
+ location)));
+ }
+
const Expression_list* args = ce->args();
if (args != NULL)
{
// artificial, function.
void
-Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name,
- Function_type* fntype)
+Thunk_statement::build_thunk(Gogo* gogo, const std::string& thunk_name)
{
source_location location = this->location();
Expression* func_to_call;
unsigned int next_index;
- if (!fntype->is_builtin())
+ if (this->is_constant_function())
{
- func_to_call = Expression::make_field_reference(thunk_parameter,
- 0, location);
- next_index = 1;
+ func_to_call = ce->fn();
+ next_index = 0;
}
else
{
- go_assert(bound_method == NULL && interface_method == NULL);
- func_to_call = ce->fn();
- next_index = 0;
+ func_to_call = Expression::make_field_reference(thunk_parameter,
+ 0, location);
+ next_index = 1;
}
if (bound_method != NULL)
{
- Expression* r = Expression::make_field_reference(thunk_parameter, 1,
+ go_assert(next_index == 0);
+ Expression* r = Expression::make_field_reference(thunk_parameter, 0,
location);
- // The main program passes in a function pointer from the
- // interface expression, so here we can make a bound method in
- // all cases.
- func_to_call = Expression::make_bound_method(r, func_to_call,
+ func_to_call = Expression::make_bound_method(r, bound_method->method(),
location);
- next_index = 2;
+ next_index = 1;
}
else if (interface_method != NULL)
{
// The main program passes the interface object.
+ go_assert(next_index == 0);
+ Expression* r = Expression::make_field_reference(thunk_parameter, 0,
+ location);
const std::string& name(interface_method->name());
- func_to_call = Expression::make_interface_field_reference(func_to_call,
- name,
+ func_to_call = Expression::make_interface_field_reference(r, name,
location);
+ next_index = 1;
}
Expression_list* call_params = new Expression_list();
bool
is_simple(Function_type*) const;
+ // Return whether the thunk function is a constant.
+ bool
+ is_constant_function() const;
+
// Build the struct to use for a complex case.
Struct_type*
build_struct(Function_type* fntype);
// Build the thunk.
void
- build_thunk(Gogo*, const std::string&, Function_type* fntype);
-
- // The field name used in the thunk structure for the function
- // pointer.
- static const char* const thunk_field_fn;
-
- // The field name used in the thunk structure for the receiver, if
- // there is one.
- static const char* const thunk_field_receiver;
+ build_thunk(Gogo*, const std::string&);
// Set the name to use for thunk field N.
void
// the child class.
return this->do_bind_method(expr, location);
}
-
- Expression* func = Expression::make_func_reference(this->stub_, NULL,
- location);
- return Expression::make_bound_method(expr, func, location);
+ return Expression::make_bound_method(expr, this->stub_, location);
}
// Return the named object associated with a method. This may only be
Expression*
Named_method::do_bind_method(Expression* expr, source_location location) const
{
- Expression* func = Expression::make_func_reference(this->named_object_, NULL,
- location);
- Bound_method_expression* bme = Expression::make_bound_method(expr, func,
+ Named_object* no = this->named_object_;
+ Bound_method_expression* bme = Expression::make_bound_method(expr, 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