From patchwork Mon Jun 24 22:11:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Lance Taylor X-Patchwork-Id: 253980 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "localhost", Issuer "www.qmailtoaster.com" (not verified)) by ozlabs.org (Postfix) with ESMTPS id A53582C0098 for ; Tue, 25 Jun 2013 08:11:47 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version:content-type; q=dns; s= default; b=FD8CvpNSpuCCRQH69bGRoiPUZ+yjihZTH0zd6q+jQXNNjFwCNCZ7S 6+UKZ17jCjuwQJHngFS/kiJwPFuPMhm/4cPnSG4FkLfGhKGgVp0qzDZIzjGcRch6 /VyxLskAc7+vW/5fRuo2TyqudI3L2fA4EOXGrl2G7jDLflzwXRhrK8= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version:content-type; s= default; bh=9Qu34FHc4uX888+RhmG2zgiHjKU=; b=oIk2smH3qYCaQG3KgmCM fIwJ7jwTquKj4U+iwsp6XVZiUmqwWfhK9EKdr3YiNFJIe63xOKttEU1hfmPdBJxH owSuDCJRPc973OctDPJxSL8YgaAEzVkqSSuV1U5JVNOTmRSdhC0vfjo5WxxUKEVA nOCQ99OVeMlKJV2orPJkB/Y= Received: (qmail 3693 invoked by alias); 24 Jun 2013 22:11:35 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 3677 invoked by uid 89); 24 Jun 2013 22:11:34 -0000 X-Spam-SWARE-Status: No, score=-3.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, SPF_PASS, TW_DF, TW_FN, T_TVD_MIME_NO_HEADERS autolearn=ham version=3.3.1 Received: from mail-pa0-f45.google.com (HELO mail-pa0-f45.google.com) (209.85.220.45) by sourceware.org (qpsmtpd/0.84/v0.84-167-ge50287c) with ESMTP; Mon, 24 Jun 2013 22:11:31 +0000 Received: by mail-pa0-f45.google.com with SMTP id bi5so11654443pad.32 for ; Mon, 24 Jun 2013 15:11:29 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:subject:date:message-id:user-agent:mime-version :content-type:x-gm-message-state; bh=iO4bx6v51nnlNQEaW+WTkP6PsPCR0Ebz6jayK0/9w20=; b=aMH7NgJsFq0/0rVo2WiSK34lAhAY52qfCVy9QjiBMcDAjz9vTK0Rofr/3uI/jAAXZQ RueR+fnUUX8Euh8ggm/6SeDtnkUDl/kF10IFte8126Fv7xm58OUYetX1acg6Y8CATzdR fPh98OqDEfuW3tTrgOy7y9qtJPJDIHKQPqzBDtjcPkIw+jNRt1OS1kKU1kZa+jiTxamF rSSibbS2y8DkCA0xMCImFsUOuZMe2QVuIblR4x+/deD/yJH0ol1FjAm7UGqi8sJ3YYdf Q69DkKLxElLn9P1z6NjEVwxpLH9Q4T7725ooU9K3kSL0c1wTad8QOrPGCSp67KukB622 GECw== X-Received: by 10.66.118.79 with SMTP id kk15mr29218931pab.193.1372111889480; Mon, 24 Jun 2013 15:11:29 -0700 (PDT) Received: from iant-glaptop.roam.corp.google.com.google.com ([2620:0:1000:3204:5954:552d:6e09:8c6d]) by mx.google.com with ESMTPSA id xj9sm19905429pbc.16.2013.06.24.15.11.28 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 24 Jun 2013 15:11:28 -0700 (PDT) From: Ian Lance Taylor To: gcc-patches@gcc.gnu.org, gofrontend-dev@googlegroups.com Subject: Go patch committed: Implement method values Date: Mon, 24 Jun 2013 15:11:27 -0700 Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.3 (gnu/linux) MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkNpXNxPff1vZeBZntfFgpdvBJsMTu0ZM6RpqZ1CGIeWyYFYG7Gk7cVNmbQPx6eYJVADBJlmCYCia3LpiwfYnudokpX4uBMb13+/fjDhAEAzDEN1uMldyWiBg+Zidl4eTh+O93xrgehNhnpS392svyI3lFUWmIfBDLjPrXj5tcO2U8MnnPu88FKsQm7py5xrQ+nSo+oz1GVOwTXknZ2tqIY/gyJmw== X-Virus-Found: No Go 1.1 introduced a new language concept: method values. If a value v is of some type that implements method M, you can now write v.M and get a function f. Calling f with some arguments is exactly like calling v.M with those same arguments. In other words, v.M keeps a copy of v and uses in later uses of f. This patch adds method values to gccgo. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline and 4.8 branch. Ian diff -r 281fd093661c go/expressions.cc --- a/go/expressions.cc Fri Jun 21 10:53:50 2013 -0700 +++ b/go/expressions.cc Mon Jun 24 15:02:23 2013 -0700 @@ -1090,6 +1090,15 @@ return this->statement_->type(); } +// Determine the type of the expression. + +void +Set_and_use_temporary_expression::do_determine_type( + const Type_context* context) +{ + this->expr_->determine_type(context); +} + // Take the address. void @@ -6626,20 +6635,49 @@ return Expression::traverse(&this->expr_, traverse); } +// Lower the expression. If this is a method value rather than being +// called, and the method is accessed via a pointer, we may need to +// add nil checks. Introduce a temporary variable so that those nil +// checks do not cause multiple evaluation. + +Expression* +Bound_method_expression::do_lower(Gogo*, Named_object*, + Statement_inserter* inserter, int) +{ + // For simplicity we use a temporary for every call to an embedded + // method, even though some of them might be pure value methods and + // not require a temporary. + if (this->expr_->var_expression() == NULL + && this->expr_->temporary_reference_expression() == NULL + && this->expr_->set_and_use_temporary_expression() == NULL + && (this->method_->field_indexes() != NULL + || (this->method_->is_value_method() + && this->expr_->type()->points_to() != NULL))) + { + Temporary_statement* temp = + Statement::make_temporary(this->expr_->type(), NULL, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, + this->location()); + } + return this; +} + // Return the type of a bound method expression. The type of this -// object is really the type of the method with no receiver. We -// should be able to get away with just returning the type of the -// method. +// object is simply the type of the method with no receiver. Type* Bound_method_expression::do_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(); + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); else return Type::make_error_type(); + return fntype->copy_without_receiver(); } // Determine the types of a method expression. @@ -6647,7 +6685,14 @@ void Bound_method_expression::do_determine_type(const Type_context*) { - Function_type* fntype = this->type()->function_type(); + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + fntype = NULL; if (fntype == NULL || !fntype->is_method()) this->expr_->determine_type_no_context(); else @@ -6662,31 +6707,278 @@ void Bound_method_expression::do_check_types(Gogo*) { - if (!this->method_->is_function() - && !this->method_->is_function_declaration()) - this->report_error(_("object is not a method")); - else - { - Type* rtype = this->type()->function_type()->receiver()->type()->deref(); - Type* etype = (this->expr_type_ != NULL - ? this->expr_type_ - : this->expr_->type()); - etype = etype->deref(); - if (!Type::are_identical(rtype, etype, true, NULL)) - this->report_error(_("method type does not match object type")); - } -} - -// Get the tree for a method expression. There is no standard tree -// representation for this. The only places it may currently be used -// are in a Call_expression or a Go_statement, which will take it -// apart directly. So this has nothing to do at present. + Named_object* fn = this->method_->named_object(); + if (!fn->is_function() && !fn->is_function_declaration()) + { + this->report_error(_("object is not a method")); + return; + } + + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + go_unreachable(); + Type* rtype = fntype->receiver()->type()->deref(); + Type* etype = (this->expr_type_ != NULL + ? this->expr_type_ + : this->expr_->type()); + etype = etype->deref(); + if (!Type::are_identical(rtype, etype, true, NULL)) + this->report_error(_("method type does not match object type")); +} + +// If a bound method expression is not simply called, then it is +// represented as a closure. The closure will hold a single variable, +// the receiver to pass to the method. The function will be a simple +// thunk that pulls that value from the closure and calls the method +// with the remaining arguments. +// +// Because method values are not common, we don't build all thunks for +// every methods, but instead only build them as we need them. In +// particular, we even build them on demand for methods defined in +// other packages. + +Bound_method_expression::Method_value_thunks + Bound_method_expression::method_value_thunks; + +// Find or create the thunk for METHOD. + +Named_object* +Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, + Named_object* fn) +{ + std::pair val(fn, NULL); + std::pair ins = + Bound_method_expression::method_value_thunks.insert(val); + if (!ins.second) + { + // We have seen this method before. + go_assert(ins.first->second != NULL); + return ins.first->second; + } + + Location loc = fn->location(); + + Function_type* orig_fntype; + if (fn->is_function()) + orig_fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + orig_fntype = fn->func_declaration_value()->type(); + else + orig_fntype = NULL; + + if (orig_fntype == NULL || !orig_fntype->is_method()) + { + ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name()); + return ins.first->second; + } + + Struct_field_list* sfl = new Struct_field_list(); + // The type here is wrong--it should be new_fntype. But we don't + // have new_fntype yet, and it doesn't really matter. + Type* vt = Type::make_pointer_type(Type::make_void_type()); + sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val.1", + orig_fntype->receiver()->type(), + loc))); + Type* closure_type = Type::make_struct_type(sfl, loc); + closure_type = Type::make_pointer_type(closure_type); + + Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type); + + Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + false, loc); + + gogo->start_block(loc); + + Named_object* cp = gogo->lookup("closure.0", NULL); + go_assert(cp != NULL + && cp->is_variable() + && cp->var_value()->is_parameter()); + + // Field 0 of the closure is the function code pointer, field 1 is + // the value on which to invoke the method. + Expression* arg = Expression::make_var_reference(cp, loc); + arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_field_reference(arg, 1, loc); + + Expression* bme = Expression::make_bound_method(arg, method, fn, loc); + + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + Expression_list* args; + if (orig_params == NULL || orig_params->empty()) + args = NULL; + else + { + const Typed_identifier_list* new_params = new_fntype->parameters(); + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p + 1 != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, loc)); + } + } + + Call_expression* call = Expression::make_call(bme, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); + + Statement* s = Statement::make_return_from_call(call, loc); + gogo->add_statement(s); + Block* b = gogo->finish_block(loc); + gogo->add_block(b, loc); + gogo->lower_block(new_no, b); + gogo->finish_function(loc); + + ins.first->second = new_no; + return new_no; +} + +// 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 +// worry about declaring Field_indexes in expressions.h. + +static Expression* +bme_check_nil(const Method::Field_indexes* field_indexes, Location loc, + Expression** ref) +{ + if (field_indexes == NULL) + return Expression::make_boolean(false, loc); + Expression* cond = bme_check_nil(field_indexes->next, loc, ref); + Struct_type* stype = (*ref)->type()->deref()->struct_type(); + go_assert(stype != NULL + && field_indexes->field_index < stype->field_count()); + if ((*ref)->type()->struct_type() == NULL) + { + go_assert((*ref)->type()->points_to() != NULL); + Expression* n = Expression::make_binary(OPERATOR_EQEQ, *ref, + Expression::make_nil(loc), + loc); + cond = Expression::make_binary(OPERATOR_OROR, cond, n, loc); + *ref = Expression::make_unary(OPERATOR_MULT, *ref, loc); + go_assert((*ref)->type()->struct_type() == stype); + } + *ref = Expression::make_field_reference(*ref, field_indexes->field_index, + loc); + return cond; +} + +// Get the tree for a method value. tree -Bound_method_expression::do_get_tree(Translate_context*) -{ - error_at(this->location(), "reference to method other than calling it"); - return error_mark_node; +Bound_method_expression::do_get_tree(Translate_context* context) +{ + Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(), + this->method_, + this->function_); + if (thunk->is_erroneous()) + { + go_assert(saw_errors()); + return error_mark_node; + } + + // FIXME: We should lower this earlier, but we can't lower it in the + // lowering pass because at that point we don't know whether we need + // to create the thunk or not. If the expression is called, we + // don't need the thunk. + + Location loc = this->location(); + + // If the method expects a value, and we have a pointer, we need to + // dereference the pointer. + + Named_object* fn = this->method_->named_object(); + Function_type* fntype; + if (fn->is_function()) + fntype = fn->func_value()->type(); + else if (fn->is_function_declaration()) + fntype = fn->func_declaration_value()->type(); + else + go_unreachable(); + + Expression* val = this->expr_; + if (fntype->receiver()->type()->points_to() == NULL + && val->type()->points_to() != NULL) + val = Expression::make_unary(OPERATOR_MULT, val, loc); + + // Note that we are ignoring this->expr_type_ here. The thunk will + // expect a closure whose second field has type this->expr_type_ (if + // that is not NULL). We are going to pass it a closure whose + // second field has type this->expr_->type(). Since + // this->expr_type_ is only not-NULL for pointer types, we can get + // away with this. + + Struct_field_list* fields = new Struct_field_list(); + fields->push_back(Struct_field(Typed_identifier("fn.0", + thunk->func_value()->type(), + loc))); + fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc))); + Struct_type* st = Type::make_struct_type(fields, loc); + + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(thunk, loc)); + vals->push_back(val); + + Expression* ret = Expression::make_struct_composite_literal(st, vals, loc); + ret = Expression::make_heap_composite(ret, loc); + + tree ret_tree = ret->get_tree(context); + + Expression* nil_check = NULL; + + // See whether the expression or any embedded pointers are nil. + + Expression* expr = this->expr_; + if (this->method_->field_indexes() != NULL) + { + // Note that we are evaluating this->expr_ twice, but that is OK + // because in the lowering pass we forced it into a temporary + // variable. + Expression* ref = expr; + nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref); + expr = ref; + } + + if (this->method_->is_value_method() && expr->type()->points_to() != NULL) + { + Expression* n = Expression::make_binary(OPERATOR_EQEQ, expr, + Expression::make_nil(loc), + loc); + if (nil_check == NULL) + nil_check = n; + else + nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc); + } + + if (nil_check != NULL) + { + tree nil_check_tree = nil_check->get_tree(context); + tree crash = + context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc); + if (ret_tree == error_mark_node + || nil_check_tree == error_mark_node + || crash == error_mark_node) + return error_mark_node; + + ret_tree = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(ret_tree), + build3_loc(loc.gcc_location(), COND_EXPR, + void_type_node, nil_check_tree, + crash, NULL_TREE), + ret_tree); + } + + return ret_tree; } // Dump ast representation of a bound method expression. @@ -6705,16 +6997,16 @@ ast_dump_context->ostream() << ")"; } - ast_dump_context->ostream() << "." << this->method_->name(); + ast_dump_context->ostream() << "." << this->function_->name(); } // Make a method expression. Bound_method_expression* -Expression::make_bound_method(Expression* expr, Named_object* method, - Location location) -{ - return new Bound_method_expression(expr, method, location); +Expression::make_bound_method(Expression* expr, const Method* method, + Named_object* function, Location location) +{ + return new Bound_method_expression(expr, method, function, location); } // Class Builtin_call_expression. This is used for a call to a @@ -8921,7 +9213,7 @@ Bound_method_expression* bme = this->fn_->bound_method_expression(); if (bme != NULL) { - Named_object* method = bme->method(); + Named_object* methodfn = bme->function(); Expression* first_arg = bme->first_argument(); // We always pass a pointer when calling a method. @@ -8962,7 +9254,7 @@ // old arguments, because we may be traversing them up in some // caller. FIXME. this->args_ = new_args; - this->fn_ = Expression::make_func_reference(method, NULL, + this->fn_ = Expression::make_func_reference(methodfn, NULL, bme->location()); } @@ -11158,6 +11450,28 @@ return Expression::traverse(&this->expr_, traverse); } +// Lower the expression. If this expression is not called, we need to +// evaluate the expression twice when converting to the backend +// interface. So introduce a temporary variable if necessary. + +Expression* +Interface_field_reference_expression::do_lower(Gogo*, Named_object*, + Statement_inserter* inserter, + int) +{ + if (this->expr_->var_expression() == NULL + && this->expr_->temporary_reference_expression() == NULL + && this->expr_->set_and_use_temporary_expression() == NULL) + { + Temporary_statement* temp = + Statement::make_temporary(this->expr_->type(), NULL, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_, + this->location()); + } + return this; +} + // Return the type of an interface field reference. Type* @@ -11218,18 +11532,188 @@ } } -// Get a tree for a reference to a field in an interface. There is no -// standard tree type representation for this: it's a function -// attached to its first argument, like a Bound_method_expression. -// The only places it may currently be used are in a Call_expression -// or a Go_statement, which will take it apart directly. So this has -// nothing to do at present. +// If an interface field reference is not simply called, then it is +// represented as a closure. The closure will hold a single variable, +// the value of the interface on which the method should be called. +// The function will be a simple thunk that pulls the value from the +// closure and calls the method with the remaining arguments. + +// Because method values are not common, we don't build all thunks for +// all possible interface methods, but instead only build them as we +// need them. In particular, we even build them on demand for +// interface methods defined in other packages. + +Interface_field_reference_expression::Interface_method_thunks + Interface_field_reference_expression::interface_method_thunks; + +// Find or create the thunk to call method NAME on TYPE. + +Named_object* +Interface_field_reference_expression::create_thunk(Gogo* gogo, + Interface_type* type, + const std::string& name) +{ + std::pair val(type, NULL); + std::pair ins = + Interface_field_reference_expression::interface_method_thunks.insert(val); + if (ins.second) + { + // This is the first time we have seen this interface. + ins.first->second = new Method_thunks(); + } + + for (Method_thunks::const_iterator p = ins.first->second->begin(); + p != ins.first->second->end(); + p++) + if (p->first == name) + return p->second; + + Location loc = type->location(); + + const Typed_identifier* method_id = type->find_method(name); + if (method_id == NULL) + return Named_object::make_erroneous_name(Gogo::thunk_name()); + + Function_type* orig_fntype = method_id->type()->function_type(); + if (orig_fntype == NULL) + return Named_object::make_erroneous_name(Gogo::thunk_name()); + + Struct_field_list* sfl = new Struct_field_list(); + // The type here is wrong--it should be new_fntype. But we don't + // have new_fntype yet, and it doesn't really matter. + Type* vt = Type::make_pointer_type(Type::make_void_type()); + sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc))); + Type* closure_type = Type::make_struct_type(sfl, loc); + closure_type = Type::make_pointer_type(closure_type); + + Function_type* new_fntype = orig_fntype->copy_with_closure(closure_type); + + Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + false, loc); + + gogo->start_block(loc); + + Named_object* cp = gogo->lookup("closure.0", NULL); + go_assert(cp != NULL + && cp->is_variable() + && cp->var_value()->is_parameter()); + + // Field 0 of the closure is the function code pointer, field 1 is + // the value on which to invoke the method. + Expression* arg = Expression::make_var_reference(cp, loc); + arg = Expression::make_unary(OPERATOR_MULT, arg, loc); + arg = Expression::make_field_reference(arg, 1, loc); + + Expression *ifre = Expression::make_interface_field_reference(arg, name, + loc); + + const Typed_identifier_list* orig_params = orig_fntype->parameters(); + Expression_list* args; + if (orig_params == NULL || orig_params->empty()) + args = NULL; + else + { + const Typed_identifier_list* new_params = new_fntype->parameters(); + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p + 1 != new_params->end(); + ++p) + { + Named_object* p_no = gogo->lookup(p->name(), NULL); + go_assert(p_no != NULL + && p_no->is_variable() + && p_no->var_value()->is_parameter()); + args->push_back(Expression::make_var_reference(p_no, loc)); + } + } + + Call_expression* call = Expression::make_call(ifre, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); + + Statement* s = Statement::make_return_from_call(call, loc); + gogo->add_statement(s); + Block* b = gogo->finish_block(loc); + gogo->add_block(b, loc); + gogo->lower_block(new_no, b); + gogo->finish_function(loc); + + ins.first->second->push_back(std::make_pair(name, new_no)); + return new_no; +} + +// Get a tree for a method value. tree -Interface_field_reference_expression::do_get_tree(Translate_context*) -{ - error_at(this->location(), "reference to method other than calling it"); - return error_mark_node; +Interface_field_reference_expression::do_get_tree(Translate_context* context) +{ + Interface_type* type = this->expr_->type()->interface_type(); + if (type == NULL) + { + go_assert(saw_errors()); + return error_mark_node; + } + + Named_object* thunk = + Interface_field_reference_expression::create_thunk(context->gogo(), + type, this->name_); + if (thunk->is_erroneous()) + { + go_assert(saw_errors()); + return error_mark_node; + } + + // FIXME: We should lower this earlier, but we can't it lower it in + // the lowering pass because at that point we don't know whether we + // need to create the thunk or not. If the expression is called, we + // don't need the thunk. + + Location loc = this->location(); + + Struct_field_list* fields = new Struct_field_list(); + fields->push_back(Struct_field(Typed_identifier("fn.0", + thunk->func_value()->type(), + loc))); + fields->push_back(Struct_field(Typed_identifier("val.1", + this->expr_->type(), + loc))); + Struct_type* st = Type::make_struct_type(fields, loc); + + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(thunk, loc)); + vals->push_back(this->expr_); + + Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); + expr = Expression::make_heap_composite(expr, loc); + + tree closure_tree = expr->get_tree(context); + + // Note that we are evaluating this->expr_ twice, but that is OK + // because in the lowering pass we forced it into a temporary + // variable. + tree expr_tree = this->expr_->get_tree(context); + tree nil_check_tree = Expression::comparison_tree(context, + Type::lookup_bool_type(), + OPERATOR_EQEQ, + this->expr_->type(), + expr_tree, + Type::make_nil_type(), + null_pointer_node, + loc); + tree crash = context->gogo()->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, + loc); + if (closure_tree == error_mark_node + || nil_check_tree == error_mark_node + || crash == error_mark_node) + return error_mark_node; + return fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR, + TREE_TYPE(closure_tree), + build3_loc(loc.gcc_location(), COND_EXPR, + void_type_node, nil_check_tree, crash, + NULL_TREE), + closure_tree); } // Dump ast representation for an interface field reference. @@ -11485,22 +11969,7 @@ method_type->is_varargs(), location); - size_t count = call->result_count(); - Statement* s; - if (count == 0) - s = Statement::make_statement(call, true); - else - { - Expression_list* retvals = new Expression_list(); - if (count <= 1) - retvals->push_back(call); - else - { - for (size_t i = 0; i < count; ++i) - retvals->push_back(Expression::make_call_result(call, i)); - } - s = Statement::make_return_statement(retvals, location); - } + Statement* s = Statement::make_return_from_call(call, location); gogo->add_statement(s); Block* b = gogo->finish_block(location); diff -r 281fd093661c go/expressions.h --- a/go/expressions.h Fri Jun 21 10:53:50 2013 -0700 +++ b/go/expressions.h Mon Jun 24 15:02:23 2013 -0700 @@ -16,6 +16,7 @@ class Traverse; class Statement_inserter; class Type; +class Method; struct Type_context; class Integer_type; class Float_type; @@ -224,9 +225,11 @@ make_call_result(Call_expression*, unsigned int index); // Make an expression which is a method bound to its first - // parameter. + // parameter. METHOD is the method being called, FUNCTION is the + // function to call. static Bound_method_expression* - make_bound_method(Expression* object, Named_object* method, Location); + make_bound_method(Expression* object, const Method* method, + Named_object* function, Location); // Make an index or slice expression. This is a parser expression // which represents LEFT[START:END]. END may be NULL, meaning an @@ -1079,8 +1082,7 @@ do_type(); void - do_determine_type(const Type_context*) - { } + do_determine_type(const Type_context*); Expression* do_copy() @@ -1852,10 +1854,10 @@ class Bound_method_expression : public Expression { public: - Bound_method_expression(Expression* expr, Named_object* method, - Location location) + Bound_method_expression(Expression* expr, const Method *method, + Named_object* function, Location location) : Expression(EXPRESSION_BOUND_METHOD, location), - expr_(expr), expr_type_(NULL), method_(method) + expr_(expr), expr_type_(NULL), method_(method), function_(function) { } // Return the object which is the first argument. @@ -1870,20 +1872,33 @@ first_argument_type() const { return this->expr_type_; } - // Return the method function. + // Return the method. + const Method* + method() const + { return this->method_; } + + // Return the function to call. Named_object* - method() - { return this->method_; } + function() const + { return this->function_; } // Set the implicit type of the expression. void set_first_argument_type(Type* type) { this->expr_type_ = type; } + // Create a thunk to call FUNCTION, for METHOD, when it is used as + // part of a method value. + static Named_object* + create_thunk(Gogo*, const Method* method, Named_object* function); + protected: int do_traverse(Traverse*); + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Type* do_type(); @@ -1897,7 +1912,7 @@ do_copy() { return new Bound_method_expression(this->expr_->copy(), this->method_, - this->location()); + this->function_, this->location()); } tree @@ -1907,6 +1922,11 @@ do_dump_expression(Ast_dump_context*) const; private: + // A mapping from method functions to the thunks we have created for + // them. + typedef Unordered_map(Named_object*, Named_object*) Method_value_thunks; + static Method_value_thunks method_value_thunks; + // The object used to find the method. This is passed to the method // as the first argument. Expression* expr_; @@ -1914,8 +1934,12 @@ // 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. - Named_object* method_; + // The method. + const Method* method_; + // The function to call. This is not the same as + // method_->named_object() when the method has a stub. This will be + // the real function rather than the stub. + Named_object* function_; }; // A reference to a field in a struct. @@ -2031,6 +2055,11 @@ name() const { return this->name_; } + // Create a thunk to call the method NAME in TYPE when it is used as + // part of a method value. + static Named_object* + create_thunk(Gogo*, Interface_type* type, const std::string& name); + // Return a tree for the pointer to the function to call, given a // tree for the expression. tree @@ -2046,6 +2075,9 @@ int do_traverse(Traverse* traverse); + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + Type* do_type(); @@ -2070,6 +2102,13 @@ do_dump_expression(Ast_dump_context*) const; private: + // A mapping from interface types to a list of thunks we have + // created for methods. + typedef std::vector > Method_thunks; + typedef Unordered_map(Interface_type*, Method_thunks*) + Interface_method_thunks; + static Interface_method_thunks interface_method_thunks; + // The expression for the interface object. This should have a type // of interface or pointer to interface. Expression* expr_; diff -r 281fd093661c go/gogo.cc --- a/go/gogo.cc Fri Jun 21 10:53:50 2013 -0700 +++ b/go/gogo.cc Mon Jun 24 15:02:23 2013 -0700 @@ -1795,11 +1795,36 @@ return TRAVERSE_CONTINUE; } + Bound_method_expression* bme = expr->bound_method_expression(); + if (bme != NULL) + { + // We would not get here for a call to this method, so this is a + // method value. We need to create a thunk. + Bound_method_expression::create_thunk(this->gogo_, bme->method(), + bme->function()); + return TRAVERSE_CONTINUE; + } + + Interface_field_reference_expression* ifre = + expr->interface_field_reference_expression(); + if (ifre != NULL) + { + // We would not get here for a call to this interface method, so + // this is a method value. We need to create a thunk. + Interface_type* type = ifre->expr()->type()->interface_type(); + if (type != NULL) + Interface_field_reference_expression::create_thunk(this->gogo_, type, + ifre->name()); + return TRAVERSE_CONTINUE; + } + Call_expression* ce = expr->call_expression(); if (ce != NULL) { Expression* fn = ce->fn(); - if (fn->func_expression() != NULL) + if (fn->func_expression() != NULL + || fn->bound_method_expression() != NULL + || fn->interface_field_reference_expression() != NULL) { // Traverse the arguments but not the function. Expression_list* args = ce->args(); @@ -2806,22 +2831,7 @@ // Any varargs call has already been lowered. call->set_varargs_are_lowered(); - Statement* s; - if (orig_fntype->results() == NULL || orig_fntype->results()->empty()) - s = Statement::make_statement(call, true); - else - { - Expression_list* vals = new Expression_list(); - size_t rc = orig_fntype->results()->size(); - if (rc == 1) - vals->push_back(call); - else - { - for (size_t i = 0; i < rc; ++i) - vals->push_back(Expression::make_call_result(call, i)); - } - s = Statement::make_return_statement(vals, location); - } + Statement* s = Statement::make_return_from_call(call, location); s->determine_types(); gogo->add_statement(s); @@ -3557,42 +3567,8 @@ { Location loc = no->location(); - Typed_identifier_list* new_params = new Typed_identifier_list(); - const Typed_identifier_list* orig_params = orig_fntype->parameters(); - if (orig_params != NULL && !orig_params->empty()) - { - static int count; - char buf[50]; - for (Typed_identifier_list::const_iterator p = orig_params->begin(); - p != orig_params->end(); - ++p) - { - snprintf(buf, sizeof buf, "pt.%u", count); - ++count; - new_params->push_back(Typed_identifier(buf, p->type(), - p->location())); - } - } Type* vt = Type::make_pointer_type(Type::make_void_type()); - new_params->push_back(Typed_identifier("closure.0", vt, loc)); - - const Typed_identifier_list* orig_results = orig_fntype->results(); - Typed_identifier_list* new_results; - if (orig_results == NULL || orig_results->empty()) - new_results = NULL; - else - { - new_results = new Typed_identifier_list(); - for (Typed_identifier_list::const_iterator p = orig_results->begin(); - p != orig_results->end(); - ++p) - new_results->push_back(Typed_identifier("", p->type(), - p->location())); - } - - Function_type* new_fntype = Type::make_function_type(NULL, new_params, - new_results, - loc); + Function_type* new_fntype = orig_fntype->copy_with_closure(vt); std::string name = no->name() + "$descriptorfn"; Named_object* dno = gogo->start_function(name, new_fntype, false, loc); @@ -3602,13 +3578,16 @@ Expression* fn = Expression::make_func_reference(no, NULL, loc); - // Call the wrapper function, passing all of the arguments except - // for the last one (the last argument is the ignored closure). + // Call the function begin wrapped, passing all of the arguments + // except for the last one (the last argument is the ignored + // closure). + const Typed_identifier_list* orig_params = orig_fntype->parameters(); Expression_list* args; if (orig_params == NULL || orig_params->empty()) args = NULL; else { + const Typed_identifier_list* new_params = new_fntype->parameters(); args = new Expression_list(); for (Typed_identifier_list::const_iterator p = new_params->begin(); p + 1 != new_params->end(); @@ -3627,23 +3606,7 @@ loc); call->set_varargs_are_lowered(); - Statement* s; - if (orig_results == NULL || orig_results->empty()) - s = Statement::make_statement(call, true); - else - { - Expression_list* vals = new Expression_list(); - size_t rc = orig_results->size(); - if (rc == 1) - vals->push_back(call); - else - { - for (size_t i = 0; i < rc; ++i) - vals->push_back(Expression::make_call_result(call, i)); - } - s = Statement::make_return_statement(vals, loc); - } - + Statement* s = Statement::make_return_from_call(call, loc); gogo->add_statement(s); Block* b = gogo->finish_block(loc); gogo->add_block(b, loc); diff -r 281fd093661c go/statements.cc --- a/go/statements.cc Fri Jun 21 10:53:50 2013 -0700 +++ b/go/statements.cc Mon Jun 24 15:02:23 2013 -0700 @@ -2815,6 +2815,28 @@ return new Return_statement(vals, location); } +// Make a statement that returns the result of a call expression. + +Statement* +Statement::make_return_from_call(Call_expression* call, Location location) +{ + size_t rc = call->result_count(); + if (rc == 0) + return Statement::make_statement(call, true); + else + { + Expression_list* vals = new Expression_list(); + if (rc == 1) + vals->push_back(call); + else + { + for (size_t i = 0; i < rc; ++i) + vals->push_back(Expression::make_call_result(call, i)); + } + return Statement::make_return_statement(vals, location); + } +} + // A break or continue statement. class Bc_statement : public Statement diff -r 281fd093661c go/statements.h --- a/go/statements.h Fri Jun 21 10:53:50 2013 -0700 +++ b/go/statements.h Mon Jun 24 15:02:23 2013 -0700 @@ -207,6 +207,13 @@ static Return_statement* make_return_statement(Expression_list*, Location); + // Make a statement that returns the result of a call expression. + // If the call does not return any results, this just returns the + // call expression as a statement, assuming that the function will + // end immediately afterward. + static Statement* + make_return_from_call(Call_expression*, Location); + // Make a break statement. static Statement* make_break_statement(Unnamed_label* label, Location); diff -r 281fd093661c go/types.cc --- a/go/types.cc Fri Jun 21 10:53:50 2013 -0700 +++ b/go/types.cc Mon Jun 24 15:02:23 2013 -0700 @@ -3396,7 +3396,8 @@ // passed when invoking the function indirectly, via the struct. Location loc = this->location(); - Btype* struct_type = gogo->backend()->placeholder_struct_type("", loc); + Btype* struct_type = + gogo->backend()->placeholder_struct_type("__go_descriptor", loc); Btype* ptr_struct_type = gogo->backend()->pointer_type(struct_type); Backend::Btyped_identifier breceiver; @@ -3835,6 +3836,49 @@ return ret; } +// Make a copy of a function type ignoring any receiver and adding a +// closure parameter. + +Function_type* +Function_type::copy_with_closure(Type* closure_type) const +{ + Typed_identifier_list* new_params = new Typed_identifier_list(); + const Typed_identifier_list* orig_params = this->parameters_; + if (orig_params != NULL && !orig_params->empty()) + { + static int count; + char buf[50]; + for (Typed_identifier_list::const_iterator p = orig_params->begin(); + p != orig_params->end(); + ++p) + { + snprintf(buf, sizeof buf, "pt.%u", count); + ++count; + new_params->push_back(Typed_identifier(buf, p->type(), + p->location())); + } + } + new_params->push_back(Typed_identifier("closure.0", closure_type, + this->location_)); + + const Typed_identifier_list* orig_results = this->results_; + Typed_identifier_list* new_results; + if (orig_results == NULL || orig_results->empty()) + new_results = NULL; + else + { + new_results = new Typed_identifier_list(); + for (Typed_identifier_list::const_iterator p = orig_results->begin(); + p != orig_results->end(); + ++p) + new_results->push_back(Typed_identifier("", p->type(), + p->location())); + } + + return Type::make_function_type(NULL, new_params, new_results, + this->location()); +} + // Make a function type. Function_type* @@ -7580,7 +7624,7 @@ // the child class. return this->do_bind_method(expr, location); } - return Expression::make_bound_method(expr, this->stub_, location); + return Expression::make_bound_method(expr, this, this->stub_, location); } // Return the named object associated with a method. This may only be @@ -7623,8 +7667,8 @@ Named_method::do_bind_method(Expression* expr, Location location) const { Named_object* no = this->named_object_; - Bound_method_expression* bme = Expression::make_bound_method(expr, no, - location); + Bound_method_expression* bme = Expression::make_bound_method(expr, this, + 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 // first argument. @@ -9002,28 +9046,16 @@ Call_expression* call = Expression::make_call(func, arguments, is_varargs, location); call->set_hidden_fields_are_ok(); - size_t count = call->result_count(); - if (count == 0) - gogo->add_statement(Statement::make_statement(call, true)); - else - { - Expression_list* retvals = new Expression_list(); - if (count <= 1) - retvals->push_back(call); - else - { - for (size_t i = 0; i < count; ++i) - retvals->push_back(Expression::make_call_result(call, i)); - } - Return_statement* retstat = Statement::make_return_statement(retvals, - location); - + + Statement* s = Statement::make_return_from_call(call, location); + Return_statement* retstat = s->return_statement(); + if (retstat != NULL) + { // We can return values with hidden fields from a stub. This is // necessary if the method is itself hidden. retstat->set_hidden_fields_are_ok(); - - gogo->add_statement(retstat); - } + } + gogo->add_statement(s); } // Apply FIELD_INDEXES to EXPR. The field indexes have to be applied diff -r 281fd093661c go/types.h --- a/go/types.h Fri Jun 21 10:53:50 2013 -0700 +++ b/go/types.h Mon Jun 24 15:02:23 2013 -0700 @@ -1789,6 +1789,12 @@ Function_type* copy_with_receiver(Type*) const; + // Return a copy of this type ignoring any receiver and adding a + // final closure parameter of type CLOSURE_TYPE. This is used when + // creating descriptors. + Function_type* + copy_with_closure(Type* closure_type) const; + static Type* make_function_type_descriptor_type();