From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2116) id 88F9B385B537; Mon, 23 Oct 2023 21:16:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 88F9B385B537 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1698095811; bh=+OtiQCIp/PPAvZTW7XkZuYwDDGySz/DwPd/pt8hkPpI=; h=From:To:Subject:Date:From; b=T0iw4LsjS9+hshnOn2lne1b7k7a1da94ndlgP0hyfKsSF1mplXdtWW/3+Ge5oKZiK 9J6EhsmuDWythSMcSUz4k4fhv50EnA8SOIWqJcVtXYMBTUZJXNcPR6N8u1I/AerZhN 32cTG8aUviQm55tEhUatfbPhQoeYni/jT/GITP1c= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Ian Lance Taylor To: gcc-cvs@gcc.gnu.org Subject: [gcc r14-4873] compiler: move Selector_expression up in file X-Act-Checkin: gcc X-Git-Author: Ian Lance Taylor X-Git-Refname: refs/heads/master X-Git-Oldrev: 597dba85b3e66a0836dd7442edcc2fda7e0703fc X-Git-Newrev: 02aa322c8cfd3f60fa5a3a0eee4340bb644261fe Message-Id: <20231023211651.88F9B385B537@sourceware.org> Date: Mon, 23 Oct 2023 21:16:51 +0000 (GMT) List-Id: https://gcc.gnu.org/g:02aa322c8cfd3f60fa5a3a0eee4340bb644261fe commit r14-4873-g02aa322c8cfd3f60fa5a3a0eee4340bb644261fe Author: Ian Lance Taylor Date: Thu Oct 19 18:53:22 2023 -0700 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. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/536642 Diff: --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 5448 +++++++++++++++++++------------------- 2 files changed, 2725 insertions(+), 2725 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 35b9cd780da0..aff74bd74dc2 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -3c2a441ef6cafb018bb3cc16f8403ae3d1daf2e1 +e997b0201512110e9c20b1fdfd40014830031047 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 f218731041bb..c9177b711748 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -8426,445 +8426,726 @@ Expression::make_bound_method(Expression* expr, const Method* method, return new Bound_method_expression(expr, method, function, location); } -// Class Builtin_call_expression. This is used for a call to a -// builtin function. +// A general selector. This is a Parser_expression for LEFT.NAME. It +// is lowered after we know the type of the left hand side. -Builtin_call_expression::Builtin_call_expression(Gogo* gogo, - Expression* fn, - Expression_list* args, - bool is_varargs, - Location location) - : Call_expression(fn, args, is_varargs, location), - gogo_(gogo), code_(BUILTIN_INVALID), seen_(false), - recover_arg_is_set_(false) +class Selector_expression : public Parser_expression { - Func_expression* fnexp = this->fn()->func_expression(); - if (fnexp == NULL) - { - this->code_ = BUILTIN_INVALID; - return; - } - const std::string& name(fnexp->named_object()->name()); - if (name == "append") - this->code_ = BUILTIN_APPEND; - else if (name == "cap") - this->code_ = BUILTIN_CAP; - else if (name == "close") - this->code_ = BUILTIN_CLOSE; - else if (name == "complex") - this->code_ = BUILTIN_COMPLEX; - else if (name == "copy") - this->code_ = BUILTIN_COPY; - else if (name == "delete") - this->code_ = BUILTIN_DELETE; - else if (name == "imag") - this->code_ = BUILTIN_IMAG; - else if (name == "len") - this->code_ = BUILTIN_LEN; - else if (name == "make") - this->code_ = BUILTIN_MAKE; - else if (name == "new") - this->code_ = BUILTIN_NEW; - else if (name == "panic") - this->code_ = BUILTIN_PANIC; - else if (name == "print") - this->code_ = BUILTIN_PRINT; - else if (name == "println") - this->code_ = BUILTIN_PRINTLN; - else if (name == "real") - this->code_ = BUILTIN_REAL; - else if (name == "recover") - this->code_ = BUILTIN_RECOVER; - else if (name == "Add") - this->code_ = BUILTIN_ADD; - else if (name == "Alignof") - this->code_ = BUILTIN_ALIGNOF; - else if (name == "Offsetof") - this->code_ = BUILTIN_OFFSETOF; - else if (name == "Sizeof") - this->code_ = BUILTIN_SIZEOF; - else if (name == "Slice") - this->code_ = BUILTIN_SLICE; - else - go_unreachable(); -} + public: + Selector_expression(Expression* left, const std::string& name, + Location location) + : Parser_expression(EXPRESSION_SELECTOR, location), + left_(left), name_(name) + { } -// Return whether this is a call to recover. This is a virtual -// function called from the parent class. + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->left_, traverse); } -bool -Builtin_call_expression::do_is_recover_call() const -{ - if (this->classification() == EXPRESSION_ERROR) - return false; - return this->code_ == BUILTIN_RECOVER; -} + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); -// Set the argument for a call to recover. + Expression* + do_copy() + { + return new Selector_expression(this->left_->copy(), this->name_, + this->location()); + } -void -Builtin_call_expression::do_set_recover_arg(Expression* arg) + 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) { - const Expression_list* args = this->args(); - go_assert(args == NULL || args->empty()); - Expression_list* new_args = new Expression_list(); - new_args->push_back(arg); - this->set_args(new_args); - this->recover_arg_is_set_ = true; + 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 builtin call expression. This turns new and make into -// specific expressions. We also convert to a constant if we can. +// Lower a method expression T.M or (*T).M. We turn this into a +// function literal. Expression* -Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, - Statement_inserter* inserter, int) +Selector_expression::lower_method_expression(Gogo* gogo) { - if (this->is_error_expression()) - return this; - - Location loc = this->location(); + Location location = this->location(); + Type* left_type = this->left_->type(); + Type* type = left_type; + const std::string& name(this->name_); - if (this->is_varargs() && this->code_ != BUILTIN_APPEND) + bool is_pointer; + if (type->points_to() == NULL) + is_pointer = false; + else { - this->report_error(_("invalid use of %<...%> with builtin function")); - return Expression::make_error(loc); + is_pointer = true; + type = type->points_to(); } - if (this->code_ == BUILTIN_OFFSETOF) + 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) { - Expression* arg = this->one_arg(); + Interface_type* it = type->interface_type(); + if (it != NULL) + imethod = it->find_method(name); + } - if (arg->bound_method_expression() != NULL - || arg->interface_field_reference_expression() != NULL) + if ((method == NULL && imethod == NULL) + || (left_type->named_type() != NULL && left_type->points_to() != NULL)) + { + if (nt != NULL) { - this->report_error(_("invalid use of method value as argument " - "of Offsetof")); - return this; + 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()); } - - Field_reference_expression* farg = arg->field_reference_expression(); - while (farg != NULL) + else { - if (!farg->implicit()) - break; - // When the selector refers to an embedded field, - // it must not be reached through pointer indirections. - if (farg->expr()->deref() != farg->expr()) - { - this->report_error(_("argument of Offsetof implies " - "indirection of an embedded field")); - return this; - } - // Go up until we reach the original base. - farg = farg->expr()->field_reference_expression(); + 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 (this->is_constant()) + if (method != NULL && !is_pointer && !method->is_value_method()) { - Numeric_constant nc; - if (this->numeric_constant_value(&nc)) - return nc.expression(loc); + 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); } - switch (this->code_) + // Build a new function type in which the receiver becomes the first + // argument. + Function_type* method_type; + if (method != NULL) { - default: - break; - - case BUILTIN_NEW: - { - const Expression_list* args = this->args(); - if (args == NULL || args->size() < 1) - this->report_error(_("not enough arguments")); - else if (args->size() > 1) - this->report_error(_("too many arguments")); - else - { - Expression* arg = args->front(); - if (!arg->is_type_expression()) - { - go_error_at(arg->location(), "expected type"); - this->set_is_error(); - } - else - return Expression::make_allocation(arg->type(), loc); - } - } - break; + 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()); + } - case BUILTIN_MAKE: - return this->lower_make(gogo, inserter); + 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)); - case BUILTIN_RECOVER: - if (function != NULL) - function->func_value()->set_calls_recover(); - else - { - // Calling recover outside of a function always returns the - // nil empty interface. - Type* eface = Type::make_empty_interface_type(loc); - return Expression::make_cast(eface, Expression::make_nil(loc), loc); - } - break; - - case BUILTIN_DELETE: - { - const Expression_list* args = this->args(); - if (args == NULL || args->size() < 2) - this->report_error(_("not enough arguments")); - else if (args->size() > 2) - this->report_error(_("too many arguments")); - else if (args->front()->type()->map_type() == NULL) - this->report_error(_("argument 1 must be a map")); - else - { - Type* key_type = - args->front()->type()->map_type()->key_type(); - Expression_list::iterator pa = this->args()->begin(); - pa++; - Type* arg_type = (*pa)->type(); - std::string reason; - if (!Type::are_assignable(key_type, arg_type, &reason)) - { - if (reason.empty()) - go_error_at(loc, "argument 2 has incompatible type"); - else - go_error_at(loc, "argument 2 has incompatible type (%s)", - reason.c_str()); - this->set_is_error(); - } - else if (!Type::are_identical(key_type, arg_type, 0, NULL)) - *pa = Expression::make_cast(key_type, *pa, loc); - } - } - break; - - case BUILTIN_PRINT: - case BUILTIN_PRINTLN: - // Force all the arguments into temporary variables, so that we - // don't try to evaluate something while holding the print lock. - if (this->args() == NULL) - break; - for (Expression_list::iterator pa = this->args()->begin(); - pa != this->args()->end(); - ++pa) + 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 (!(*pa)->is_multi_eval_safe()) + if (!p->name().empty() && !Gogo::is_sink_name(p->name())) + parameters->push_back(*p); + else { - Temporary_statement* temp = - Statement::make_temporary(NULL, *pa, loc); - inserter->insert(temp); - *pa = Expression::make_temporary_reference(temp, loc); + char buf[20]; + snprintf(buf, sizeof buf, "$param%d", i); + parameters->push_back(Typed_identifier(buf, p->type(), + p->location())); } } - break; } - return this; -} + 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); + } -// Flatten a builtin call expression. This turns the arguments of some -// builtin calls into temporary expressions. Also expand copy and append -// to runtime calls. + Function_type* fntype = Type::make_function_type(NULL, parameters, results, + location); + if (method_type->is_varargs()) + fntype->set_is_varargs(); -Expression* -Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, - Statement_inserter* inserter) -{ - if (this->is_error_expression()) + // 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) { - go_assert(saw_errors()); - return this; + 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(f); + tce->set_may_convert_function_types(); + return f; } - Location loc = this->location(); + Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false, + location); - switch (this->code_) + 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()) { - default: - break; + gogo->finish_function(location); + return bm; + } - case BUILTIN_APPEND: - return this->flatten_append(gogo, function, inserter, NULL, NULL); + 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)); + } + } - case BUILTIN_COPY: - { - Type* at = this->args()->front()->type(); - for (Expression_list::iterator pa = this->args()->begin(); - pa != this->args()->end(); - ++pa) - { - if ((*pa)->is_error_expression()) - { - go_assert(saw_errors()); - return Expression::make_error(loc); - } - if ((*pa)->is_nil_expression()) - { - Expression* nil = Expression::make_nil(loc); - Expression* zero = Expression::make_integer_ul(0, NULL, loc); - *pa = Expression::make_slice_value(at, nil, zero, zero, loc); - } - if (!(*pa)->is_multi_eval_safe()) - { - Temporary_statement* temp = - Statement::make_temporary(NULL, *pa, loc); - inserter->insert(temp); - *pa = Expression::make_temporary_reference(temp, loc); - } - } + gogo->start_block(location); - // Lower to runtime call. - const Expression_list* args = this->args(); - go_assert(args != NULL && args->size() == 2); - Expression* arg1 = args->front(); - Expression* arg2 = args->back(); - go_assert(arg1->is_multi_eval_safe()); - go_assert(arg2->is_multi_eval_safe()); - bool arg2_is_string = arg2->type()->is_string_type(); + Call_expression* call = Expression::make_call(bm, args, + method_type->is_varargs(), + location); - Expression* ret; - Type* et = at->array_type()->element_type(); - if (et->has_pointer()) - { - Expression* td = Expression::make_type_descriptor(et, loc); - Expression* pd = - Expression::make_slice_info(arg1, SLICE_INFO_VALUE_POINTER, loc); - Expression* ld = - Expression::make_slice_info(arg1, SLICE_INFO_LENGTH, loc); - Expression* ps = - Expression::make_slice_info(arg2, SLICE_INFO_VALUE_POINTER, loc); - Expression* ls = - Expression::make_slice_info(arg2, SLICE_INFO_LENGTH, loc); - ret = Runtime::make_call(gogo, Runtime::TYPEDSLICECOPY, loc, - 5, td, pd, ld, ps, ls); - } - else - { - Type* int_type = Type::lookup_integer_type("int"); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Statement* s = Statement::make_return_from_call(call, location); + gogo->add_statement(s); - // l1 = len(arg1) - Named_object* lenfn = gogo->lookup_global("len"); - Expression* lenref = Expression::make_func_reference(lenfn, NULL, loc); - Expression_list* len_args = new Expression_list(); - len_args->push_back(arg1->copy()); - Expression* len1 = Expression::make_call(lenref, len_args, false, loc); - gogo->lower_expression(function, inserter, &len1); - gogo->flatten_expression(function, inserter, &len1); - Temporary_statement* l1tmp = Statement::make_temporary(int_type, len1, loc); - inserter->insert(l1tmp); + Block* b = gogo->finish_block(location); - // l2 = len(arg2) - len_args = new Expression_list(); - len_args->push_back(arg2->copy()); - Expression* len2 = Expression::make_call(lenref, len_args, false, loc); - gogo->lower_expression(function, inserter, &len2); - gogo->flatten_expression(function, inserter, &len2); - Temporary_statement* l2tmp = Statement::make_temporary(int_type, len2, loc); - inserter->insert(l2tmp); + gogo->add_block(b, location); - // n = (l1 < l2 ? l1 : l2) - Expression* l1ref = Expression::make_temporary_reference(l1tmp, loc); - Expression* l2ref = Expression::make_temporary_reference(l2tmp, loc); - Expression* cond = Expression::make_binary(OPERATOR_LT, l1ref, l2ref, loc); - Expression* n = Expression::make_conditional(cond, - l1ref->copy(), - l2ref->copy(), - loc); - Temporary_statement* ntmp = Statement::make_temporary(NULL, n, loc); - inserter->insert(ntmp); + // Lower the call in case there are multiple results. + gogo->lower_block(no, b); + gogo->flatten_block(no, b); - // sz = n * sizeof(elem_type) - Expression* nref = Expression::make_temporary_reference(ntmp, loc); - nref = Expression::make_cast(uintptr_type, nref, loc); - Expression* sz = Expression::make_type_info(et, TYPE_INFO_SIZE); - sz = Expression::make_binary(OPERATOR_MULT, sz, nref, loc); + gogo->finish_function(location); - // memmove(arg1.ptr, arg2.ptr, sz) - Expression* p1 = Expression::make_slice_info(arg1, - SLICE_INFO_VALUE_POINTER, - loc); - Expression* p2 = (arg2_is_string - ? Expression::make_string_info(arg2, - STRING_INFO_DATA, - loc) - : Expression::make_slice_info(arg2, - SLICE_INFO_VALUE_POINTER, - loc)); - Expression* call = Runtime::make_call(gogo, - Runtime::BUILTIN_MEMMOVE, - loc, 3, - p1, p2, sz); + return Expression::make_func_reference(no, NULL, location); +} - // n is the return value of copy - nref = Expression::make_temporary_reference(ntmp, loc); - ret = Expression::make_compound(call, nref, loc); - } - return ret; - } - break; +// Dump the ast for a selector expression. - case BUILTIN_PANIC: - for (Expression_list::iterator pa = this->args()->begin(); - pa != this->args()->end(); - ++pa) +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. + +Builtin_call_expression::Builtin_call_expression(Gogo* gogo, + Expression* fn, + Expression_list* args, + bool is_varargs, + Location location) + : Call_expression(fn, args, is_varargs, location), + gogo_(gogo), code_(BUILTIN_INVALID), seen_(false), + recover_arg_is_set_(false) +{ + Func_expression* fnexp = this->fn()->func_expression(); + if (fnexp == NULL) + { + this->code_ = BUILTIN_INVALID; + return; + } + const std::string& name(fnexp->named_object()->name()); + if (name == "append") + this->code_ = BUILTIN_APPEND; + else if (name == "cap") + this->code_ = BUILTIN_CAP; + else if (name == "close") + this->code_ = BUILTIN_CLOSE; + else if (name == "complex") + this->code_ = BUILTIN_COMPLEX; + else if (name == "copy") + this->code_ = BUILTIN_COPY; + else if (name == "delete") + this->code_ = BUILTIN_DELETE; + else if (name == "imag") + this->code_ = BUILTIN_IMAG; + else if (name == "len") + this->code_ = BUILTIN_LEN; + else if (name == "make") + this->code_ = BUILTIN_MAKE; + else if (name == "new") + this->code_ = BUILTIN_NEW; + else if (name == "panic") + this->code_ = BUILTIN_PANIC; + else if (name == "print") + this->code_ = BUILTIN_PRINT; + else if (name == "println") + this->code_ = BUILTIN_PRINTLN; + else if (name == "real") + this->code_ = BUILTIN_REAL; + else if (name == "recover") + this->code_ = BUILTIN_RECOVER; + else if (name == "Add") + this->code_ = BUILTIN_ADD; + else if (name == "Alignof") + this->code_ = BUILTIN_ALIGNOF; + else if (name == "Offsetof") + this->code_ = BUILTIN_OFFSETOF; + else if (name == "Sizeof") + this->code_ = BUILTIN_SIZEOF; + else if (name == "Slice") + this->code_ = BUILTIN_SLICE; + else + go_unreachable(); +} + +// Return whether this is a call to recover. This is a virtual +// function called from the parent class. + +bool +Builtin_call_expression::do_is_recover_call() const +{ + if (this->classification() == EXPRESSION_ERROR) + return false; + return this->code_ == BUILTIN_RECOVER; +} + +// Set the argument for a call to recover. + +void +Builtin_call_expression::do_set_recover_arg(Expression* arg) +{ + const Expression_list* args = this->args(); + go_assert(args == NULL || args->empty()); + Expression_list* new_args = new Expression_list(); + new_args->push_back(arg); + this->set_args(new_args); + this->recover_arg_is_set_ = true; +} + +// Lower a builtin call expression. This turns new and make into +// specific expressions. We also convert to a constant if we can. + +Expression* +Builtin_call_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) +{ + if (this->is_error_expression()) + return this; + + Location loc = this->location(); + + if (this->is_varargs() && this->code_ != BUILTIN_APPEND) + { + this->report_error(_("invalid use of %<...%> with builtin function")); + return Expression::make_error(loc); + } + + if (this->code_ == BUILTIN_OFFSETOF) + { + Expression* arg = this->one_arg(); + + if (arg->bound_method_expression() != NULL + || arg->interface_field_reference_expression() != NULL) { - if (!(*pa)->is_multi_eval_safe() - && (*pa)->type()->interface_type() != NULL) + this->report_error(_("invalid use of method value as argument " + "of Offsetof")); + return this; + } + + Field_reference_expression* farg = arg->field_reference_expression(); + while (farg != NULL) + { + if (!farg->implicit()) + break; + // When the selector refers to an embedded field, + // it must not be reached through pointer indirections. + if (farg->expr()->deref() != farg->expr()) { - Temporary_statement* temp = - Statement::make_temporary(NULL, *pa, loc); - inserter->insert(temp); - *pa = Expression::make_temporary_reference(temp, loc); + this->report_error(_("argument of Offsetof implies " + "indirection of an embedded field")); + return this; } + // Go up until we reach the original base. + farg = farg->expr()->field_reference_expression(); } + } + + if (this->is_constant()) + { + Numeric_constant nc; + if (this->numeric_constant_value(&nc)) + return nc.expression(loc); + } + + switch (this->code_) + { + default: break; - case BUILTIN_LEN: - case BUILTIN_CAP: + case BUILTIN_NEW: { - Expression_list::iterator pa = this->args()->begin(); - if (!(*pa)->is_multi_eval_safe() - && ((*pa)->type()->map_type() != NULL - || (*pa)->type()->channel_type() != NULL)) + const Expression_list* args = this->args(); + if (args == NULL || args->size() < 1) + this->report_error(_("not enough arguments")); + else if (args->size() > 1) + this->report_error(_("too many arguments")); + else { - Temporary_statement* temp = - Statement::make_temporary(NULL, *pa, loc); - inserter->insert(temp); - *pa = Expression::make_temporary_reference(temp, loc); + Expression* arg = args->front(); + if (!arg->is_type_expression()) + { + go_error_at(arg->location(), "expected type"); + this->set_is_error(); + } + else + return Expression::make_allocation(arg->type(), loc); } } break; + case BUILTIN_MAKE: + return this->lower_make(gogo, inserter); + + case BUILTIN_RECOVER: + if (function != NULL) + function->func_value()->set_calls_recover(); + else + { + // Calling recover outside of a function always returns the + // nil empty interface. + Type* eface = Type::make_empty_interface_type(loc); + return Expression::make_cast(eface, Expression::make_nil(loc), loc); + } + break; + case BUILTIN_DELETE: { - // Lower to a runtime function call. const Expression_list* args = this->args(); - - // Since this function returns no value it must appear in - // a statement by itself, so we don't have to worry about - // order of evaluation of values around it. Evaluate the - // map first to get order of evaluation right. - Map_type* mt = args->front()->type()->map_type(); - Temporary_statement* map_temp = - Statement::make_temporary(mt, args->front(), loc); - inserter->insert(map_temp); - - Temporary_statement* key_temp = - Statement::make_temporary(mt->key_type(), args->back(), loc); - inserter->insert(key_temp); - - Expression* e1 = Expression::make_type_descriptor(mt, loc); - Expression* e2 = Expression::make_temporary_reference(map_temp, - loc); - Expression* e3 = Expression::make_temporary_reference(key_temp, - loc); - - Runtime::Function code; - switch (mt->algorithm(gogo)) + if (args == NULL || args->size() < 2) + this->report_error(_("not enough arguments")); + else if (args->size() > 2) + this->report_error(_("too many arguments")); + else if (args->front()->type()->map_type() == NULL) + this->report_error(_("argument 1 must be a map")); + else + { + Type* key_type = + args->front()->type()->map_type()->key_type(); + Expression_list::iterator pa = this->args()->begin(); + pa++; + Type* arg_type = (*pa)->type(); + std::string reason; + if (!Type::are_assignable(key_type, arg_type, &reason)) + { + if (reason.empty()) + go_error_at(loc, "argument 2 has incompatible type"); + else + go_error_at(loc, "argument 2 has incompatible type (%s)", + reason.c_str()); + this->set_is_error(); + } + else if (!Type::are_identical(key_type, arg_type, 0, NULL)) + *pa = Expression::make_cast(key_type, *pa, loc); + } + } + break; + + case BUILTIN_PRINT: + case BUILTIN_PRINTLN: + // Force all the arguments into temporary variables, so that we + // don't try to evaluate something while holding the print lock. + if (this->args() == NULL) + break; + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if (!(*pa)->is_multi_eval_safe()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; + } + + return this; +} + +// Flatten a builtin call expression. This turns the arguments of some +// builtin calls into temporary expressions. Also expand copy and append +// to runtime calls. + +Expression* +Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, + Statement_inserter* inserter) +{ + if (this->is_error_expression()) + { + go_assert(saw_errors()); + return this; + } + + Location loc = this->location(); + + switch (this->code_) + { + default: + break; + + case BUILTIN_APPEND: + return this->flatten_append(gogo, function, inserter, NULL, NULL); + + case BUILTIN_COPY: + { + Type* at = this->args()->front()->type(); + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if ((*pa)->is_error_expression()) + { + go_assert(saw_errors()); + return Expression::make_error(loc); + } + if ((*pa)->is_nil_expression()) + { + Expression* nil = Expression::make_nil(loc); + Expression* zero = Expression::make_integer_ul(0, NULL, loc); + *pa = Expression::make_slice_value(at, nil, zero, zero, loc); + } + if (!(*pa)->is_multi_eval_safe()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + + // Lower to runtime call. + const Expression_list* args = this->args(); + go_assert(args != NULL && args->size() == 2); + Expression* arg1 = args->front(); + Expression* arg2 = args->back(); + go_assert(arg1->is_multi_eval_safe()); + go_assert(arg2->is_multi_eval_safe()); + bool arg2_is_string = arg2->type()->is_string_type(); + + Expression* ret; + Type* et = at->array_type()->element_type(); + if (et->has_pointer()) + { + Expression* td = Expression::make_type_descriptor(et, loc); + Expression* pd = + Expression::make_slice_info(arg1, SLICE_INFO_VALUE_POINTER, loc); + Expression* ld = + Expression::make_slice_info(arg1, SLICE_INFO_LENGTH, loc); + Expression* ps = + Expression::make_slice_info(arg2, SLICE_INFO_VALUE_POINTER, loc); + Expression* ls = + Expression::make_slice_info(arg2, SLICE_INFO_LENGTH, loc); + ret = Runtime::make_call(gogo, Runtime::TYPEDSLICECOPY, loc, + 5, td, pd, ld, ps, ls); + } + else + { + Type* int_type = Type::lookup_integer_type("int"); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + + // l1 = len(arg1) + Named_object* lenfn = gogo->lookup_global("len"); + Expression* lenref = Expression::make_func_reference(lenfn, NULL, loc); + Expression_list* len_args = new Expression_list(); + len_args->push_back(arg1->copy()); + Expression* len1 = Expression::make_call(lenref, len_args, false, loc); + gogo->lower_expression(function, inserter, &len1); + gogo->flatten_expression(function, inserter, &len1); + Temporary_statement* l1tmp = Statement::make_temporary(int_type, len1, loc); + inserter->insert(l1tmp); + + // l2 = len(arg2) + len_args = new Expression_list(); + len_args->push_back(arg2->copy()); + Expression* len2 = Expression::make_call(lenref, len_args, false, loc); + gogo->lower_expression(function, inserter, &len2); + gogo->flatten_expression(function, inserter, &len2); + Temporary_statement* l2tmp = Statement::make_temporary(int_type, len2, loc); + inserter->insert(l2tmp); + + // n = (l1 < l2 ? l1 : l2) + Expression* l1ref = Expression::make_temporary_reference(l1tmp, loc); + Expression* l2ref = Expression::make_temporary_reference(l2tmp, loc); + Expression* cond = Expression::make_binary(OPERATOR_LT, l1ref, l2ref, loc); + Expression* n = Expression::make_conditional(cond, + l1ref->copy(), + l2ref->copy(), + loc); + Temporary_statement* ntmp = Statement::make_temporary(NULL, n, loc); + inserter->insert(ntmp); + + // sz = n * sizeof(elem_type) + Expression* nref = Expression::make_temporary_reference(ntmp, loc); + nref = Expression::make_cast(uintptr_type, nref, loc); + Expression* sz = Expression::make_type_info(et, TYPE_INFO_SIZE); + sz = Expression::make_binary(OPERATOR_MULT, sz, nref, loc); + + // memmove(arg1.ptr, arg2.ptr, sz) + Expression* p1 = Expression::make_slice_info(arg1, + SLICE_INFO_VALUE_POINTER, + loc); + Expression* p2 = (arg2_is_string + ? Expression::make_string_info(arg2, + STRING_INFO_DATA, + loc) + : Expression::make_slice_info(arg2, + SLICE_INFO_VALUE_POINTER, + loc)); + Expression* call = Runtime::make_call(gogo, + Runtime::BUILTIN_MEMMOVE, + loc, 3, + p1, p2, sz); + + // n is the return value of copy + nref = Expression::make_temporary_reference(ntmp, loc); + ret = Expression::make_compound(call, nref, loc); + } + return ret; + } + break; + + case BUILTIN_PANIC: + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) + { + if (!(*pa)->is_multi_eval_safe() + && (*pa)->type()->interface_type() != NULL) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; + + case BUILTIN_LEN: + case BUILTIN_CAP: + { + Expression_list::iterator pa = this->args()->begin(); + if (!(*pa)->is_multi_eval_safe() + && ((*pa)->type()->map_type() != NULL + || (*pa)->type()->channel_type() != NULL)) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, *pa, loc); + inserter->insert(temp); + *pa = Expression::make_temporary_reference(temp, loc); + } + } + break; + + case BUILTIN_DELETE: + { + // Lower to a runtime function call. + const Expression_list* args = this->args(); + + // Since this function returns no value it must appear in + // a statement by itself, so we don't have to worry about + // order of evaluation of values around it. Evaluate the + // map first to get order of evaluation right. + Map_type* mt = args->front()->type()->map_type(); + Temporary_statement* map_temp = + Statement::make_temporary(mt, args->front(), loc); + inserter->insert(map_temp); + + Temporary_statement* key_temp = + Statement::make_temporary(mt->key_type(), args->back(), loc); + inserter->insert(key_temp); + + Expression* e1 = Expression::make_type_descriptor(mt, loc); + Expression* e2 = Expression::make_temporary_reference(map_temp, + loc); + Expression* e3 = Expression::make_temporary_reference(key_temp, + loc); + + Runtime::Function code; + switch (mt->algorithm(gogo)) { case Map_type::MAP_ALG_FAST32: case Map_type::MAP_ALG_FAST32PTR: @@ -12504,1646 +12785,1046 @@ Call_expression::intrinsify(Gogo* gogo, return Runtime::make_call(gogo, code, loc, 3, a1, a2, a3); } } - else if (package == "internal/abi" - || package == "bootstrap/internal/abi") // for bootstrapping gc - { - if (is_method) - return NULL; - - if ((name == "FuncPCABI0" || name == "FuncPCABIInternal") - && this->args_ != NULL - && this->args_->size() == 1) - { - // We expect to see a conversion from the expression to "any". - Expression* expr = this->args_->front(); - Type_conversion_expression* tce = expr->conversion_expression(); - if (tce != NULL) - expr = tce->expr(); - Func_expression* fe = expr->func_expression(); - Interface_field_reference_expression* interface_method = - expr->interface_field_reference_expression(); - if (fe != NULL) - { - Named_object* no = fe->named_object(); - Expression* ref = Expression::make_func_code_reference(no, loc); - Type* uintptr_type = Type::lookup_integer_type("uintptr"); - return Expression::make_cast(uintptr_type, ref, loc); - } - else if (interface_method != NULL) - return interface_method->get_function(); - else - { - expr = this->args_->front(); - go_assert(expr->type()->interface_type() != NULL - && expr->type()->interface_type()->is_empty()); - expr = Expression::make_interface_info(expr, - INTERFACE_INFO_OBJECT, - loc); - // Trust that this is a function type, which means that - // it is a direct iface type and we can use EXPR - // directly. The backend representation of this - // function is a pointer to a struct whose first field - // is the actual function to call. - Type* pvoid = Type::make_pointer_type(Type::make_void_type()); - Type* pfntype = Type::make_pointer_type(pvoid); - Expression* ref = make_unsafe_cast(pfntype, expr, loc); - return Expression::make_dereference(ref, NIL_CHECK_NOT_NEEDED, - loc); - } - } - } - - return NULL; -} - -// Make implicit type conversions explicit. - -void -Call_expression::do_add_conversions() -{ - // Skip call that requires a thunk. We generate conversions inside the thunk. - if (this->is_concurrent_ || this->is_deferred_) - return; - - if (this->args_ == NULL || this->args_->empty()) - return; - - Function_type* fntype = this->get_function_type(); - if (fntype == NULL) - { - go_assert(saw_errors()); - return; - } - if (fntype->parameters() == NULL || fntype->parameters()->empty()) - return; - - Location loc = this->location(); - Expression_list::iterator pa = this->args_->begin(); - Typed_identifier_list::const_iterator pp = fntype->parameters()->begin(); - bool is_interface_method = - this->fn_->interface_field_reference_expression() != NULL; - size_t argcount = this->args_->size(); - if (!is_interface_method && fntype->is_method()) - { - // Skip the receiver argument, which cannot be interface. - pa++; - argcount--; - } - if (argcount != fntype->parameters()->size()) - { - go_assert(saw_errors()); - return; - } - for (; pa != this->args_->end(); ++pa, ++pp) - { - Type* pt = pp->type(); - if (!Type::are_identical(pt, (*pa)->type(), 0, NULL) - && pt->interface_type() != NULL) - *pa = Expression::make_cast(pt, *pa, loc); - } -} - -// Get the function type. This can return NULL in error cases. - -Function_type* -Call_expression::get_function_type() const -{ - return this->fn_->type()->function_type(); -} - -// Return the number of values which this call will return. - -size_t -Call_expression::result_count() const -{ - const Function_type* fntype = this->get_function_type(); - if (fntype == NULL) - return 0; - if (fntype->results() == NULL) - return 0; - return fntype->results()->size(); -} - -// Return the temporary that holds the result for a call with multiple -// results. - -Temporary_statement* -Call_expression::results() const -{ - if (this->call_temp_ == NULL) - { - go_assert(saw_errors()); - return NULL; - } - return this->call_temp_; -} - -// Set the number of results expected from a call expression. - -void -Call_expression::set_expected_result_count(size_t count) -{ - go_assert(this->expected_result_count_ == 0); - this->expected_result_count_ = count; -} - -// Return whether this is a call to the predeclared function recover. - -bool -Call_expression::is_recover_call() const -{ - return this->do_is_recover_call(); -} - -// Set the argument to the recover function. - -void -Call_expression::set_recover_arg(Expression* arg) -{ - this->do_set_recover_arg(arg); -} - -// Virtual functions also implemented by Builtin_call_expression. - -bool -Call_expression::do_is_recover_call() const -{ - return false; -} - -void -Call_expression::do_set_recover_arg(Expression*) -{ - go_unreachable(); -} - -// We have found an error with this call expression; return true if -// we should report it. - -bool -Call_expression::issue_error() -{ - if (this->issued_error_) - return false; - else - { - this->issued_error_ = true; - return true; - } -} - -// Whether or not this call contains errors, either in the call or the -// arguments to the call. - -bool -Call_expression::is_erroneous_call() -{ - if (this->is_error_expression() || this->fn()->is_error_expression()) - return true; - - if (this->args() == NULL) - return false; - for (Expression_list::iterator pa = this->args()->begin(); - pa != this->args()->end(); - ++pa) - { - if ((*pa)->type()->is_error_type() || (*pa)->is_error_expression()) - return true; - } - return false; -} - -// Get the type. - -Type* -Call_expression::do_type() -{ - if (this->is_error_expression()) - return Type::make_error_type(); - if (this->type_ != NULL) - return this->type_; - - Type* ret; - Function_type* fntype = this->get_function_type(); - if (fntype == NULL) - return Type::make_error_type(); - - const Typed_identifier_list* results = fntype->results(); - if (results == NULL) - ret = Type::make_void_type(); - else if (results->size() == 1) - ret = results->begin()->type(); - else - ret = Type::make_call_multiple_result_type(); - - this->type_ = ret; - - return this->type_; -} - -// Determine types for a call expression. We can use the function -// parameter types to set the types of the arguments. - -void -Call_expression::do_determine_type(Gogo* gogo, const Type_context* context) -{ - if (!this->determining_types()) - return; - - this->fn_->determine_type_no_context(gogo); - Function_type* fntype = this->get_function_type(); - const Typed_identifier_list* parameters = NULL; - if (fntype != NULL) - parameters = fntype->parameters(); - if (this->args_ != NULL) - { - Typed_identifier_list::const_iterator pt; - if (parameters != NULL) - pt = parameters->begin(); - bool first = true; - for (Expression_list::const_iterator pa = this->args_->begin(); - pa != this->args_->end(); - ++pa) - { - if (first) - { - first = false; - // If this is a method, the first argument is the - // receiver. - if (fntype != NULL && fntype->is_method()) - { - Type* rtype = fntype->receiver()->type(); - // The receiver is always passed as a pointer. - if (rtype->points_to() == NULL) - rtype = Type::make_pointer_type(rtype); - Type_context subcontext(rtype, false); - (*pa)->determine_type(gogo, &subcontext); - continue; - } - } - - if (parameters != NULL && pt != parameters->end()) - { - Type_context subcontext(pt->type(), false); - (*pa)->determine_type(gogo, &subcontext); - ++pt; - } - else - (*pa)->determine_type_no_context(gogo); - } - } - - // If this is a call to a generated equality function, we determine - // the type based on the context. See the comment in - // Binary_expression::lower_array_comparison. - if (this->is_equal_function_ - && !context->may_be_abstract - && context->type != NULL - && context->type->is_boolean_type() - && context->type != Type::lookup_bool_type()) - { - go_assert(this->type_ == NULL - || this->type_ == Type::lookup_bool_type() - || this->type_ == context->type - || this->type_->is_error()); - this->type_ = context->type; - } -} - -// Called when determining types for a Call_expression. Return true -// if we should go ahead, false if they have already been determined. - -bool -Call_expression::determining_types() -{ - if (this->types_are_determined_) - return false; - else - { - this->types_are_determined_ = true; - return true; - } -} - -// Check types for parameter I. - -bool -Call_expression::check_argument_type(int i, const Type* parameter_type, - const Type* argument_type, - Location argument_location, - bool issued_error) -{ - std::string reason; - if (!Type::are_assignable(parameter_type, argument_type, &reason)) + else if (package == "internal/abi" + || package == "bootstrap/internal/abi") // for bootstrapping gc { - if (!issued_error) + if (is_method) + return NULL; + + if ((name == "FuncPCABI0" || name == "FuncPCABIInternal") + && this->args_ != NULL + && this->args_->size() == 1) { - if (reason.empty()) - go_error_at(argument_location, "argument %d has incompatible type", i); + // We expect to see a conversion from the expression to "any". + Expression* expr = this->args_->front(); + Type_conversion_expression* tce = expr->conversion_expression(); + if (tce != NULL) + expr = tce->expr(); + Func_expression* fe = expr->func_expression(); + Interface_field_reference_expression* interface_method = + expr->interface_field_reference_expression(); + if (fe != NULL) + { + Named_object* no = fe->named_object(); + Expression* ref = Expression::make_func_code_reference(no, loc); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + return Expression::make_cast(uintptr_type, ref, loc); + } + else if (interface_method != NULL) + return interface_method->get_function(); else - go_error_at(argument_location, - "argument %d has incompatible type (%s)", - i, reason.c_str()); + { + expr = this->args_->front(); + go_assert(expr->type()->interface_type() != NULL + && expr->type()->interface_type()->is_empty()); + expr = Expression::make_interface_info(expr, + INTERFACE_INFO_OBJECT, + loc); + // Trust that this is a function type, which means that + // it is a direct iface type and we can use EXPR + // directly. The backend representation of this + // function is a pointer to a struct whose first field + // is the actual function to call. + Type* pvoid = Type::make_pointer_type(Type::make_void_type()); + Type* pfntype = Type::make_pointer_type(pvoid); + Expression* ref = make_unsafe_cast(pfntype, expr, loc); + return Expression::make_dereference(ref, NIL_CHECK_NOT_NEEDED, + loc); + } } - this->set_is_error(); - return false; } - return true; + + return NULL; } -// Check types. +// Make implicit type conversions explicit. void -Call_expression::do_check_types(Gogo*) +Call_expression::do_add_conversions() { - if (this->classification() == EXPRESSION_ERROR) + // Skip call that requires a thunk. We generate conversions inside the thunk. + if (this->is_concurrent_ || this->is_deferred_) + return; + + if (this->args_ == NULL || this->args_->empty()) return; Function_type* fntype = this->get_function_type(); if (fntype == NULL) { - if (!this->fn_->type()->is_error()) - this->report_error(_("expected function")); - return; - } - - if (this->expected_result_count_ != 0 - && this->expected_result_count_ != this->result_count()) - { - if (this->issue_error()) - this->report_error(_("function result count mismatch")); - this->set_is_error(); + go_assert(saw_errors()); return; } + if (fntype->parameters() == NULL || fntype->parameters()->empty()) + return; - bool is_method = fntype->is_method(); - if (is_method) - { - go_assert(this->args_ != NULL && !this->args_->empty()); - Type* rtype = fntype->receiver()->type(); - Expression* first_arg = this->args_->front(); - // We dereference the values since receivers are always passed - // as pointers. - std::string reason; - if (!Type::are_assignable(rtype->deref(), first_arg->type()->deref(), - &reason)) - { - if (reason.empty()) - this->report_error(_("incompatible type for receiver")); - else - { - go_error_at(this->location(), - "incompatible type for receiver (%s)", - reason.c_str()); - this->set_is_error(); - } - } - } - - // Note that varargs was handled by the lower_varargs() method, so - // we don't have to worry about it here unless something is wrong. - if (this->is_varargs_ && !this->varargs_are_lowered_) - { - if (!fntype->is_varargs()) - { - go_error_at(this->location(), - _("invalid use of %<...%> calling non-variadic function")); - this->set_is_error(); - return; - } - } - - const Typed_identifier_list* parameters = fntype->parameters(); - if (this->args_ == NULL || this->args_->size() == 0) - { - if (parameters != NULL && !parameters->empty()) - this->report_error(_("not enough arguments")); - } - else if (parameters == NULL) + Location loc = this->location(); + Expression_list::iterator pa = this->args_->begin(); + Typed_identifier_list::const_iterator pp = fntype->parameters()->begin(); + bool is_interface_method = + this->fn_->interface_field_reference_expression() != NULL; + size_t argcount = this->args_->size(); + if (!is_interface_method && fntype->is_method()) { - if (!is_method || this->args_->size() > 1) - this->report_error(_("too many arguments")); + // Skip the receiver argument, which cannot be interface. + pa++; + argcount--; } - else if (this->args_->size() == 1 - && this->args_->front()->call_expression() != NULL - && this->args_->front()->call_expression()->result_count() > 1) + if (argcount != fntype->parameters()->size()) { - // This is F(G()) when G returns more than one result. If the - // results can be matched to parameters, it would have been - // lowered in do_lower. If we get here we know there is a - // mismatch. - if (this->args_->front()->call_expression()->result_count() - < parameters->size()) - this->report_error(_("not enough arguments")); - else - this->report_error(_("too many arguments")); + go_assert(saw_errors()); + return; } - else + for (; pa != this->args_->end(); ++pa, ++pp) { - int i = 0; - Expression_list::const_iterator pa = this->args_->begin(); - if (is_method) - ++pa; - for (Typed_identifier_list::const_iterator pt = parameters->begin(); - pt != parameters->end(); - ++pt, ++pa, ++i) - { - if (pa == this->args_->end()) - { - this->report_error(_("not enough arguments")); - return; - } - this->check_argument_type(i + 1, pt->type(), (*pa)->type(), - (*pa)->location(), false); - } - if (pa != this->args_->end()) - this->report_error(_("too many arguments")); + Type* pt = pp->type(); + if (!Type::are_identical(pt, (*pa)->type(), 0, NULL) + && pt->interface_type() != NULL) + *pa = Expression::make_cast(pt, *pa, loc); } } -Expression* -Call_expression::do_copy() -{ - Call_expression* call = - Expression::make_call(this->fn_->copy(), - (this->args_ == NULL - ? NULL - : this->args_->copy()), - this->is_varargs_, this->location()); - - if (this->varargs_are_lowered_) - call->set_varargs_are_lowered(); - if (this->is_deferred_) - call->set_is_deferred(); - if (this->is_concurrent_) - call->set_is_concurrent(); - return call; -} - -// Return whether we have to use a temporary variable to ensure that -// we evaluate this call expression in order. If the call returns no -// results then it will inevitably be executed last. +// Get the function type. This can return NULL in error cases. -bool -Call_expression::do_must_eval_in_order() const +Function_type* +Call_expression::get_function_type() const { - return this->result_count() > 0; + return this->fn_->type()->function_type(); } -// Get the function and the first argument to use when calling an -// interface method. +// Return the number of values which this call will return. -Expression* -Call_expression::interface_method_function( - Interface_field_reference_expression* interface_method, - Expression** first_arg_ptr, - Location location) +size_t +Call_expression::result_count() const { - Expression* object = interface_method->get_underlying_object(); - Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); - *first_arg_ptr = - Expression::make_unsafe_cast(unsafe_ptr_type, object, location); - return interface_method->get_function(); + const Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return 0; + if (fntype->results() == NULL) + return 0; + return fntype->results()->size(); } -// Build the call expression. +// Return the temporary that holds the result for a call with multiple +// results. -Bexpression* -Call_expression::do_get_backend(Translate_context* context) +Temporary_statement* +Call_expression::results() const { - Location location = this->location(); - - if (this->call_ != NULL) + if (this->call_temp_ == NULL) { - // If the call returns multiple results, make a new reference to - // the temporary. - if (this->call_temp_ != NULL) - { - Expression* ref = - Expression::make_temporary_reference(this->call_temp_, location); - return ref->get_backend(context); - } - - return this->call_; + go_assert(saw_errors()); + return NULL; } + return this->call_temp_; +} - Function_type* fntype = this->get_function_type(); - if (fntype == NULL) - return context->backend()->error_expression(); +// Set the number of results expected from a call expression. - if (this->fn_->is_error_expression()) - return context->backend()->error_expression(); +void +Call_expression::set_expected_result_count(size_t count) +{ + go_assert(this->expected_result_count_ == 0); + this->expected_result_count_ = count; +} - Gogo* gogo = context->gogo(); +// Return whether this is a call to the predeclared function recover. - Func_expression* func = this->fn_->func_expression(); - Interface_field_reference_expression* interface_method = - this->fn_->interface_field_reference_expression(); - const bool has_closure = func != NULL && func->closure() != NULL; - const bool is_interface_method = interface_method != NULL; +bool +Call_expression::is_recover_call() const +{ + return this->do_is_recover_call(); +} - bool has_closure_arg; - if (has_closure) - has_closure_arg = true; - else if (func != NULL) - has_closure_arg = false; - else if (is_interface_method) - has_closure_arg = false; - else - has_closure_arg = true; +// Set the argument to the recover function. - Expression* first_arg = NULL; - if (!is_interface_method && fntype->is_method()) - { - first_arg = this->args_->front(); - if (first_arg->type()->points_to() == NULL - && first_arg->type()->is_direct_iface_type()) - first_arg = Expression::unpack_direct_iface(first_arg, - first_arg->location()); - } +void +Call_expression::set_recover_arg(Expression* arg) +{ + this->do_set_recover_arg(arg); +} - int nargs; - std::vector fn_args; - if (this->args_ == NULL || this->args_->empty()) - { - nargs = is_interface_method ? 1 : 0; - if (nargs > 0) - fn_args.resize(1); - } - else if (fntype->parameters() == NULL || fntype->parameters()->empty()) - { - // Passing a receiver parameter. - go_assert(!is_interface_method - && fntype->is_method() - && this->args_->size() == 1); - nargs = 1; - fn_args.resize(1); - fn_args[0] = first_arg->get_backend(context); - } - else - { - const Typed_identifier_list* params = fntype->parameters(); +// Virtual functions also implemented by Builtin_call_expression. - nargs = this->args_->size(); - int i = is_interface_method ? 1 : 0; - nargs += i; - fn_args.resize(nargs); +bool +Call_expression::do_is_recover_call() const +{ + return false; +} - Typed_identifier_list::const_iterator pp = params->begin(); - Expression_list::const_iterator pe = this->args_->begin(); - if (!is_interface_method && fntype->is_method()) - { - fn_args[i] = first_arg->get_backend(context); - ++pe; - ++i; - } - for (; pe != this->args_->end(); ++pe, ++pp, ++i) - { - go_assert(pp != params->end()); - Expression* arg = - Expression::convert_for_assignment(gogo, pp->type(), *pe, - location); - fn_args[i] = arg->get_backend(context); - } - go_assert(pp == params->end()); - go_assert(i == nargs); - } +void +Call_expression::do_set_recover_arg(Expression*) +{ + go_unreachable(); +} - Expression* fn; - Expression* closure = NULL; - if (func != NULL) - { - Named_object* no = func->named_object(); - fn = Expression::make_func_code_reference(no, location); - if (has_closure) - closure = func->closure(); - } - else if (!is_interface_method) - { - closure = this->fn_; +// We have found an error with this call expression; return true if +// we should report it. - // The backend representation of this function type is a pointer - // to a struct whose first field is the actual function to call. - Type* pfntype = - Type::make_pointer_type( - Type::make_pointer_type(Type::make_void_type())); - fn = Expression::make_unsafe_cast(pfntype, this->fn_, location); - fn = Expression::make_dereference(fn, NIL_CHECK_NOT_NEEDED, location); - } +bool +Call_expression::issue_error() +{ + if (this->issued_error_) + return false; else { - Expression* arg0; - fn = this->interface_method_function(interface_method, &arg0, - location); - fn_args[0] = arg0->get_backend(context); + this->issued_error_ = true; + return true; } +} - Bexpression* bclosure = NULL; - if (has_closure_arg) - bclosure = closure->get_backend(context); - else - go_assert(closure == NULL); +// Whether or not this call contains errors, either in the call or the +// arguments to the call. - Bexpression* bfn = fn->get_backend(context); +bool +Call_expression::is_erroneous_call() +{ + if (this->is_error_expression() || this->fn()->is_error_expression()) + return true; - // When not calling a named function directly, use a type conversion - // in case the type of the function is a recursive type which refers - // to itself. We don't do this for an interface method because 1) - // an interface method never refers to itself, so we always have a - // function type here; 2) we pass an extra first argument to an - // interface method, so fntype is not correct. - if (func == NULL && !is_interface_method) + if (this->args() == NULL) + return false; + for (Expression_list::iterator pa = this->args()->begin(); + pa != this->args()->end(); + ++pa) { - Btype* bft = fntype->get_backend_fntype(gogo); - bfn = gogo->backend()->convert_expression(bft, bfn, location); + if ((*pa)->type()->is_error_type() || (*pa)->is_error_expression()) + return true; } + return false; +} - Bfunction* bfunction = NULL; - if (context->function()) - bfunction = context->function()->func_value()->get_decl(); - Bexpression* call = gogo->backend()->call_expression(bfunction, bfn, - fn_args, bclosure, - location); +// Get the type. - if (this->call_temp_ != NULL) - { - // This case occurs when the call returns multiple results. +Type* +Call_expression::do_type() +{ + if (this->is_error_expression()) + return Type::make_error_type(); + if (this->type_ != NULL) + return this->type_; - Expression* ref = Expression::make_temporary_reference(this->call_temp_, - location); - Bexpression* bref = ref->get_backend(context); - Bstatement* bassn = gogo->backend()->assignment_statement(bfunction, - bref, call, - location); + Type* ret; + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return Type::make_error_type(); - ref = Expression::make_temporary_reference(this->call_temp_, location); - this->call_ = ref->get_backend(context); + const Typed_identifier_list* results = fntype->results(); + if (results == NULL) + ret = Type::make_void_type(); + else if (results->size() == 1) + ret = results->begin()->type(); + else + ret = Type::make_call_multiple_result_type(); - return gogo->backend()->compound_expression(bassn, this->call_, - location); - } + this->type_ = ret; - this->call_ = call; - return this->call_; + return this->type_; } -// The cost of inlining a call expression. +// Determine types for a call expression. We can use the function +// parameter types to set the types of the arguments. -int -Call_expression::do_inlining_cost() const +void +Call_expression::do_determine_type(Gogo* gogo, const Type_context* context) { - Func_expression* fn = this->fn_->func_expression(); + if (!this->determining_types()) + return; - // FIXME: We don't yet support all kinds of calls. - if (fn != NULL && fn->closure() != NULL) - return 0x100000; - if (this->fn_->interface_field_reference_expression()) - return 0x100000; - if (this->get_function_type()->is_method()) - return 0x100000; + this->fn_->determine_type_no_context(gogo); + Function_type* fntype = this->get_function_type(); + const Typed_identifier_list* parameters = NULL; + if (fntype != NULL) + parameters = fntype->parameters(); + if (this->args_ != NULL) + { + Typed_identifier_list::const_iterator pt; + if (parameters != NULL) + pt = parameters->begin(); + bool first = true; + for (Expression_list::const_iterator pa = this->args_->begin(); + pa != this->args_->end(); + ++pa) + { + if (first) + { + first = false; + // If this is a method, the first argument is the + // receiver. + if (fntype != NULL && fntype->is_method()) + { + Type* rtype = fntype->receiver()->type(); + // The receiver is always passed as a pointer. + if (rtype->points_to() == NULL) + rtype = Type::make_pointer_type(rtype); + Type_context subcontext(rtype, false); + (*pa)->determine_type(gogo, &subcontext); + continue; + } + } + + if (parameters != NULL && pt != parameters->end()) + { + Type_context subcontext(pt->type(), false); + (*pa)->determine_type(gogo, &subcontext); + ++pt; + } + else + (*pa)->determine_type_no_context(gogo); + } + } - return 5; + // If this is a call to a generated equality function, we determine + // the type based on the context. See the comment in + // Binary_expression::lower_array_comparison. + if (this->is_equal_function_ + && !context->may_be_abstract + && context->type != NULL + && context->type->is_boolean_type() + && context->type != Type::lookup_bool_type()) + { + go_assert(this->type_ == NULL + || this->type_ == Type::lookup_bool_type() + || this->type_ == context->type + || this->type_->is_error()); + this->type_ = context->type; + } } -// Export a call expression. +// Called when determining types for a Call_expression. Return true +// if we should go ahead, false if they have already been determined. -void -Call_expression::do_export(Export_function_body* efb) const +bool +Call_expression::determining_types() { - bool simple_call = (this->fn_->func_expression() != NULL); - if (!simple_call) - efb->write_c_string("("); - this->fn_->export_expression(efb); - if (!simple_call) - efb->write_c_string(")"); - this->export_arguments(efb); + if (this->types_are_determined_) + return false; + else + { + this->types_are_determined_ = true; + return true; + } } -// Export call expression arguments. +// Check types for parameter I. -void -Call_expression::export_arguments(Export_function_body* efb) const +bool +Call_expression::check_argument_type(int i, const Type* parameter_type, + const Type* argument_type, + Location argument_location, + bool issued_error) { - efb->write_c_string("("); - if (this->args_ != NULL && !this->args_->empty()) + std::string reason; + if (!Type::are_assignable(parameter_type, argument_type, &reason)) { - Expression_list::const_iterator pa = this->args_->begin(); - (*pa)->export_expression(efb); - for (pa++; pa != this->args_->end(); pa++) + if (!issued_error) { - efb->write_c_string(", "); - (*pa)->export_expression(efb); + if (reason.empty()) + go_error_at(argument_location, "argument %d has incompatible type", i); + else + go_error_at(argument_location, + "argument %d has incompatible type (%s)", + i, reason.c_str()); } - if (this->is_varargs_) - efb->write_c_string("..."); + this->set_is_error(); + return false; } - efb->write_c_string(")"); + return true; } -// Dump ast representation for a call expression. +// Check types. void -Call_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const -{ - this->fn_->dump_expression(ast_dump_context); - ast_dump_context->ostream() << "("; - if (args_ != NULL) - ast_dump_context->dump_expression_list(this->args_); - - ast_dump_context->ostream() << ") "; -} - -// Make a call expression. - -Call_expression* -Expression::make_call(Expression* fn, Expression_list* args, bool is_varargs, - Location location) +Call_expression::do_check_types(Gogo*) { - return new Call_expression(fn, args, is_varargs, location); -} - -// Class Call_result_expression. - -// Traverse a call result. + if (this->classification() == EXPRESSION_ERROR) + return; -int -Call_result_expression::do_traverse(Traverse* traverse) -{ - if (traverse->remember_expression(this->call_)) + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) { - // We have already traversed the call expression. - return TRAVERSE_CONTINUE; + if (!this->fn_->type()->is_error()) + this->report_error(_("expected function")); + return; } - return Expression::traverse(&this->call_, traverse); -} - -// Get the type. - -Type* -Call_result_expression::do_type() -{ - if (this->classification() == EXPRESSION_ERROR) - return Type::make_error_type(); - // THIS->CALL_ can be replaced with a temporary reference due to - // Call_expression::do_must_eval_in_order when there is an error. - Call_expression* ce = this->call_->call_expression(); - if (ce == NULL) + if (this->expected_result_count_ != 0 + && this->expected_result_count_ != this->result_count()) { + if (this->issue_error()) + this->report_error(_("function result count mismatch")); this->set_is_error(); - return Type::make_error_type(); + return; } - Function_type* fntype = ce->get_function_type(); - if (fntype == NULL) + + bool is_method = fntype->is_method(); + if (is_method) { - if (ce->issue_error()) + go_assert(this->args_ != NULL && !this->args_->empty()); + Type* rtype = fntype->receiver()->type(); + Expression* first_arg = this->args_->front(); + // We dereference the values since receivers are always passed + // as pointers. + std::string reason; + if (!Type::are_assignable(rtype->deref(), first_arg->type()->deref(), + &reason)) { - if (!ce->fn()->type()->is_error()) - this->report_error(_("expected function")); + if (reason.empty()) + this->report_error(_("incompatible type for receiver")); + else + { + go_error_at(this->location(), + "incompatible type for receiver (%s)", + reason.c_str()); + this->set_is_error(); + } } - this->set_is_error(); - return Type::make_error_type(); } - const Typed_identifier_list* results = fntype->results(); - if (results == NULL || results->size() < 2) + + // Note that varargs was handled by the lower_varargs() method, so + // we don't have to worry about it here unless something is wrong. + if (this->is_varargs_ && !this->varargs_are_lowered_) { - if (ce->issue_error()) - this->report_error(_("number of results does not match " - "number of values")); - return Type::make_error_type(); + if (!fntype->is_varargs()) + { + go_error_at(this->location(), + _("invalid use of %<...%> calling non-variadic function")); + this->set_is_error(); + return; + } } - Typed_identifier_list::const_iterator pr = results->begin(); - for (unsigned int i = 0; i < this->index_; ++i) + + const Typed_identifier_list* parameters = fntype->parameters(); + if (this->args_ == NULL || this->args_->size() == 0) { - if (pr == results->end()) - break; - ++pr; + if (parameters != NULL && !parameters->empty()) + this->report_error(_("not enough arguments")); } - if (pr == results->end()) + else if (parameters == NULL) { - if (ce->issue_error()) - this->report_error(_("number of results does not match " - "number of values")); - return Type::make_error_type(); + if (!is_method || this->args_->size() > 1) + this->report_error(_("too many arguments")); } - return pr->type(); -} - -// Check the type. Just make sure that we trigger the warning in -// do_type. - -void -Call_result_expression::do_check_types(Gogo*) -{ - this->type(); -} - -// Determine the type. We have nothing to do here, but the 0 result -// needs to pass down to the caller. - -void -Call_result_expression::do_determine_type(Gogo* gogo, const Type_context*) -{ - this->call_->determine_type_no_context(gogo); -} - -// Return the backend representation. We just refer to the temporary set by the -// call expression. We don't do this at lowering time because it makes it -// hard to evaluate the call at the right time. - -Bexpression* -Call_result_expression::do_get_backend(Translate_context* context) -{ - Call_expression* ce = this->call_->call_expression(); - if (ce == NULL) + else if (this->args_->size() == 1 + && this->args_->front()->call_expression() != NULL + && this->args_->front()->call_expression()->result_count() > 1) { - go_assert(this->call_->is_error_expression()); - return context->backend()->error_expression(); + // This is F(G()) when G returns more than one result. If the + // results can be matched to parameters, it would have been + // lowered in do_lower. If we get here we know there is a + // mismatch. + if (this->args_->front()->call_expression()->result_count() + < parameters->size()) + this->report_error(_("not enough arguments")); + else + this->report_error(_("too many arguments")); } - Temporary_statement* ts = ce->results(); - if (ts == NULL) + else { - go_assert(saw_errors()); - return context->backend()->error_expression(); + int i = 0; + Expression_list::const_iterator pa = this->args_->begin(); + if (is_method) + ++pa; + for (Typed_identifier_list::const_iterator pt = parameters->begin(); + pt != parameters->end(); + ++pt, ++pa, ++i) + { + if (pa == this->args_->end()) + { + this->report_error(_("not enough arguments")); + return; + } + this->check_argument_type(i + 1, pt->type(), (*pa)->type(), + (*pa)->location(), false); + } + if (pa != this->args_->end()) + this->report_error(_("too many arguments")); } - Expression* ref = Expression::make_temporary_reference(ts, this->location()); - ref = Expression::make_field_reference(ref, this->index_, this->location()); - return ref->get_backend(context); } -// Dump ast representation for a call result expression. - -void -Call_result_expression::do_dump_expression(Ast_dump_context* ast_dump_context) - const +Expression* +Call_expression::do_copy() { - // FIXME: Wouldn't it be better if the call is assigned to a temporary - // (struct) and the fields are referenced instead. - ast_dump_context->ostream() << this->index_ << "@("; - ast_dump_context->dump_expression(this->call_); - ast_dump_context->ostream() << ")"; + Call_expression* call = + Expression::make_call(this->fn_->copy(), + (this->args_ == NULL + ? NULL + : this->args_->copy()), + this->is_varargs_, this->location()); + + if (this->varargs_are_lowered_) + call->set_varargs_are_lowered(); + if (this->is_deferred_) + call->set_is_deferred(); + if (this->is_concurrent_) + call->set_is_concurrent(); + return call; } -// Make a reference to a single result of a call which returns -// multiple results. +// Return whether we have to use a temporary variable to ensure that +// we evaluate this call expression in order. If the call returns no +// results then it will inevitably be executed last. -Expression* -Expression::make_call_result(Call_expression* call, unsigned int index) +bool +Call_expression::do_must_eval_in_order() const { - return new Call_result_expression(call, index); + return this->result_count() > 0; } -// Class Index_expression. - -// Traversal. +// Get the function and the first argument to use when calling an +// interface method. -int -Index_expression::do_traverse(Traverse* traverse) +Expression* +Call_expression::interface_method_function( + Interface_field_reference_expression* interface_method, + Expression** first_arg_ptr, + Location location) { - if (Expression::traverse(&this->left_, traverse) == TRAVERSE_EXIT - || Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT - || (this->end_ != NULL - && Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) - || (this->cap_ != NULL - && Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT)) - return TRAVERSE_EXIT; - return TRAVERSE_CONTINUE; + Expression* object = interface_method->get_underlying_object(); + Type* unsafe_ptr_type = Type::make_pointer_type(Type::make_void_type()); + *first_arg_ptr = + Expression::make_unsafe_cast(unsafe_ptr_type, object, location); + return interface_method->get_function(); } -// Lower an index expression. This converts the generic index -// expression into an array index, a string index, or a map index. +// Build the call expression. -Expression* -Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) +Bexpression* +Call_expression::do_get_backend(Translate_context* context) { Location location = this->location(); - Expression* left = this->left_; - Expression* start = this->start_; - Expression* end = this->end_; - Expression* cap = this->cap_; - Type* type = left->type(); - if (type->is_error()) + if (this->call_ != NULL) { - go_assert(saw_errors()); - return Expression::make_error(location); + // If the call returns multiple results, make a new reference to + // the temporary. + if (this->call_temp_ != NULL) + { + Expression* ref = + Expression::make_temporary_reference(this->call_temp_, location); + return ref->get_backend(context); + } + + return this->call_; } - else if (left->is_type_expression()) + + Function_type* fntype = this->get_function_type(); + if (fntype == NULL) + return context->backend()->error_expression(); + + if (this->fn_->is_error_expression()) + return context->backend()->error_expression(); + + Gogo* gogo = context->gogo(); + + Func_expression* func = this->fn_->func_expression(); + Interface_field_reference_expression* interface_method = + this->fn_->interface_field_reference_expression(); + const bool has_closure = func != NULL && func->closure() != NULL; + const bool is_interface_method = interface_method != NULL; + + bool has_closure_arg; + if (has_closure) + has_closure_arg = true; + else if (func != NULL) + has_closure_arg = false; + else if (is_interface_method) + has_closure_arg = false; + else + has_closure_arg = true; + + Expression* first_arg = NULL; + if (!is_interface_method && fntype->is_method()) { - go_error_at(location, "attempt to index type expression"); - return Expression::make_error(location); + first_arg = this->args_->front(); + if (first_arg->type()->points_to() == NULL + && first_arg->type()->is_direct_iface_type()) + first_arg = Expression::unpack_direct_iface(first_arg, + first_arg->location()); } - else if (type->array_type() != NULL) - return Expression::make_array_index(left, start, end, cap, location); - else if (type->points_to() != NULL - && type->points_to()->array_type() != NULL - && !type->points_to()->is_slice_type()) - { - Expression* deref = - Expression::make_dereference(left, NIL_CHECK_DEFAULT, location); - - // For an ordinary index into the array, the pointer will be - // dereferenced. For a slice it will not--the resulting slice - // will simply reuse the pointer, which is incorrect if that - // pointer is nil. - if (end != NULL || cap != NULL) - deref->issue_nil_check(); - return Expression::make_array_index(deref, start, end, cap, location); + int nargs; + std::vector fn_args; + if (this->args_ == NULL || this->args_->empty()) + { + nargs = is_interface_method ? 1 : 0; + if (nargs > 0) + fn_args.resize(1); } - else if (type->is_string_type()) + else if (fntype->parameters() == NULL || fntype->parameters()->empty()) { - if (cap != NULL) - { - go_error_at(location, "invalid 3-index slice of string"); - return Expression::make_error(location); - } - return Expression::make_string_index(left, start, end, location); + // Passing a receiver parameter. + go_assert(!is_interface_method + && fntype->is_method() + && this->args_->size() == 1); + nargs = 1; + fn_args.resize(1); + fn_args[0] = first_arg->get_backend(context); } - else if (type->map_type() != NULL) + else { - if (end != NULL || cap != NULL) + const Typed_identifier_list* params = fntype->parameters(); + + nargs = this->args_->size(); + int i = is_interface_method ? 1 : 0; + nargs += i; + fn_args.resize(nargs); + + Typed_identifier_list::const_iterator pp = params->begin(); + Expression_list::const_iterator pe = this->args_->begin(); + if (!is_interface_method && fntype->is_method()) { - go_error_at(location, "invalid slice of map"); - return Expression::make_error(location); + fn_args[i] = first_arg->get_backend(context); + ++pe; + ++i; } - return Expression::make_map_index(left, start, location); + for (; pe != this->args_->end(); ++pe, ++pp, ++i) + { + go_assert(pp != params->end()); + Expression* arg = + Expression::convert_for_assignment(gogo, pp->type(), *pe, + location); + fn_args[i] = arg->get_backend(context); + } + go_assert(pp == params->end()); + go_assert(i == nargs); } - else if (cap != NULL) + + Expression* fn; + Expression* closure = NULL; + if (func != NULL) { - go_error_at(location, - "invalid 3-index slice of object that is not a slice"); - return Expression::make_error(location); + Named_object* no = func->named_object(); + fn = Expression::make_func_code_reference(no, location); + if (has_closure) + closure = func->closure(); } - else if (end != NULL) + else if (!is_interface_method) { - go_error_at(location, - ("attempt to slice object that is not " - "array, slice, or string")); - return Expression::make_error(location); + closure = this->fn_; + + // The backend representation of this function type is a pointer + // to a struct whose first field is the actual function to call. + Type* pfntype = + Type::make_pointer_type( + Type::make_pointer_type(Type::make_void_type())); + fn = Expression::make_unsafe_cast(pfntype, this->fn_, location); + fn = Expression::make_dereference(fn, NIL_CHECK_NOT_NEEDED, location); } else { - go_error_at(location, - ("attempt to index object that is not " - "array, slice, string, or map")); - return Expression::make_error(location); + Expression* arg0; + fn = this->interface_method_function(interface_method, &arg0, + location); + fn_args[0] = arg0->get_backend(context); } -} -// Write an indexed expression -// (expr[expr:expr:expr], expr[expr:expr] or expr[expr]) to a dump context. + Bexpression* bclosure = NULL; + if (has_closure_arg) + bclosure = closure->get_backend(context); + else + go_assert(closure == NULL); -void -Index_expression::dump_index_expression(Ast_dump_context* ast_dump_context, - const Expression* expr, - const Expression* start, - const Expression* end, - const Expression* cap) -{ - expr->dump_expression(ast_dump_context); - ast_dump_context->ostream() << "["; - start->dump_expression(ast_dump_context); - if (end != NULL) + Bexpression* bfn = fn->get_backend(context); + + // When not calling a named function directly, use a type conversion + // in case the type of the function is a recursive type which refers + // to itself. We don't do this for an interface method because 1) + // an interface method never refers to itself, so we always have a + // function type here; 2) we pass an extra first argument to an + // interface method, so fntype is not correct. + if (func == NULL && !is_interface_method) { - ast_dump_context->ostream() << ":"; - end->dump_expression(ast_dump_context); + Btype* bft = fntype->get_backend_fntype(gogo); + bfn = gogo->backend()->convert_expression(bft, bfn, location); } - if (cap != NULL) + + Bfunction* bfunction = NULL; + if (context->function()) + bfunction = context->function()->func_value()->get_decl(); + Bexpression* call = gogo->backend()->call_expression(bfunction, bfn, + fn_args, bclosure, + location); + + if (this->call_temp_ != NULL) { - ast_dump_context->ostream() << ":"; - cap->dump_expression(ast_dump_context); - } - ast_dump_context->ostream() << "]"; -} + // This case occurs when the call returns multiple results. -// Dump ast representation for an index expression. + Expression* ref = Expression::make_temporary_reference(this->call_temp_, + location); + Bexpression* bref = ref->get_backend(context); + Bstatement* bassn = gogo->backend()->assignment_statement(bfunction, + bref, call, + location); -void -Index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) - const -{ - Index_expression::dump_index_expression(ast_dump_context, this->left_, - this->start_, this->end_, this->cap_); -} + ref = Expression::make_temporary_reference(this->call_temp_, location); + this->call_ = ref->get_backend(context); -// Make an index expression. + return gogo->backend()->compound_expression(bassn, this->call_, + location); + } -Expression* -Expression::make_index(Expression* left, Expression* start, Expression* end, - Expression* cap, Location location) -{ - return new Index_expression(left, start, end, cap, location); + this->call_ = call; + return this->call_; } -// Class Array_index_expression. - -// Array index traversal. +// The cost of inlining a call expression. int -Array_index_expression::do_traverse(Traverse* traverse) +Call_expression::do_inlining_cost() const { - if (Expression::traverse(&this->array_, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - if (this->end_ != NULL) - { - if (Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - if (this->cap_ != NULL) - { - if (Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT) - return TRAVERSE_EXIT; - } - return TRAVERSE_CONTINUE; -} + Func_expression* fn = this->fn_->func_expression(); -// Return the type of an array index. + // FIXME: We don't yet support all kinds of calls. + if (fn != NULL && fn->closure() != NULL) + return 0x100000; + if (this->fn_->interface_field_reference_expression()) + return 0x100000; + if (this->get_function_type()->is_method()) + return 0x100000; -Type* -Array_index_expression::do_type() -{ - if (this->type_ == NULL) - { - Array_type* type = this->array_->type()->array_type(); - if (type == NULL) - this->type_ = Type::make_error_type(); - else if (this->end_ == NULL) - this->type_ = type->element_type(); - else if (type->is_slice_type()) - { - // A slice of a slice has the same type as the original - // slice. - this->type_ = this->array_->type()->deref(); - } - else - { - // A slice of an array is a slice. - this->type_ = Type::make_array_type(type->element_type(), NULL); - } - } - return this->type_; + return 5; } -// Set the type of an array index. +// Export a call expression. void -Array_index_expression::do_determine_type(Gogo* gogo, const Type_context*) +Call_expression::do_export(Export_function_body* efb) const { - this->array_->determine_type_no_context(gogo); - - Type_context index_context(Type::lookup_integer_type("int"), false); - this->start_->determine_type(gogo, &index_context); - if (this->end_ != NULL) - this->end_->determine_type(gogo, &index_context); - if (this->cap_ != NULL) - this->cap_->determine_type(gogo, &index_context); + bool simple_call = (this->fn_->func_expression() != NULL); + if (!simple_call) + efb->write_c_string("("); + this->fn_->export_expression(efb); + if (!simple_call) + efb->write_c_string(")"); + this->export_arguments(efb); } -// Check types of an array index. +// Export call expression arguments. void -Array_index_expression::do_check_types(Gogo*) +Call_expression::export_arguments(Export_function_body* efb) const { - Numeric_constant nc; - unsigned long v; - if (this->start_->type()->integer_type() == NULL - && !this->start_->type()->is_error() - && (!this->start_->type()->is_abstract() - || !this->start_->numeric_constant_value(&nc) - || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) - this->report_error(_("index must be integer")); - if (this->end_ != NULL - && this->end_->type()->integer_type() == NULL - && !this->end_->type()->is_error() - && !this->end_->is_nil_expression() - && !this->end_->is_error_expression() - && (!this->end_->type()->is_abstract() - || !this->end_->numeric_constant_value(&nc) - || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) - this->report_error(_("slice end must be integer")); - if (this->cap_ != NULL - && this->cap_->type()->integer_type() == NULL - && !this->cap_->type()->is_error() - && !this->cap_->is_nil_expression() - && !this->cap_->is_error_expression() - && (!this->cap_->type()->is_abstract() - || !this->cap_->numeric_constant_value(&nc) - || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) - this->report_error(_("slice capacity must be integer")); - - Array_type* array_type = this->array_->type()->array_type(); - if (array_type == NULL) - { - go_assert(this->array_->type()->is_error()); - this->set_is_error(); - return; - } - - unsigned int int_bits = - Type::lookup_integer_type("int")->integer_type()->bits(); - - Numeric_constant lvalnc; - mpz_t lval; - bool lval_valid = (array_type->length() != NULL - && array_type->length()->numeric_constant_value(&lvalnc) - && lvalnc.to_int(&lval)); - Numeric_constant inc; - mpz_t ival; - bool ival_valid = false; - if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) - { - ival_valid = true; - if (mpz_sgn(ival) < 0 - || mpz_sizeinbase(ival, 2) >= int_bits - || (lval_valid - && (this->end_ == NULL - ? mpz_cmp(ival, lval) >= 0 - : mpz_cmp(ival, lval) > 0))) - { - go_error_at(this->start_->location(), "array index out of bounds"); - this->set_is_error(); - } - } - if (this->end_ != NULL && !this->end_->is_nil_expression()) - { - Numeric_constant enc; - mpz_t eval; - bool eval_valid = false; - if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) - { - eval_valid = true; - if (mpz_sgn(eval) < 0 - || mpz_sizeinbase(eval, 2) >= int_bits - || (lval_valid && mpz_cmp(eval, lval) > 0)) - { - go_error_at(this->end_->location(), "array index out of bounds"); - this->set_is_error(); - } - else if (ival_valid && mpz_cmp(ival, eval) > 0) - this->report_error(_("inverted slice range")); - } - - Numeric_constant cnc; - mpz_t cval; - if (this->cap_ != NULL - && this->cap_->numeric_constant_value(&cnc) && cnc.to_int(&cval)) - { - if (mpz_sgn(cval) < 0 - || mpz_sizeinbase(cval, 2) >= int_bits - || (lval_valid && mpz_cmp(cval, lval) > 0)) - { - go_error_at(this->cap_->location(), "array index out of bounds"); - this->set_is_error(); - } - else if (ival_valid && mpz_cmp(ival, cval) > 0) - { - go_error_at(this->cap_->location(), - "invalid slice index: capacity less than start"); - this->set_is_error(); - } - else if (eval_valid && mpz_cmp(eval, cval) > 0) - { - go_error_at(this->cap_->location(), - "invalid slice index: capacity less than length"); - this->set_is_error(); - } - mpz_clear(cval); - } - - if (eval_valid) - mpz_clear(eval); - } - if (ival_valid) - mpz_clear(ival); - if (lval_valid) - mpz_clear(lval); - - // A slice of an array requires an addressable array. A slice of a - // slice is always possible. - if (this->end_ != NULL && !array_type->is_slice_type()) - { - if (!this->array_->is_addressable()) - this->report_error(_("slice of unaddressable value")); - else - // Set the array address taken but not escape. The escape - // analysis will make it escape to heap when needed. - this->array_->address_taken(false); + efb->write_c_string("("); + if (this->args_ != NULL && !this->args_->empty()) + { + Expression_list::const_iterator pa = this->args_->begin(); + (*pa)->export_expression(efb); + for (pa++; pa != this->args_->end(); pa++) + { + efb->write_c_string(", "); + (*pa)->export_expression(efb); + } + if (this->is_varargs_) + efb->write_c_string("..."); } + efb->write_c_string(")"); } -// The subexpressions of an array index must be evaluated in order. -// If this is indexing into an array, rather than a slice, then only -// the index should be evaluated. Since this is called for values on -// the left hand side of an assigment, evaluating the array, meaning -// copying the array, will cause a different array to be modified. +// Dump ast representation for a call expression. -bool -Array_index_expression::do_must_eval_subexpressions_in_order( - int* skip) const +void +Call_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { - *skip = this->array_->type()->is_slice_type() ? 0 : 1; - return true; + this->fn_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "("; + if (args_ != NULL) + ast_dump_context->dump_expression_list(this->args_); + + ast_dump_context->ostream() << ") "; } -// Flatten array indexing: add temporary variables and bounds checks. +// Make a call expression. -Expression* -Array_index_expression::do_flatten(Gogo* gogo, Named_object*, - Statement_inserter* inserter) +Call_expression* +Expression::make_call(Expression* fn, Expression_list* args, bool is_varargs, + Location location) { - if (this->is_flattened_) - return this; - this->is_flattened_ = true; - - Location loc = this->location(); - - if (this->is_error_expression()) - return Expression::make_error(loc); + return new Call_expression(fn, args, is_varargs, location); +} - Expression* array = this->array_; - Expression* start = this->start_; - Expression* end = this->end_; - Expression* cap = this->cap_; - if (array->is_error_expression() - || array->type()->is_error_type() - || start->is_error_expression() - || start->type()->is_error_type() - || (end != NULL - && (end->is_error_expression() || end->type()->is_error_type())) - || (cap != NULL - && (cap->is_error_expression() || cap->type()->is_error_type()))) - { - go_assert(saw_errors()); - return Expression::make_error(loc); - } +// Class Call_result_expression. - Array_type* array_type = this->array_->type()->array_type(); - if (array_type == NULL) - { - go_assert(saw_errors()); - return Expression::make_error(loc); - } +// Traverse a call result. - Temporary_statement* temp; - if (array_type->is_slice_type() && !array->is_multi_eval_safe()) - { - temp = Statement::make_temporary(NULL, array, loc); - inserter->insert(temp); - this->array_ = Expression::make_temporary_reference(temp, loc); - array = this->array_; - } - if (!start->is_multi_eval_safe()) - { - temp = Statement::make_temporary(NULL, start, loc); - inserter->insert(temp); - this->start_ = Expression::make_temporary_reference(temp, loc); - start = this->start_; - } - if (end != NULL - && !end->is_nil_expression() - && !end->is_multi_eval_safe()) - { - temp = Statement::make_temporary(NULL, end, loc); - inserter->insert(temp); - this->end_ = Expression::make_temporary_reference(temp, loc); - end = this->end_; - } - if (cap != NULL && !cap->is_multi_eval_safe()) +int +Call_result_expression::do_traverse(Traverse* traverse) +{ + if (traverse->remember_expression(this->call_)) { - temp = Statement::make_temporary(NULL, cap, loc); - inserter->insert(temp); - this->cap_ = Expression::make_temporary_reference(temp, loc); - cap = this->cap_; + // We have already traversed the call expression. + return TRAVERSE_CONTINUE; } + return Expression::traverse(&this->call_, traverse); +} - if (!this->needs_bounds_check_) - return this; +// Get the type. - Expression* len; - if (!array_type->is_slice_type()) - { - len = array_type->get_length(gogo, this->array_); - go_assert(len->is_constant()); - } - else - { - len = array_type->get_length(gogo, this->array_->copy()); - temp = Statement::make_temporary(NULL, len, loc); - inserter->insert(temp); - len = Expression::make_temporary_reference(temp, loc); - } +Type* +Call_result_expression::do_type() +{ + if (this->classification() == EXPRESSION_ERROR) + return Type::make_error_type(); - Expression* scap = NULL; - if (array_type->is_slice_type()) + // THIS->CALL_ can be replaced with a temporary reference due to + // Call_expression::do_must_eval_in_order when there is an error. + Call_expression* ce = this->call_->call_expression(); + if (ce == NULL) { - scap = array_type->get_capacity(gogo, this->array_->copy()); - temp = Statement::make_temporary(NULL, scap, loc); - inserter->insert(temp); - scap = Expression::make_temporary_reference(temp, loc); + this->set_is_error(); + return Type::make_error_type(); } - - // The order of bounds checks here matches the order used by the gc - // compiler, as tested by issue30116[u].go. - - if (cap != NULL) + Function_type* fntype = ce->get_function_type(); + if (fntype == NULL) { - if (array_type->is_slice_type()) - Expression::check_bounds(gogo, cap, OPERATOR_LE, scap, - Runtime::PANIC_SLICE3_ACAP, - Runtime::PANIC_SLICE3_ACAP_U, - Runtime::PANIC_EXTEND_SLICE3_ACAP, - Runtime::PANIC_EXTEND_SLICE3_ACAP_U, - inserter, loc); - else - Expression::check_bounds(gogo, cap, OPERATOR_LE, len, - Runtime::PANIC_SLICE3_ALEN, - Runtime::PANIC_SLICE3_ALEN_U, - Runtime::PANIC_EXTEND_SLICE3_ALEN, - Runtime::PANIC_EXTEND_SLICE3_ALEN_U, - inserter, loc); - - Expression* start_bound = cap; - if (end != NULL && !end->is_nil_expression()) + if (ce->issue_error()) { - Expression::check_bounds(gogo, end, OPERATOR_LE, cap, - Runtime::PANIC_SLICE3_B, - Runtime::PANIC_SLICE3_B_U, - Runtime::PANIC_EXTEND_SLICE3_B, - Runtime::PANIC_EXTEND_SLICE3_B_U, - inserter, loc); - start_bound = end; + if (!ce->fn()->type()->is_error()) + this->report_error(_("expected function")); } - - Expression::check_bounds(gogo, start, OPERATOR_LE, start_bound, - Runtime::PANIC_SLICE3_C, - Runtime::PANIC_SLICE3_C_U, - Runtime::PANIC_EXTEND_SLICE3_C, - Runtime::PANIC_EXTEND_SLICE3_C_U, - inserter, loc); + this->set_is_error(); + return Type::make_error_type(); } - else if (end != NULL && !end->is_nil_expression()) + const Typed_identifier_list* results = fntype->results(); + if (results == NULL || results->size() < 2) { - if (array_type->is_slice_type()) - Expression::check_bounds(gogo, end, OPERATOR_LE, scap, - Runtime::PANIC_SLICE_ACAP, - Runtime::PANIC_SLICE_ACAP_U, - Runtime::PANIC_EXTEND_SLICE_ACAP, - Runtime::PANIC_EXTEND_SLICE_ACAP_U, - inserter, loc); - else - Expression::check_bounds(gogo, end, OPERATOR_LE, len, - Runtime::PANIC_SLICE_ALEN, - Runtime::PANIC_SLICE_ALEN_U, - Runtime::PANIC_EXTEND_SLICE_ALEN, - Runtime::PANIC_EXTEND_SLICE_ALEN_U, - inserter, loc); - - Expression::check_bounds(gogo, start, OPERATOR_LE, end, - Runtime::PANIC_SLICE_B, - Runtime::PANIC_SLICE_B_U, - Runtime::PANIC_EXTEND_SLICE_B, - Runtime::PANIC_EXTEND_SLICE_B_U, - inserter, loc); + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); + return Type::make_error_type(); } - else if (end != NULL) + Typed_identifier_list::const_iterator pr = results->begin(); + for (unsigned int i = 0; i < this->index_; ++i) { - Expression* start_bound; - if (array_type->is_slice_type()) - start_bound = scap; - else - start_bound = len; - Expression::check_bounds(gogo, start, OPERATOR_LE, start_bound, - Runtime::PANIC_SLICE_B, - Runtime::PANIC_SLICE_B_U, - Runtime::PANIC_EXTEND_SLICE_B, - Runtime::PANIC_EXTEND_SLICE_B_U, - inserter, loc); + if (pr == results->end()) + break; + ++pr; } - else - Expression::check_bounds(gogo, start, OPERATOR_LT, len, - Runtime::PANIC_INDEX, - Runtime::PANIC_INDEX_U, - Runtime::PANIC_EXTEND_INDEX, - Runtime::PANIC_EXTEND_INDEX_U, - inserter, loc); - - return this; + if (pr == results->end()) + { + if (ce->issue_error()) + this->report_error(_("number of results does not match " + "number of values")); + return Type::make_error_type(); + } + return pr->type(); } -// Return whether this expression is addressable. - -bool -Array_index_expression::do_is_addressable() const -{ - // A slice expression is not addressable. - if (this->end_ != NULL) - return false; - - // An index into a slice is addressable. - if (this->array_->type()->is_slice_type()) - return true; +// Check the type. Just make sure that we trigger the warning in +// do_type. - // An index into an array is addressable if the array is - // addressable. - return this->array_->is_addressable(); +void +Call_result_expression::do_check_types(Gogo*) +{ + this->type(); } +// Determine the type. We have nothing to do here, but the 0 result +// needs to pass down to the caller. + void -Array_index_expression::do_address_taken(bool escapes) +Call_result_expression::do_determine_type(Gogo* gogo, const Type_context*) { - // In &x[0], if x is a slice, then x's address is not taken. - if (!this->array_->type()->is_slice_type()) - this->array_->address_taken(escapes); + this->call_->determine_type_no_context(gogo); } -// Get the backend representation for an array index. +// Return the backend representation. We just refer to the temporary set by the +// call expression. We don't do this at lowering time because it makes it +// hard to evaluate the call at the right time. Bexpression* -Array_index_expression::do_get_backend(Translate_context* context) +Call_result_expression::do_get_backend(Translate_context* context) { - Array_type* array_type = this->array_->type()->array_type(); - if (array_type == NULL) + Call_expression* ce = this->call_->call_expression(); + if (ce == NULL) { - go_assert(this->array_->type()->is_error()); + go_assert(this->call_->is_error_expression()); return context->backend()->error_expression(); } - go_assert(!array_type->is_slice_type() - || this->array_->is_multi_eval_safe()); + Temporary_statement* ts = ce->results(); + if (ts == NULL) + { + go_assert(saw_errors()); + return context->backend()->error_expression(); + } + Expression* ref = Expression::make_temporary_reference(ts, this->location()); + ref = Expression::make_field_reference(ref, this->index_, this->location()); + return ref->get_backend(context); +} - Location loc = this->location(); - Gogo* gogo = context->gogo(); +// Dump ast representation for a call result expression. - Type* int_type = Type::lookup_integer_type("int"); - Btype* int_btype = int_type->get_backend(gogo); +void +Call_result_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const +{ + // FIXME: Wouldn't it be better if the call is assigned to a temporary + // (struct) and the fields are referenced instead. + ast_dump_context->ostream() << this->index_ << "@("; + ast_dump_context->dump_expression(this->call_); + ast_dump_context->ostream() << ")"; +} - // Convert the length and capacity to "int". FIXME: Do we need to - // do this? - Bexpression* length = NULL; - if (this->end_ == NULL || this->end_->is_nil_expression()) - { - Expression* len = array_type->get_length(gogo, this->array_); - length = len->get_backend(context); - length = gogo->backend()->convert_expression(int_btype, length, loc); - } +// Make a reference to a single result of a call which returns +// multiple results. - Bexpression* capacity = NULL; - if (this->end_ != NULL) - { - Expression* cap = array_type->get_capacity(gogo, this->array_); - capacity = cap->get_backend(context); - capacity = gogo->backend()->convert_expression(int_btype, capacity, loc); - } +Expression* +Expression::make_call_result(Call_expression* call, unsigned int index) +{ + return new Call_result_expression(call, index); +} - Bexpression* cap_arg = capacity; - if (this->cap_ != NULL) - { - cap_arg = this->cap_->get_backend(context); - cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc); - } +// Class Index_expression. - if (length == NULL) - length = cap_arg; +// Traversal. - if (this->start_->type()->integer_type() == NULL - && !Type::are_convertible(int_type, this->start_->type(), NULL)) +int +Index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->left_, traverse) == TRAVERSE_EXIT + || Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT + || (this->end_ != NULL + && Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) + || (this->cap_ != NULL + && Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT)) + return TRAVERSE_EXIT; + return TRAVERSE_CONTINUE; +} + +// Lower an index expression. This converts the generic index +// expression into an array index, a string index, or a map index. + +Expression* +Index_expression::do_lower(Gogo*, Named_object*, Statement_inserter*, int) +{ + Location location = this->location(); + Expression* left = this->left_; + Expression* start = this->start_; + Expression* end = this->end_; + Expression* cap = this->cap_; + + Type* type = left->type(); + if (type->is_error()) { go_assert(saw_errors()); - return context->backend()->error_expression(); + return Expression::make_error(location); + } + else if (left->is_type_expression()) + { + go_error_at(location, "attempt to index type expression"); + return Expression::make_error(location); } + else if (type->array_type() != NULL) + return Expression::make_array_index(left, start, end, cap, location); + else if (type->points_to() != NULL + && type->points_to()->array_type() != NULL + && !type->points_to()->is_slice_type()) + { + Expression* deref = + Expression::make_dereference(left, NIL_CHECK_DEFAULT, location); - Bexpression* start = this->start_->get_backend(context); - start = gogo->backend()->convert_expression(int_btype, start, loc); + // For an ordinary index into the array, the pointer will be + // dereferenced. For a slice it will not--the resulting slice + // will simply reuse the pointer, which is incorrect if that + // pointer is nil. + if (end != NULL || cap != NULL) + deref->issue_nil_check(); - Bfunction* bfn = context->function()->func_value()->get_decl(); - if (this->end_ == NULL) + return Expression::make_array_index(deref, start, end, cap, location); + } + else if (type->is_string_type()) { - // Simple array indexing. - Bexpression* ret; - if (!array_type->is_slice_type()) - { - Bexpression* array = this->array_->get_backend(context); - ret = gogo->backend()->array_index_expression(array, start, loc); - } - else + if (cap != NULL) + { + go_error_at(location, "invalid 3-index slice of string"); + return Expression::make_error(location); + } + return Expression::make_string_index(left, start, end, location); + } + else if (type->map_type() != NULL) + { + if (end != NULL || cap != NULL) { - Expression* valptr = array_type->get_value_pointer(gogo, - this->array_); - Bexpression* ptr = valptr->get_backend(context); - ptr = gogo->backend()->pointer_offset_expression(ptr, start, loc); - - Type* ele_type = this->array_->type()->array_type()->element_type(); - Btype* ele_btype = ele_type->get_backend(gogo); - ret = gogo->backend()->indirect_expression(ele_btype, ptr, false, - loc); + go_error_at(location, "invalid slice of map"); + return Expression::make_error(location); } - return ret; + return Expression::make_map_index(left, start, location); + } + else if (cap != NULL) + { + go_error_at(location, + "invalid 3-index slice of object that is not a slice"); + return Expression::make_error(location); + } + else if (end != NULL) + { + go_error_at(location, + ("attempt to slice object that is not " + "array, slice, or string")); + return Expression::make_error(location); } - - // Slice expression. - - Bexpression* end; - if (this->end_->is_nil_expression()) - end = length; else { - end = this->end_->get_backend(context); - end = gogo->backend()->convert_expression(int_btype, end, loc); + go_error_at(location, + ("attempt to index object that is not " + "array, slice, string, or map")); + return Expression::make_error(location); } - - Bexpression* result_length = - gogo->backend()->binary_expression(OPERATOR_MINUS, end, start, loc); - - Bexpression* result_capacity = - gogo->backend()->binary_expression(OPERATOR_MINUS, cap_arg, start, loc); - - // If the new capacity is zero, don't change val. Otherwise we can - // get a pointer to the next object in memory, keeping it live - // unnecessarily. When the capacity is zero, the actual pointer - // value doesn't matter. - Bexpression* zero = - Expression::make_integer_ul(0, int_type, loc)->get_backend(context); - Bexpression* cond = - gogo->backend()->binary_expression(OPERATOR_EQEQ, result_capacity, zero, - loc); - Bexpression* offset = gogo->backend()->conditional_expression(bfn, int_btype, - cond, zero, - start, loc); - Expression* valptr = array_type->get_value_pointer(gogo, this->array_); - Bexpression* val = valptr->get_backend(context); - val = gogo->backend()->pointer_offset_expression(val, offset, loc); - - Btype* struct_btype = this->type()->get_backend(gogo); - std::vector init; - init.push_back(val); - init.push_back(result_length); - init.push_back(result_capacity); - - return gogo->backend()->constructor_expression(struct_btype, init, loc); } -// Export an array index expression. +// Write an indexed expression +// (expr[expr:expr:expr], expr[expr:expr] or expr[expr]) to a dump context. void -Array_index_expression::do_export(Export_function_body* efb) const +Index_expression::dump_index_expression(Ast_dump_context* ast_dump_context, + const Expression* expr, + const Expression* start, + const Expression* end, + const Expression* cap) { - efb->write_c_string("("); - this->array_->export_expression(efb); - efb->write_c_string(")["); - - Type* old_context = efb->type_context(); - efb->set_type_context(Type::lookup_integer_type("int")); - - this->start_->export_expression(efb); - if (this->end_ == NULL) - go_assert(this->cap_ == NULL); - else + expr->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "["; + start->dump_expression(ast_dump_context); + if (end != NULL) { - efb->write_c_string(":"); - if (!this->end_->is_nil_expression()) - this->end_->export_expression(efb); - if (this->cap_ != NULL) - { - efb->write_c_string(":"); - this->cap_->export_expression(efb); - } + ast_dump_context->ostream() << ":"; + end->dump_expression(ast_dump_context); } - - efb->set_type_context(old_context); - - efb->write_c_string("]"); + if (cap != NULL) + { + ast_dump_context->ostream() << ":"; + cap->dump_expression(ast_dump_context); + } + ast_dump_context->ostream() << "]"; } -// Dump ast representation for an array index expression. +// Dump ast representation for an index expression. void -Array_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) +Index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { - Index_expression::dump_index_expression(ast_dump_context, this->array_, + Index_expression::dump_index_expression(ast_dump_context, this->left_, this->start_, this->end_, this->cap_); } -// Make an array index expression. END and CAP may be NULL. +// Make an index expression. Expression* -Expression::make_array_index(Expression* array, Expression* start, - Expression* end, Expression* cap, - Location location) +Expression::make_index(Expression* left, Expression* start, Expression* end, + Expression* cap, Location location) { - return new Array_index_expression(array, start, end, cap, location); + return new Index_expression(left, start, end, cap, location); } -// Class String_index_expression. +// Class Array_index_expression. -// String index traversal. +// Array index traversal. int -String_index_expression::do_traverse(Traverse* traverse) +Array_index_expression::do_traverse(Traverse* traverse) { - if (Expression::traverse(&this->string_, traverse) == TRAVERSE_EXIT) + if (Expression::traverse(&this->array_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; @@ -14152,12 +13833,207 @@ String_index_expression::do_traverse(Traverse* traverse) if (Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; } + if (this->cap_ != NULL) + { + if (Expression::traverse(&this->cap_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } return TRAVERSE_CONTINUE; } +// Return the type of an array index. + +Type* +Array_index_expression::do_type() +{ + if (this->type_ == NULL) + { + Array_type* type = this->array_->type()->array_type(); + if (type == NULL) + this->type_ = Type::make_error_type(); + else if (this->end_ == NULL) + this->type_ = type->element_type(); + else if (type->is_slice_type()) + { + // A slice of a slice has the same type as the original + // slice. + this->type_ = this->array_->type()->deref(); + } + else + { + // A slice of an array is a slice. + this->type_ = Type::make_array_type(type->element_type(), NULL); + } + } + return this->type_; +} + +// Set the type of an array index. + +void +Array_index_expression::do_determine_type(Gogo* gogo, const Type_context*) +{ + this->array_->determine_type_no_context(gogo); + + Type_context index_context(Type::lookup_integer_type("int"), false); + this->start_->determine_type(gogo, &index_context); + if (this->end_ != NULL) + this->end_->determine_type(gogo, &index_context); + if (this->cap_ != NULL) + this->cap_->determine_type(gogo, &index_context); +} + +// Check types of an array index. + +void +Array_index_expression::do_check_types(Gogo*) +{ + Numeric_constant nc; + unsigned long v; + if (this->start_->type()->integer_type() == NULL + && !this->start_->type()->is_error() + && (!this->start_->type()->is_abstract() + || !this->start_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("index must be integer")); + if (this->end_ != NULL + && this->end_->type()->integer_type() == NULL + && !this->end_->type()->is_error() + && !this->end_->is_nil_expression() + && !this->end_->is_error_expression() + && (!this->end_->type()->is_abstract() + || !this->end_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice end must be integer")); + if (this->cap_ != NULL + && this->cap_->type()->integer_type() == NULL + && !this->cap_->type()->is_error() + && !this->cap_->is_nil_expression() + && !this->cap_->is_error_expression() + && (!this->cap_->type()->is_abstract() + || !this->cap_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice capacity must be integer")); + + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) + { + go_assert(this->array_->type()->is_error()); + this->set_is_error(); + return; + } + + unsigned int int_bits = + Type::lookup_integer_type("int")->integer_type()->bits(); + + Numeric_constant lvalnc; + mpz_t lval; + bool lval_valid = (array_type->length() != NULL + && array_type->length()->numeric_constant_value(&lvalnc) + && lvalnc.to_int(&lval)); + Numeric_constant inc; + mpz_t ival; + bool ival_valid = false; + if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) + { + ival_valid = true; + if (mpz_sgn(ival) < 0 + || mpz_sizeinbase(ival, 2) >= int_bits + || (lval_valid + && (this->end_ == NULL + ? mpz_cmp(ival, lval) >= 0 + : mpz_cmp(ival, lval) > 0))) + { + go_error_at(this->start_->location(), "array index out of bounds"); + this->set_is_error(); + } + } + if (this->end_ != NULL && !this->end_->is_nil_expression()) + { + Numeric_constant enc; + mpz_t eval; + bool eval_valid = false; + if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) + { + eval_valid = true; + if (mpz_sgn(eval) < 0 + || mpz_sizeinbase(eval, 2) >= int_bits + || (lval_valid && mpz_cmp(eval, lval) > 0)) + { + go_error_at(this->end_->location(), "array index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, eval) > 0) + this->report_error(_("inverted slice range")); + } + + Numeric_constant cnc; + mpz_t cval; + if (this->cap_ != NULL + && this->cap_->numeric_constant_value(&cnc) && cnc.to_int(&cval)) + { + if (mpz_sgn(cval) < 0 + || mpz_sizeinbase(cval, 2) >= int_bits + || (lval_valid && mpz_cmp(cval, lval) > 0)) + { + go_error_at(this->cap_->location(), "array index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, cval) > 0) + { + go_error_at(this->cap_->location(), + "invalid slice index: capacity less than start"); + this->set_is_error(); + } + else if (eval_valid && mpz_cmp(eval, cval) > 0) + { + go_error_at(this->cap_->location(), + "invalid slice index: capacity less than length"); + this->set_is_error(); + } + mpz_clear(cval); + } + + if (eval_valid) + mpz_clear(eval); + } + if (ival_valid) + mpz_clear(ival); + if (lval_valid) + mpz_clear(lval); + + // A slice of an array requires an addressable array. A slice of a + // slice is always possible. + if (this->end_ != NULL && !array_type->is_slice_type()) + { + if (!this->array_->is_addressable()) + this->report_error(_("slice of unaddressable value")); + else + // Set the array address taken but not escape. The escape + // analysis will make it escape to heap when needed. + this->array_->address_taken(false); + } +} + +// The subexpressions of an array index must be evaluated in order. +// If this is indexing into an array, rather than a slice, then only +// the index should be evaluated. Since this is called for values on +// the left hand side of an assigment, evaluating the array, meaning +// copying the array, will cause a different array to be modified. + +bool +Array_index_expression::do_must_eval_subexpressions_in_order( + int* skip) const +{ + *skip = this->array_->type()->is_slice_type() ? 0 : 1; + return true; +} + +// Flatten array indexing: add temporary variables and bounds checks. + Expression* -String_index_expression::do_flatten(Gogo* gogo, Named_object*, - Statement_inserter* inserter) +Array_index_expression::do_flatten(Gogo* gogo, Named_object*, + Statement_inserter* inserter) { if (this->is_flattened_) return this; @@ -14168,27 +14044,37 @@ String_index_expression::do_flatten(Gogo* gogo, Named_object*, if (this->is_error_expression()) return Expression::make_error(loc); - Expression* string = this->string_; + Expression* array = this->array_; Expression* start = this->start_; Expression* end = this->end_; - if (string->is_error_expression() - || string->type()->is_error_type() + Expression* cap = this->cap_; + if (array->is_error_expression() + || array->type()->is_error_type() || start->is_error_expression() || start->type()->is_error_type() || (end != NULL - && (end->is_error_expression() || end->type()->is_error_type()))) + && (end->is_error_expression() || end->type()->is_error_type())) + || (cap != NULL + && (cap->is_error_expression() || cap->type()->is_error_type()))) + { + go_assert(saw_errors()); + return Expression::make_error(loc); + } + + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) { go_assert(saw_errors()); return Expression::make_error(loc); } Temporary_statement* temp; - if (!string->is_multi_eval_safe()) + if (array_type->is_slice_type() && !array->is_multi_eval_safe()) { - temp = Statement::make_temporary(NULL, string, loc); + temp = Statement::make_temporary(NULL, array, loc); inserter->insert(temp); - this->string_ = Expression::make_temporary_reference(temp, loc); - string = this->string_; + this->array_ = Expression::make_temporary_reference(temp, loc); + array = this->array_; } if (!start->is_multi_eval_safe()) { @@ -14206,24 +14092,96 @@ String_index_expression::do_flatten(Gogo* gogo, Named_object*, this->end_ = Expression::make_temporary_reference(temp, loc); end = this->end_; } + if (cap != NULL && !cap->is_multi_eval_safe()) + { + temp = Statement::make_temporary(NULL, cap, loc); + inserter->insert(temp); + this->cap_ = Expression::make_temporary_reference(temp, loc); + cap = this->cap_; + } - Expression* len = Expression::make_string_info(string->copy(), - STRING_INFO_LENGTH, loc); - temp = Statement::make_temporary(NULL, len, loc); - inserter->insert(temp); - len = Expression::make_temporary_reference(temp, loc); + if (!this->needs_bounds_check_) + return this; + + Expression* len; + if (!array_type->is_slice_type()) + { + len = array_type->get_length(gogo, this->array_); + go_assert(len->is_constant()); + } + else + { + len = array_type->get_length(gogo, this->array_->copy()); + temp = Statement::make_temporary(NULL, len, loc); + inserter->insert(temp); + len = Expression::make_temporary_reference(temp, loc); + } + + Expression* scap = NULL; + if (array_type->is_slice_type()) + { + scap = array_type->get_capacity(gogo, this->array_->copy()); + temp = Statement::make_temporary(NULL, scap, loc); + inserter->insert(temp); + scap = Expression::make_temporary_reference(temp, loc); + } // The order of bounds checks here matches the order used by the gc // compiler, as tested by issue30116[u].go. - if (end != NULL && !end->is_nil_expression()) + if (cap != NULL) { - Expression::check_bounds(gogo, end, OPERATOR_LE, len, - Runtime::PANIC_SLICE_ALEN, - Runtime::PANIC_SLICE_ALEN_U, - Runtime::PANIC_EXTEND_SLICE_ALEN, - Runtime::PANIC_EXTEND_SLICE_ALEN_U, + if (array_type->is_slice_type()) + Expression::check_bounds(gogo, cap, OPERATOR_LE, scap, + Runtime::PANIC_SLICE3_ACAP, + Runtime::PANIC_SLICE3_ACAP_U, + Runtime::PANIC_EXTEND_SLICE3_ACAP, + Runtime::PANIC_EXTEND_SLICE3_ACAP_U, + inserter, loc); + else + Expression::check_bounds(gogo, cap, OPERATOR_LE, len, + Runtime::PANIC_SLICE3_ALEN, + Runtime::PANIC_SLICE3_ALEN_U, + Runtime::PANIC_EXTEND_SLICE3_ALEN, + Runtime::PANIC_EXTEND_SLICE3_ALEN_U, + inserter, loc); + + Expression* start_bound = cap; + if (end != NULL && !end->is_nil_expression()) + { + Expression::check_bounds(gogo, end, OPERATOR_LE, cap, + Runtime::PANIC_SLICE3_B, + Runtime::PANIC_SLICE3_B_U, + Runtime::PANIC_EXTEND_SLICE3_B, + Runtime::PANIC_EXTEND_SLICE3_B_U, + inserter, loc); + start_bound = end; + } + + Expression::check_bounds(gogo, start, OPERATOR_LE, start_bound, + Runtime::PANIC_SLICE3_C, + Runtime::PANIC_SLICE3_C_U, + Runtime::PANIC_EXTEND_SLICE3_C, + Runtime::PANIC_EXTEND_SLICE3_C_U, inserter, loc); + } + else if (end != NULL && !end->is_nil_expression()) + { + if (array_type->is_slice_type()) + Expression::check_bounds(gogo, end, OPERATOR_LE, scap, + Runtime::PANIC_SLICE_ACAP, + Runtime::PANIC_SLICE_ACAP_U, + Runtime::PANIC_EXTEND_SLICE_ACAP, + Runtime::PANIC_EXTEND_SLICE_ACAP_U, + inserter, loc); + else + Expression::check_bounds(gogo, end, OPERATOR_LE, len, + Runtime::PANIC_SLICE_ALEN, + Runtime::PANIC_SLICE_ALEN_U, + Runtime::PANIC_EXTEND_SLICE_ALEN, + Runtime::PANIC_EXTEND_SLICE_ALEN_U, + inserter, loc); + Expression::check_bounds(gogo, start, OPERATOR_LE, end, Runtime::PANIC_SLICE_B, Runtime::PANIC_SLICE_B_U, @@ -14232,12 +14190,19 @@ String_index_expression::do_flatten(Gogo* gogo, Named_object*, inserter, loc); } else if (end != NULL) - Expression::check_bounds(gogo, start, OPERATOR_LE, len, - Runtime::PANIC_SLICE_B, - Runtime::PANIC_SLICE_B_U, - Runtime::PANIC_EXTEND_SLICE_B, - Runtime::PANIC_EXTEND_SLICE_B_U, - inserter, loc); + { + Expression* start_bound; + if (array_type->is_slice_type()) + start_bound = scap; + else + start_bound = len; + Expression::check_bounds(gogo, start, OPERATOR_LE, start_bound, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + } else Expression::check_bounds(gogo, start, OPERATOR_LT, len, Runtime::PANIC_INDEX, @@ -14249,108 +14214,80 @@ String_index_expression::do_flatten(Gogo* gogo, Named_object*, return this; } -// Return the type of a string index. +// Return whether this expression is addressable. -Type* -String_index_expression::do_type() +bool +Array_index_expression::do_is_addressable() const { - if (this->end_ == NULL) - return Type::lookup_integer_type("byte"); - else - return this->string_->type(); -} + // A slice expression is not addressable. + if (this->end_ != NULL) + return false; -// Determine the type of a string index. + // An index into a slice is addressable. + if (this->array_->type()->is_slice_type()) + return true; + + // An index into an array is addressable if the array is + // addressable. + return this->array_->is_addressable(); +} void -String_index_expression::do_determine_type(Gogo* gogo, const Type_context*) +Array_index_expression::do_address_taken(bool escapes) { - this->string_->determine_type_no_context(gogo); - - Type_context index_context(Type::lookup_integer_type("int"), false); - this->start_->determine_type(gogo, &index_context); - if (this->end_ != NULL) - this->end_->determine_type(gogo, &index_context); + // In &x[0], if x is a slice, then x's address is not taken. + if (!this->array_->type()->is_slice_type()) + this->array_->address_taken(escapes); } -// Check types of a string index. +// Get the backend representation for an array index. -void -String_index_expression::do_check_types(Gogo*) +Bexpression* +Array_index_expression::do_get_backend(Translate_context* context) { - Numeric_constant nc; - unsigned long v; - if (this->start_->type()->integer_type() == NULL - && !this->start_->type()->is_error() - && (!this->start_->type()->is_abstract() - || !this->start_->numeric_constant_value(&nc) - || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) - this->report_error(_("index must be integer")); - if (this->end_ != NULL - && this->end_->type()->integer_type() == NULL - && !this->end_->type()->is_error() - && !this->end_->is_nil_expression() - && !this->end_->is_error_expression() - && (!this->end_->type()->is_abstract() - || !this->end_->numeric_constant_value(&nc) - || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) - this->report_error(_("slice end must be integer")); + Array_type* array_type = this->array_->type()->array_type(); + if (array_type == NULL) + { + go_assert(this->array_->type()->is_error()); + return context->backend()->error_expression(); + } + go_assert(!array_type->is_slice_type() + || this->array_->is_multi_eval_safe()); - std::string sval; - bool sval_valid = this->string_->string_constant_value(&sval); + Location loc = this->location(); + Gogo* gogo = context->gogo(); - Numeric_constant inc; - mpz_t ival; - bool ival_valid = false; - if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) + Type* int_type = Type::lookup_integer_type("int"); + Btype* int_btype = int_type->get_backend(gogo); + + // Convert the length and capacity to "int". FIXME: Do we need to + // do this? + Bexpression* length = NULL; + if (this->end_ == NULL || this->end_->is_nil_expression()) { - ival_valid = true; - if (mpz_sgn(ival) < 0 - || (sval_valid - && (this->end_ == NULL - ? mpz_cmp_ui(ival, sval.length()) >= 0 - : mpz_cmp_ui(ival, sval.length()) > 0))) - { - go_error_at(this->start_->location(), "string index out of bounds"); - this->set_is_error(); - } + Expression* len = array_type->get_length(gogo, this->array_); + length = len->get_backend(context); + length = gogo->backend()->convert_expression(int_btype, length, loc); } - if (this->end_ != NULL && !this->end_->is_nil_expression()) + + Bexpression* capacity = NULL; + if (this->end_ != NULL) { - Numeric_constant enc; - mpz_t eval; - if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) - { - if (mpz_sgn(eval) < 0 - || (sval_valid && mpz_cmp_ui(eval, sval.length()) > 0)) - { - go_error_at(this->end_->location(), "string index out of bounds"); - this->set_is_error(); - } - else if (ival_valid && mpz_cmp(ival, eval) > 0) - this->report_error(_("inverted slice range")); - mpz_clear(eval); - } + Expression* cap = array_type->get_capacity(gogo, this->array_); + capacity = cap->get_backend(context); + capacity = gogo->backend()->convert_expression(int_btype, capacity, loc); } - if (ival_valid) - mpz_clear(ival); -} - -// Get the backend representation for a string index. -Bexpression* -String_index_expression::do_get_backend(Translate_context* context) -{ - Location loc = this->location(); - Gogo* gogo = context->gogo(); + Bexpression* cap_arg = capacity; + if (this->cap_ != NULL) + { + cap_arg = this->cap_->get_backend(context); + cap_arg = gogo->backend()->convert_expression(int_btype, cap_arg, loc); + } - Type* int_type = Type::lookup_integer_type("int"); + if (length == NULL) + length = cap_arg; - // It is possible that an error occurred earlier because the start index - // cannot be represented as an integer type. In this case, we shouldn't - // try casting the starting index into an integer since - // Type_conversion_expression will fail to get the backend representation. - // FIXME. if (this->start_->type()->integer_type() == NULL && !Type::are_convertible(int_type, this->start_->type(), NULL)) { @@ -14358,82 +14295,101 @@ String_index_expression::do_get_backend(Translate_context* context) return context->backend()->error_expression(); } - go_assert(this->string_->is_multi_eval_safe()); - go_assert(this->start_->is_multi_eval_safe()); + Bexpression* start = this->start_->get_backend(context); + start = gogo->backend()->convert_expression(int_btype, start, loc); - Expression* start = Expression::make_cast(int_type, this->start_, loc); Bfunction* bfn = context->function()->func_value()->get_decl(); - - Expression* length = - Expression::make_string_info(this->string_, STRING_INFO_LENGTH, loc); - Expression* bytes = - Expression::make_string_info(this->string_, STRING_INFO_DATA, loc); - - Bexpression* bstart = start->get_backend(context); - Bexpression* ptr = bytes->get_backend(context); - if (this->end_ == NULL) { - ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc); - Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo); - return gogo->backend()->indirect_expression(ubtype, ptr, false, loc); + // Simple array indexing. + Bexpression* ret; + if (!array_type->is_slice_type()) + { + Bexpression* array = this->array_->get_backend(context); + ret = gogo->backend()->array_index_expression(array, start, loc); + } + else + { + Expression* valptr = array_type->get_value_pointer(gogo, + this->array_); + Bexpression* ptr = valptr->get_backend(context); + ptr = gogo->backend()->pointer_offset_expression(ptr, start, loc); + + Type* ele_type = this->array_->type()->array_type()->element_type(); + Btype* ele_btype = ele_type->get_backend(gogo); + ret = gogo->backend()->indirect_expression(ele_btype, ptr, false, + loc); + } + return ret; } - Expression* end = NULL; + // Slice expression. + + Bexpression* end; if (this->end_->is_nil_expression()) end = length; else { - go_assert(this->end_->is_multi_eval_safe()); - end = Expression::make_cast(int_type, this->end_, loc); + end = this->end_->get_backend(context); + end = gogo->backend()->convert_expression(int_btype, end, loc); } - end = end->copy(); - Bexpression* bend = end->get_backend(context); - Bexpression* new_length = - gogo->backend()->binary_expression(OPERATOR_MINUS, bend, bstart, loc); + Bexpression* result_length = + gogo->backend()->binary_expression(OPERATOR_MINUS, end, start, loc); - // If the new length is zero, don't change pointer. Otherwise we can + Bexpression* result_capacity = + gogo->backend()->binary_expression(OPERATOR_MINUS, cap_arg, start, loc); + + // If the new capacity is zero, don't change val. Otherwise we can // get a pointer to the next object in memory, keeping it live - // unnecessarily. When the length is zero, the actual pointer + // unnecessarily. When the capacity is zero, the actual pointer // value doesn't matter. - Btype* int_btype = int_type->get_backend(gogo); Bexpression* zero = Expression::make_integer_ul(0, int_type, loc)->get_backend(context); Bexpression* cond = - gogo->backend()->binary_expression(OPERATOR_EQEQ, new_length, zero, - loc); - Bexpression* offset = - gogo->backend()->conditional_expression(bfn, int_btype, cond, zero, - bstart, loc); - - ptr = gogo->backend()->pointer_offset_expression(ptr, offset, loc); + gogo->backend()->binary_expression(OPERATOR_EQEQ, result_capacity, zero, + loc); + Bexpression* offset = gogo->backend()->conditional_expression(bfn, int_btype, + cond, zero, + start, loc); + Expression* valptr = array_type->get_value_pointer(gogo, this->array_); + Bexpression* val = valptr->get_backend(context); + val = gogo->backend()->pointer_offset_expression(val, offset, loc); - Btype* str_btype = this->type()->get_backend(gogo); + Btype* struct_btype = this->type()->get_backend(gogo); std::vector init; - init.push_back(ptr); - init.push_back(new_length); - return gogo->backend()->constructor_expression(str_btype, init, loc); + init.push_back(val); + init.push_back(result_length); + init.push_back(result_capacity); + + return gogo->backend()->constructor_expression(struct_btype, init, loc); } -// Export a string index expression. +// Export an array index expression. void -String_index_expression::do_export(Export_function_body* efb) const +Array_index_expression::do_export(Export_function_body* efb) const { efb->write_c_string("("); - this->string_->export_expression(efb); + this->array_->export_expression(efb); efb->write_c_string(")["); Type* old_context = efb->type_context(); efb->set_type_context(Type::lookup_integer_type("int")); this->start_->export_expression(efb); - if (this->end_ != NULL) + if (this->end_ == NULL) + go_assert(this->cap_ == NULL); + else { efb->write_c_string(":"); if (!this->end_->is_nil_expression()) this->end_->export_expression(efb); + if (this->cap_ != NULL) + { + efb->write_c_string(":"); + this->cap_->export_expression(efb); + } } efb->set_type_context(old_context); @@ -14441,1109 +14397,1153 @@ String_index_expression::do_export(Export_function_body* efb) const efb->write_c_string("]"); } -// Dump ast representation for a string index expression. +// Dump ast representation for an array index expression. void -String_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) +Array_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { - Index_expression::dump_index_expression(ast_dump_context, this->string_, - this->start_, this->end_, NULL); + Index_expression::dump_index_expression(ast_dump_context, this->array_, + this->start_, this->end_, this->cap_); } -// Make a string index expression. END may be NULL. +// Make an array index expression. END and CAP may be NULL. Expression* -Expression::make_string_index(Expression* string, Expression* start, - Expression* end, Location location) +Expression::make_array_index(Expression* array, Expression* start, + Expression* end, Expression* cap, + Location location) { - return new String_index_expression(string, start, end, location); + return new Array_index_expression(array, start, end, cap, location); } -// Class Map_index. - -// Get the type of the map. - -Map_type* -Map_index_expression::get_map_type() const -{ - Map_type* mt = this->map_->type()->map_type(); - if (mt == NULL) - go_assert(saw_errors()); - return mt; -} +// Class String_index_expression. -// Map index traversal. +// String index traversal. int -Map_index_expression::do_traverse(Traverse* traverse) +String_index_expression::do_traverse(Traverse* traverse) { - if (Expression::traverse(&this->map_, traverse) == TRAVERSE_EXIT) + if (Expression::traverse(&this->string_, traverse) == TRAVERSE_EXIT) return TRAVERSE_EXIT; - return Expression::traverse(&this->index_, traverse); + if (Expression::traverse(&this->start_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + if (this->end_ != NULL) + { + if (Expression::traverse(&this->end_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + return TRAVERSE_CONTINUE; } -// We need to pass in a pointer to the key, so flatten the index into a -// temporary variable if it isn't already. The value pointer will be -// dereferenced and checked for nil, so flatten into a temporary to avoid -// recomputation. - Expression* -Map_index_expression::do_flatten(Gogo* gogo, Named_object*, - Statement_inserter* inserter) +String_index_expression::do_flatten(Gogo* gogo, Named_object*, + Statement_inserter* inserter) { + if (this->is_flattened_) + return this; + this->is_flattened_ = true; + Location loc = this->location(); - Map_type* mt = this->get_map_type(); - if (this->index()->is_error_expression() - || this->index()->type()->is_error_type() - || mt->is_error_type()) + + if (this->is_error_expression()) + return Expression::make_error(loc); + + Expression* string = this->string_; + Expression* start = this->start_; + Expression* end = this->end_; + if (string->is_error_expression() + || string->type()->is_error_type() + || start->is_error_expression() + || start->type()->is_error_type() + || (end != NULL + && (end->is_error_expression() || end->type()->is_error_type()))) { go_assert(saw_errors()); return Expression::make_error(loc); } - // Avoid copy for string([]byte) conversions used in map keys. - // mapaccess doesn't keep the reference, so this is safe. - Type_conversion_expression* ce = this->index_->conversion_expression(); - if (ce != NULL && ce->type()->is_string_type() - && ce->expr()->type()->is_slice_type()) - ce->set_no_copy(true); - - if (!Type::are_identical(mt->key_type(), this->index_->type(), - Type::COMPARE_ERRORS | Type::COMPARE_TAGS, - NULL)) + Temporary_statement* temp; + if (!string->is_multi_eval_safe()) { - if (this->index_->type()->interface_type() != NULL - && !this->index_->is_multi_eval_safe()) - { - Temporary_statement* temp = - Statement::make_temporary(NULL, this->index_, loc); - inserter->insert(temp); - this->index_ = Expression::make_temporary_reference(temp, loc); - } - this->index_ = Expression::convert_for_assignment(gogo, mt->key_type(), - this->index_, loc); + temp = Statement::make_temporary(NULL, string, loc); + inserter->insert(temp); + this->string_ = Expression::make_temporary_reference(temp, loc); + string = this->string_; } - - if (!this->index_->is_multi_eval_safe()) + if (!start->is_multi_eval_safe()) { - Temporary_statement* temp = Statement::make_temporary(NULL, this->index_, - loc); + temp = Statement::make_temporary(NULL, start, loc); inserter->insert(temp); - this->index_ = Expression::make_temporary_reference(temp, loc); + this->start_ = Expression::make_temporary_reference(temp, loc); + start = this->start_; } - - if (this->value_pointer_ == NULL) - this->get_value_pointer(gogo); - if (this->value_pointer_->is_error_expression() - || this->value_pointer_->type()->is_error_type()) - return Expression::make_error(loc); - if (!this->value_pointer_->is_multi_eval_safe()) + if (end != NULL + && !end->is_nil_expression() + && !end->is_multi_eval_safe()) { - Temporary_statement* temp = - Statement::make_temporary(NULL, this->value_pointer_, loc); + temp = Statement::make_temporary(NULL, end, loc); inserter->insert(temp); - this->value_pointer_ = Expression::make_temporary_reference(temp, loc); + this->end_ = Expression::make_temporary_reference(temp, loc); + end = this->end_; + } + + Expression* len = Expression::make_string_info(string->copy(), + STRING_INFO_LENGTH, loc); + temp = Statement::make_temporary(NULL, len, loc); + inserter->insert(temp); + len = Expression::make_temporary_reference(temp, loc); + + // The order of bounds checks here matches the order used by the gc + // compiler, as tested by issue30116[u].go. + + if (end != NULL && !end->is_nil_expression()) + { + Expression::check_bounds(gogo, end, OPERATOR_LE, len, + Runtime::PANIC_SLICE_ALEN, + Runtime::PANIC_SLICE_ALEN_U, + Runtime::PANIC_EXTEND_SLICE_ALEN, + Runtime::PANIC_EXTEND_SLICE_ALEN_U, + inserter, loc); + Expression::check_bounds(gogo, start, OPERATOR_LE, end, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); } + else if (end != NULL) + Expression::check_bounds(gogo, start, OPERATOR_LE, len, + Runtime::PANIC_SLICE_B, + Runtime::PANIC_SLICE_B_U, + Runtime::PANIC_EXTEND_SLICE_B, + Runtime::PANIC_EXTEND_SLICE_B_U, + inserter, loc); + else + Expression::check_bounds(gogo, start, OPERATOR_LT, len, + Runtime::PANIC_INDEX, + Runtime::PANIC_INDEX_U, + Runtime::PANIC_EXTEND_INDEX, + Runtime::PANIC_EXTEND_INDEX_U, + inserter, loc); return this; } -// Return the type of a map index. +// Return the type of a string index. Type* -Map_index_expression::do_type() +String_index_expression::do_type() { - Map_type* mt = this->get_map_type(); - if (mt == NULL) - return Type::make_error_type(); - return mt->val_type(); + if (this->end_ == NULL) + return Type::lookup_integer_type("byte"); + else + return this->string_->type(); } -// Fix the type of a map index. +// Determine the type of a string index. void -Map_index_expression::do_determine_type(Gogo* gogo, const Type_context*) +String_index_expression::do_determine_type(Gogo* gogo, const Type_context*) { - this->map_->determine_type_no_context(gogo); - Map_type* mt = this->get_map_type(); - Type* key_type = mt == NULL ? NULL : mt->key_type(); - Type_context subcontext(key_type, false); - this->index_->determine_type(gogo, &subcontext); + this->string_->determine_type_no_context(gogo); + + Type_context index_context(Type::lookup_integer_type("int"), false); + this->start_->determine_type(gogo, &index_context); + if (this->end_ != NULL) + this->end_->determine_type(gogo, &index_context); } -// Check types of a map index. +// Check types of a string index. void -Map_index_expression::do_check_types(Gogo*) +String_index_expression::do_check_types(Gogo*) { - std::string reason; - Map_type* mt = this->get_map_type(); - if (mt == NULL) - return; - if (!Type::are_assignable(mt->key_type(), this->index_->type(), &reason)) + Numeric_constant nc; + unsigned long v; + if (this->start_->type()->integer_type() == NULL + && !this->start_->type()->is_error() + && (!this->start_->type()->is_abstract() + || !this->start_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("index must be integer")); + if (this->end_ != NULL + && this->end_->type()->integer_type() == NULL + && !this->end_->type()->is_error() + && !this->end_->is_nil_expression() + && !this->end_->is_error_expression() + && (!this->end_->type()->is_abstract() + || !this->end_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_NOTINT)) + this->report_error(_("slice end must be integer")); + + std::string sval; + bool sval_valid = this->string_->string_constant_value(&sval); + + Numeric_constant inc; + mpz_t ival; + bool ival_valid = false; + if (this->start_->numeric_constant_value(&inc) && inc.to_int(&ival)) { - if (reason.empty()) - this->report_error(_("incompatible type for map index")); - else + ival_valid = true; + if (mpz_sgn(ival) < 0 + || (sval_valid + && (this->end_ == NULL + ? mpz_cmp_ui(ival, sval.length()) >= 0 + : mpz_cmp_ui(ival, sval.length()) > 0))) { - go_error_at(this->location(), "incompatible type for map index (%s)", - reason.c_str()); + go_error_at(this->start_->location(), "string index out of bounds"); this->set_is_error(); } } + if (this->end_ != NULL && !this->end_->is_nil_expression()) + { + Numeric_constant enc; + mpz_t eval; + if (this->end_->numeric_constant_value(&enc) && enc.to_int(&eval)) + { + if (mpz_sgn(eval) < 0 + || (sval_valid && mpz_cmp_ui(eval, sval.length()) > 0)) + { + go_error_at(this->end_->location(), "string index out of bounds"); + this->set_is_error(); + } + else if (ival_valid && mpz_cmp(ival, eval) > 0) + this->report_error(_("inverted slice range")); + mpz_clear(eval); + } + } + if (ival_valid) + mpz_clear(ival); } -// Add explicit type conversions. +// Get the backend representation for a string index. -void -Map_index_expression::do_add_conversions() +Bexpression* +String_index_expression::do_get_backend(Translate_context* context) { - Map_type* mt = this->get_map_type(); - if (mt == NULL) - return; - Type* lt = mt->key_type(); - Type* rt = this->index_->type(); - if (!Type::are_identical(lt, rt, 0, NULL) - && lt->interface_type() != NULL) - this->index_ = Expression::make_cast(lt, this->index_, this->location()); -} + Location loc = this->location(); + Gogo* gogo = context->gogo(); -// Get the backend representation for a map index. + Type* int_type = Type::lookup_integer_type("int"); -Bexpression* -Map_index_expression::do_get_backend(Translate_context* context) -{ - Map_type* type = this->get_map_type(); - if (type == NULL) + // It is possible that an error occurred earlier because the start index + // cannot be represented as an integer type. In this case, we shouldn't + // try casting the starting index into an integer since + // Type_conversion_expression will fail to get the backend representation. + // FIXME. + if (this->start_->type()->integer_type() == NULL + && !Type::are_convertible(int_type, this->start_->type(), NULL)) { go_assert(saw_errors()); return context->backend()->error_expression(); } - go_assert(this->value_pointer_ != NULL - && this->value_pointer_->is_multi_eval_safe()); + go_assert(this->string_->is_multi_eval_safe()); + go_assert(this->start_->is_multi_eval_safe()); - Expression* val = Expression::make_dereference(this->value_pointer_, - NIL_CHECK_NOT_NEEDED, - this->location()); - return val->get_backend(context); -} + Expression* start = Expression::make_cast(int_type, this->start_, loc); + Bfunction* bfn = context->function()->func_value()->get_decl(); -// Get an expression for the map index. This returns an expression -// that evaluates to a pointer to a value. If the key is not in the -// map, the pointer will point to a zero value. + Expression* length = + Expression::make_string_info(this->string_, STRING_INFO_LENGTH, loc); + Expression* bytes = + Expression::make_string_info(this->string_, STRING_INFO_DATA, loc); -Expression* -Map_index_expression::get_value_pointer(Gogo* gogo) -{ - if (this->value_pointer_ == NULL) + Bexpression* bstart = start->get_backend(context); + Bexpression* ptr = bytes->get_backend(context); + + if (this->end_ == NULL) { - Map_type* type = this->get_map_type(); - if (type == NULL) - { - go_assert(saw_errors()); - return Expression::make_error(this->location()); - } + ptr = gogo->backend()->pointer_offset_expression(ptr, bstart, loc); + Btype* ubtype = Type::lookup_integer_type("uint8")->get_backend(gogo); + return gogo->backend()->indirect_expression(ubtype, ptr, false, loc); + } - Location loc = this->location(); - Expression* map_ref = this->map_; + Expression* end = NULL; + if (this->end_->is_nil_expression()) + end = length; + else + { + go_assert(this->end_->is_multi_eval_safe()); + end = Expression::make_cast(int_type, this->end_, loc); + } - Expression* index_ptr = Expression::make_unary(OPERATOR_AND, - this->index_, - loc); + end = end->copy(); + Bexpression* bend = end->get_backend(context); + Bexpression* new_length = + gogo->backend()->binary_expression(OPERATOR_MINUS, bend, bstart, loc); - Expression* type_expr = Expression::make_type_descriptor(type, loc); - Expression* zero = type->fat_zero_value(gogo); - Expression* map_index; - if (zero == NULL) - { - Runtime::Function code; - Expression* key; - switch (type->algorithm(gogo)) - { - case Map_type::MAP_ALG_FAST32: - case Map_type::MAP_ALG_FAST32PTR: - { - Type* uint32_type = Type::lookup_integer_type("uint32"); - Type* uint32_ptr_type = Type::make_pointer_type(uint32_type); - key = Expression::make_unsafe_cast(uint32_ptr_type, index_ptr, - loc); - key = Expression::make_dereference(key, NIL_CHECK_NOT_NEEDED, - loc); - code = Runtime::MAPACCESS1_FAST32; - break; - } - case Map_type::MAP_ALG_FAST64: - case Map_type::MAP_ALG_FAST64PTR: - { - Type* uint64_type = Type::lookup_integer_type("uint64"); - Type* uint64_ptr_type = Type::make_pointer_type(uint64_type); - key = Expression::make_unsafe_cast(uint64_ptr_type, index_ptr, - loc); - key = Expression::make_dereference(key, NIL_CHECK_NOT_NEEDED, - loc); - code = Runtime::MAPACCESS1_FAST64; - break; - } - case Map_type::MAP_ALG_FASTSTR: - key = this->index_; - code = Runtime::MAPACCESS1_FASTSTR; - break; - default: - key = index_ptr; - code = Runtime::MAPACCESS1; - break; - } - map_index = Runtime::make_call(gogo, code, loc, 3, - type_expr, map_ref, key); - } - else - map_index = Runtime::make_call(gogo, Runtime::MAPACCESS1_FAT, loc, 4, - type_expr, map_ref, index_ptr, zero); + // If the new length is zero, don't change pointer. Otherwise we can + // get a pointer to the next object in memory, keeping it live + // unnecessarily. When the length is zero, the actual pointer + // value doesn't matter. + Btype* int_btype = int_type->get_backend(gogo); + Bexpression* zero = + Expression::make_integer_ul(0, int_type, loc)->get_backend(context); + Bexpression* cond = + gogo->backend()->binary_expression(OPERATOR_EQEQ, new_length, zero, + loc); + Bexpression* offset = + gogo->backend()->conditional_expression(bfn, int_btype, cond, zero, + bstart, loc); - Type* val_type = type->val_type(); - this->value_pointer_ = - Expression::make_unsafe_cast(Type::make_pointer_type(val_type), - map_index, this->location()); - } + ptr = gogo->backend()->pointer_offset_expression(ptr, offset, loc); - return this->value_pointer_; + Btype* str_btype = this->type()->get_backend(gogo); + std::vector init; + init.push_back(ptr); + init.push_back(new_length); + return gogo->backend()->constructor_expression(str_btype, init, loc); } -// Export a map index expression. +// Export a string index expression. void -Map_index_expression::do_export(Export_function_body* efb) const +String_index_expression::do_export(Export_function_body* efb) const { efb->write_c_string("("); - this->map_->export_expression(efb); + this->string_->export_expression(efb); efb->write_c_string(")["); Type* old_context = efb->type_context(); - efb->set_type_context(this->get_map_type()->key_type()); + efb->set_type_context(Type::lookup_integer_type("int")); - this->index_->export_expression(efb); + this->start_->export_expression(efb); + if (this->end_ != NULL) + { + efb->write_c_string(":"); + if (!this->end_->is_nil_expression()) + this->end_->export_expression(efb); + } efb->set_type_context(old_context); efb->write_c_string("]"); } -// Dump ast representation for a map index expression +// Dump ast representation for a string index expression. void -Map_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) +String_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const { - Index_expression::dump_index_expression(ast_dump_context, this->map_, - this->index_, NULL, NULL); + Index_expression::dump_index_expression(ast_dump_context, this->string_, + this->start_, this->end_, NULL); } -// Make a map index expression. +// Make a string index expression. END may be NULL. -Map_index_expression* -Expression::make_map_index(Expression* map, Expression* index, - Location location) +Expression* +Expression::make_string_index(Expression* string, Expression* start, + Expression* end, Location location) { - return new Map_index_expression(map, index, location); + return new String_index_expression(string, start, end, location); } -// Class Field_reference_expression. +// Class Map_index. -// Lower a field reference expression. There is nothing to lower, but -// this is where we generate the tracking information for fields with -// the magic go:"track" tag. +// Get the type of the map. -Expression* -Field_reference_expression::do_lower(Gogo* gogo, Named_object* function, - Statement_inserter* inserter, int) +Map_type* +Map_index_expression::get_map_type() const { - Struct_type* struct_type = this->expr_->type()->struct_type(); - if (struct_type == NULL) - { - // Error will be reported elsewhere. - return this; - } - const Struct_field* field = struct_type->field(this->field_index_); - if (field == NULL) - return this; - if (!field->has_tag()) - return this; - if (field->tag().find("go:\"track\"") == std::string::npos) - return this; + Map_type* mt = this->map_->type()->map_type(); + if (mt == NULL) + go_assert(saw_errors()); + return mt; +} - // References from functions generated by the compiler don't count. - if (function != NULL && function->func_value()->is_type_specific_function()) - return this; +// Map index traversal. - // We have found a reference to a tracked field. Build a call to - // the runtime function __go_fieldtrack with a string that describes - // the field. FIXME: We should only call this once per referenced - // field per function, not once for each reference to the field. +int +Map_index_expression::do_traverse(Traverse* traverse) +{ + if (Expression::traverse(&this->map_, traverse) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + return Expression::traverse(&this->index_, traverse); +} - if (this->called_fieldtrack_) - return this; - this->called_fieldtrack_ = true; +// We need to pass in a pointer to the key, so flatten the index into a +// temporary variable if it isn't already. The value pointer will be +// dereferenced and checked for nil, so flatten into a temporary to avoid +// recomputation. +Expression* +Map_index_expression::do_flatten(Gogo* gogo, Named_object*, + Statement_inserter* inserter) +{ Location loc = this->location(); - - std::string s = "fieldtrack \""; - Named_type* nt = this->expr_->type()->unalias()->named_type(); - if (nt == NULL || nt->named_object()->package() == NULL) - s.append(gogo->pkgpath()); - else - s.append(nt->named_object()->package()->pkgpath()); - s.push_back('.'); - if (nt != NULL) - s.append(Gogo::unpack_hidden_name(nt->name())); - s.push_back('.'); - s.append(Gogo::unpack_hidden_name(field->field_name())); - s.push_back('"'); - - // We can't use a string here, because internally a string holds a - // pointer to the actual bytes; when the linker garbage collects the - // string, it won't garbage collect the bytes. So we use a - // [...]byte. - - Expression* length_expr = Expression::make_integer_ul(s.length(), NULL, loc); - - Type* byte_type = Type::lookup_integer_type("byte"); - Array_type* array_type = Type::make_array_type(byte_type, length_expr); - array_type->set_is_array_incomparable(); - - Expression_list* bytes = new Expression_list(); - for (std::string::const_iterator p = s.begin(); p != s.end(); p++) + Map_type* mt = this->get_map_type(); + if (this->index()->is_error_expression() + || this->index()->type()->is_error_type() + || mt->is_error_type()) { - unsigned char c = static_cast(*p); - bytes->push_back(Expression::make_integer_ul(c, NULL, loc)); + go_assert(saw_errors()); + return Expression::make_error(loc); } - Expression* e = Expression::make_composite_literal(array_type, 0, false, - bytes, false, loc); - - Variable* var = new Variable(array_type, e, true, false, false, loc); - - static int count; - char buf[50]; - snprintf(buf, sizeof buf, "fieldtrack.%d", count); - ++count; + // Avoid copy for string([]byte) conversions used in map keys. + // mapaccess doesn't keep the reference, so this is safe. + Type_conversion_expression* ce = this->index_->conversion_expression(); + if (ce != NULL && ce->type()->is_string_type() + && ce->expr()->type()->is_slice_type()) + ce->set_no_copy(true); - Named_object* no = gogo->add_variable(buf, var); - e = Expression::make_var_reference(no, loc); - e = Expression::make_unary(OPERATOR_AND, e, loc); + if (!Type::are_identical(mt->key_type(), this->index_->type(), + Type::COMPARE_ERRORS | Type::COMPARE_TAGS, + NULL)) + { + if (this->index_->type()->interface_type() != NULL + && !this->index_->is_multi_eval_safe()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->index_, loc); + inserter->insert(temp); + this->index_ = Expression::make_temporary_reference(temp, loc); + } + this->index_ = Expression::convert_for_assignment(gogo, mt->key_type(), + this->index_, loc); + } - Expression* call = Runtime::make_call(gogo, Runtime::FIELDTRACK, loc, 1, e); - gogo->lower_expression(function, inserter, &call); - inserter->insert(Statement::make_statement(call, false)); + if (!this->index_->is_multi_eval_safe()) + { + Temporary_statement* temp = Statement::make_temporary(NULL, this->index_, + loc); + inserter->insert(temp); + this->index_ = Expression::make_temporary_reference(temp, loc); + } - // Put this function, and the global variable we just created, into - // unique sections. This will permit the linker to garbage collect - // them if they are not referenced. The effect is that the only - // strings, indicating field references, that will wind up in the - // executable will be those for functions that are actually needed. - if (function != NULL) - function->func_value()->set_in_unique_section(); - var->set_in_unique_section(); + if (this->value_pointer_ == NULL) + this->get_value_pointer(gogo); + if (this->value_pointer_->is_error_expression() + || this->value_pointer_->type()->is_error_type()) + return Expression::make_error(loc); + if (!this->value_pointer_->is_multi_eval_safe()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->value_pointer_, loc); + inserter->insert(temp); + this->value_pointer_ = Expression::make_temporary_reference(temp, loc); + } return this; } -// Return the type of a field reference. +// Return the type of a map index. Type* -Field_reference_expression::do_type() +Map_index_expression::do_type() { - Type* type = this->expr_->type(); - if (type->is_error()) - return type; - Struct_type* struct_type = type->struct_type(); - go_assert(struct_type != NULL); - return struct_type->field(this->field_index_)->type(); + Map_type* mt = this->get_map_type(); + if (mt == NULL) + return Type::make_error_type(); + return mt->val_type(); } -// Check the types for a field reference. +// Fix the type of a map index. void -Field_reference_expression::do_check_types(Gogo*) -{ - Type* type = this->expr_->type(); - if (type->is_error()) - return; - Struct_type* struct_type = type->struct_type(); - go_assert(struct_type != NULL); - go_assert(struct_type->field(this->field_index_) != NULL); -} - -// Get the backend representation for a field reference. - -Bexpression* -Field_reference_expression::do_get_backend(Translate_context* context) +Map_index_expression::do_determine_type(Gogo* gogo, const Type_context*) { - Bexpression* bstruct = this->expr_->get_backend(context); - return context->gogo()->backend()->struct_field_expression(bstruct, - this->field_index_, - this->location()); + this->map_->determine_type_no_context(gogo); + Map_type* mt = this->get_map_type(); + Type* key_type = mt == NULL ? NULL : mt->key_type(); + Type_context subcontext(key_type, false); + this->index_->determine_type(gogo, &subcontext); } -// Dump ast representation for a field reference expression. +// Check types of a map index. void -Field_reference_expression::do_dump_expression( - Ast_dump_context* ast_dump_context) const +Map_index_expression::do_check_types(Gogo*) { - this->expr_->dump_expression(ast_dump_context); - ast_dump_context->ostream() << "." << this->field_index_; + std::string reason; + Map_type* mt = this->get_map_type(); + if (mt == NULL) + return; + if (!Type::are_assignable(mt->key_type(), this->index_->type(), &reason)) + { + if (reason.empty()) + this->report_error(_("incompatible type for map index")); + else + { + go_error_at(this->location(), "incompatible type for map index (%s)", + reason.c_str()); + this->set_is_error(); + } + } } -// Make a reference to a qualified identifier in an expression. +// Add explicit type conversions. -Field_reference_expression* -Expression::make_field_reference(Expression* expr, unsigned int field_index, - Location location) +void +Map_index_expression::do_add_conversions() { - return new Field_reference_expression(expr, field_index, location); + Map_type* mt = this->get_map_type(); + if (mt == NULL) + return; + Type* lt = mt->key_type(); + Type* rt = this->index_->type(); + if (!Type::are_identical(lt, rt, 0, NULL) + && lt->interface_type() != NULL) + this->index_ = Expression::make_cast(lt, this->index_, this->location()); } -// Class Interface_field_reference_expression. - -// Return an expression for the pointer to the function to call. +// Get the backend representation for a map index. -Expression* -Interface_field_reference_expression::get_function() +Bexpression* +Map_index_expression::do_get_backend(Translate_context* context) { - Expression* ref = this->expr_; - Location loc = this->location(); - if (ref->type()->points_to() != NULL) - ref = Expression::make_dereference(ref, NIL_CHECK_DEFAULT, loc); - - Expression* mtable = - Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); - Struct_type* mtable_type = mtable->type()->points_to()->struct_type(); + Map_type* type = this->get_map_type(); + if (type == NULL) + { + go_assert(saw_errors()); + return context->backend()->error_expression(); + } - std::string name = Gogo::unpack_hidden_name(this->name_); - unsigned int index; - const Struct_field* field = mtable_type->find_local_field(name, &index); - go_assert(field != NULL); + go_assert(this->value_pointer_ != NULL + && this->value_pointer_->is_multi_eval_safe()); - mtable = Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, loc); - return Expression::make_field_reference(mtable, index, loc); + Expression* val = Expression::make_dereference(this->value_pointer_, + NIL_CHECK_NOT_NEEDED, + this->location()); + return val->get_backend(context); } -// Return an expression for the first argument to pass to the interface -// function. +// Get an expression for the map index. This returns an expression +// that evaluates to a pointer to a value. If the key is not in the +// map, the pointer will point to a zero value. Expression* -Interface_field_reference_expression::get_underlying_object() +Map_index_expression::get_value_pointer(Gogo* gogo) { - Expression* expr = this->expr_; - if (expr->type()->points_to() != NULL) - expr = Expression::make_dereference(expr, NIL_CHECK_DEFAULT, - this->location()); - return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, - this->location()); -} + if (this->value_pointer_ == NULL) + { + Map_type* type = this->get_map_type(); + if (type == NULL) + { + go_assert(saw_errors()); + return Expression::make_error(this->location()); + } -// Traversal. + Location loc = this->location(); + Expression* map_ref = this->map_; -int -Interface_field_reference_expression::do_traverse(Traverse* traverse) -{ - return Expression::traverse(&this->expr_, traverse); -} + Expression* index_ptr = Expression::make_unary(OPERATOR_AND, + this->index_, + loc); -// 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* type_expr = Expression::make_type_descriptor(type, loc); + Expression* zero = type->fat_zero_value(gogo); + Expression* map_index; + if (zero == NULL) + { + Runtime::Function code; + Expression* key; + switch (type->algorithm(gogo)) + { + case Map_type::MAP_ALG_FAST32: + case Map_type::MAP_ALG_FAST32PTR: + { + Type* uint32_type = Type::lookup_integer_type("uint32"); + Type* uint32_ptr_type = Type::make_pointer_type(uint32_type); + key = Expression::make_unsafe_cast(uint32_ptr_type, index_ptr, + loc); + key = Expression::make_dereference(key, NIL_CHECK_NOT_NEEDED, + loc); + code = Runtime::MAPACCESS1_FAST32; + break; + } + case Map_type::MAP_ALG_FAST64: + case Map_type::MAP_ALG_FAST64PTR: + { + Type* uint64_type = Type::lookup_integer_type("uint64"); + Type* uint64_ptr_type = Type::make_pointer_type(uint64_type); + key = Expression::make_unsafe_cast(uint64_ptr_type, index_ptr, + loc); + key = Expression::make_dereference(key, NIL_CHECK_NOT_NEEDED, + loc); + code = Runtime::MAPACCESS1_FAST64; + break; + } + case Map_type::MAP_ALG_FASTSTR: + key = this->index_; + code = Runtime::MAPACCESS1_FASTSTR; + break; + default: + key = index_ptr; + code = Runtime::MAPACCESS1; + break; + } + map_index = Runtime::make_call(gogo, code, loc, 3, + type_expr, map_ref, key); + } + else + map_index = Runtime::make_call(gogo, Runtime::MAPACCESS1_FAT, loc, 4, + type_expr, map_ref, index_ptr, zero); -Expression* -Interface_field_reference_expression::do_flatten(Gogo*, Named_object*, - Statement_inserter* inserter) -{ - if (this->expr_->is_error_expression() - || this->expr_->type()->is_error_type()) - { - go_assert(saw_errors()); - return Expression::make_error(this->location()); + Type* val_type = type->val_type(); + this->value_pointer_ = + Expression::make_unsafe_cast(Type::make_pointer_type(val_type), + map_index, this->location()); } - if (!this->expr_->is_multi_eval_safe()) - { - Temporary_statement* temp = - Statement::make_temporary(NULL, this->expr_, this->location()); - inserter->insert(temp); - this->expr_ = Expression::make_temporary_reference(temp, this->location()); - } - return this; + return this->value_pointer_; } -// Return the type of an interface field reference. +// Export a map index expression. -Type* -Interface_field_reference_expression::do_type() +void +Map_index_expression::do_export(Export_function_body* efb) const { - Type* expr_type = this->expr_->type(); + efb->write_c_string("("); + this->map_->export_expression(efb); + efb->write_c_string(")["); - Type* points_to = expr_type->points_to(); - if (points_to != NULL) - expr_type = points_to; + Type* old_context = efb->type_context(); + efb->set_type_context(this->get_map_type()->key_type()); - Interface_type* interface_type = expr_type->interface_type(); - if (interface_type == NULL) - return Type::make_error_type(); + this->index_->export_expression(efb); - const Typed_identifier* method = interface_type->find_method(this->name_); - if (method == NULL) - return Type::make_error_type(); + efb->set_type_context(old_context); - return method->type(); + efb->write_c_string("]"); } -// Determine types. +// Dump ast representation for a map index expression void -Interface_field_reference_expression::do_determine_type(Gogo* gogo, - const Type_context*) +Map_index_expression::do_dump_expression(Ast_dump_context* ast_dump_context) + const { - this->expr_->determine_type_no_context(gogo); + Index_expression::dump_index_expression(ast_dump_context, this->map_, + this->index_, NULL, NULL); } -// Check the types for an interface field reference. +// Make a map index expression. -void -Interface_field_reference_expression::do_check_types(Gogo*) +Map_index_expression* +Expression::make_map_index(Expression* map, Expression* index, + Location location) { - Type* type = this->expr_->type(); - - Type* points_to = type->points_to(); - if (points_to != NULL) - type = points_to; - - Interface_type* interface_type = type->interface_type(); - if (interface_type == NULL) - { - if (!type->is_error_type()) - this->report_error(_("expected interface or pointer to interface")); - } - else - { - const Typed_identifier* method = - interface_type->find_method(this->name_); - if (method == NULL) - { - go_error_at(this->location(), "method %qs not in interface", - Gogo::message_name(this->name_).c_str()); - this->set_is_error(); - } - } + return new Map_index_expression(map, index, location); } -// 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; +// Class Field_reference_expression. -// Find or create the thunk to call method NAME on TYPE. +// Lower a field reference expression. There is nothing to lower, but +// this is where we generate the tracking information for fields with +// the magic go:"track" tag. -Named_object* -Interface_field_reference_expression::create_thunk(Gogo* gogo, - Interface_type* type, - const std::string& name) +Expression* +Field_reference_expression::do_lower(Gogo* gogo, Named_object* function, + Statement_inserter* inserter, int) { - std::pair val(type, NULL); - std::pair ins = - Interface_field_reference_expression::interface_method_thunks.insert(val); - if (ins.second) + Struct_type* struct_type = this->expr_->type()->struct_type(); + if (struct_type == NULL) { - // This is the first time we have seen this interface. - ins.first->second = new Method_thunks(); + // Error will be reported elsewhere. + return this; } + const Struct_field* field = struct_type->field(this->field_index_); + if (field == NULL) + return this; + if (!field->has_tag()) + return this; + if (field->tag().find("go:\"track\"") == std::string::npos) + return this; - for (Method_thunks::const_iterator p = ins.first->second->begin(); - p != ins.first->second->end(); - p++) - if (p->first == name) - return p->second; + // References from functions generated by the compiler don't count. + if (function != NULL && function->func_value()->is_type_specific_function()) + return this; - Location loc = type->location(); + // We have found a reference to a tracked field. Build a call to + // the runtime function __go_fieldtrack with a string that describes + // the field. FIXME: We should only call this once per referenced + // field per function, not once for each reference to the field. - const Typed_identifier* method_id = type->find_method(name); - if (method_id == NULL) - return Named_object::make_erroneous_name(gogo->thunk_name()); + if (this->called_fieldtrack_) + return this; + this->called_fieldtrack_ = true; - Function_type* orig_fntype = method_id->type()->function_type(); - if (orig_fntype == NULL) - return Named_object::make_erroneous_name(gogo->thunk_name()); + Location loc = this->location(); - Struct_field_list* sfl = new Struct_field_list(); - // The type here is wrong--it should be the C function type. But it - // doesn't really matter. - Type* vt = Type::make_pointer_type(Type::make_void_type()); - sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); - sfl->push_back(Struct_field(Typed_identifier("val", type, loc))); - Struct_type* st = Type::make_struct_type(sfl, loc); - st->set_is_struct_incomparable(); - Type* closure_type = Type::make_pointer_type(st); + std::string s = "fieldtrack \""; + Named_type* nt = this->expr_->type()->unalias()->named_type(); + if (nt == NULL || nt->named_object()->package() == NULL) + s.append(gogo->pkgpath()); + else + s.append(nt->named_object()->package()->pkgpath()); + s.push_back('.'); + if (nt != NULL) + s.append(Gogo::unpack_hidden_name(nt->name())); + s.push_back('.'); + s.append(Gogo::unpack_hidden_name(field->field_name())); + s.push_back('"'); - Function_type* new_fntype = orig_fntype->copy_with_names(); + // We can't use a string here, because internally a string holds a + // pointer to the actual bytes; when the linker garbage collects the + // string, it won't garbage collect the bytes. So we use a + // [...]byte. - std::string thunk_name = gogo->thunk_name(); - Named_object* new_no = gogo->start_function(thunk_name, new_fntype, - false, loc); + Expression* length_expr = Expression::make_integer_ul(s.length(), NULL, loc); - Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); - cvar->set_is_used(); - cvar->set_is_closure(); - Named_object* cp = Named_object::make_variable("$closure" + thunk_name, - NULL, cvar); - new_no->func_value()->set_closure_var(cp); + Type* byte_type = Type::lookup_integer_type("byte"); + Array_type* array_type = Type::make_array_type(byte_type, length_expr); + array_type->set_is_array_incomparable(); - gogo->start_block(loc); + Expression_list* bytes = new Expression_list(); + for (std::string::const_iterator p = s.begin(); p != s.end(); p++) + { + unsigned char c = static_cast(*p); + bytes->push_back(Expression::make_integer_ul(c, NULL, loc)); + } - // 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_dereference(arg, NIL_CHECK_NOT_NEEDED, loc); - arg = Expression::make_field_reference(arg, 1, loc); + Expression* e = Expression::make_composite_literal(array_type, 0, false, + bytes, false, loc); - Expression *ifre = Expression::make_interface_field_reference(arg, name, - loc); + Variable* var = new Variable(array_type, e, true, false, false, 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 != 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)); - } - } + static int count; + char buf[50]; + snprintf(buf, sizeof buf, "fieldtrack.%d", count); + ++count; - Call_expression* call = Expression::make_call(ifre, args, - orig_fntype->is_varargs(), - loc); - call->set_varargs_are_lowered(); + Named_object* no = gogo->add_variable(buf, var); + e = Expression::make_var_reference(no, loc); + e = Expression::make_unary(OPERATOR_AND, e, 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); + Expression* call = Runtime::make_call(gogo, Runtime::FIELDTRACK, loc, 1, e); + gogo->lower_expression(function, inserter, &call); + inserter->insert(Statement::make_statement(call, false)); - // This is called after lowering but before determine_types. - gogo->lower_block(new_no, b); + // Put this function, and the global variable we just created, into + // unique sections. This will permit the linker to garbage collect + // them if they are not referenced. The effect is that the only + // strings, indicating field references, that will wind up in the + // executable will be those for functions that are actually needed. + if (function != NULL) + function->func_value()->set_in_unique_section(); + var->set_in_unique_section(); - gogo->finish_function(loc); + return this; +} - ins.first->second->push_back(std::make_pair(name, new_no)); - return new_no; +// Return the type of a field reference. + +Type* +Field_reference_expression::do_type() +{ + Type* type = this->expr_->type(); + if (type->is_error()) + return type; + Struct_type* struct_type = type->struct_type(); + go_assert(struct_type != NULL); + return struct_type->field(this->field_index_)->type(); } -// Lookup a thunk to call method NAME on TYPE. +// Check the types for a field reference. -Named_object* -Interface_field_reference_expression::lookup_thunk(Interface_type* type, - const std::string& name) +void +Field_reference_expression::do_check_types(Gogo*) { - 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; + Type* type = this->expr_->type(); + if (type->is_error()) + return; + Struct_type* struct_type = type->struct_type(); + go_assert(struct_type != NULL); + go_assert(struct_type->field(this->field_index_) != NULL); } -// Get the backend representation for a method value. +// Get the backend representation for a field reference. Bexpression* -Interface_field_reference_expression::do_get_backend(Translate_context* context) +Field_reference_expression::do_get_backend(Translate_context* context) { - Interface_type* type = this->expr_->type()->interface_type(); - if (type == NULL) - { - go_assert(saw_errors()); - return context->backend()->error_expression(); - } - - Named_object* thunk = - Interface_field_reference_expression::lookup_thunk(type, this->name_); + Bexpression* bstruct = this->expr_->get_backend(context); + return context->gogo()->backend()->struct_field_expression(bstruct, + this->field_index_, + this->location()); +} - // 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(); - } +// Dump ast representation for a field reference expression. - // 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. +void +Field_reference_expression::do_dump_expression( + Ast_dump_context* ast_dump_context) const +{ + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "." << this->field_index_; +} - Location loc = this->location(); +// Make a reference to a qualified identifier in an expression. - Struct_field_list* fields = new Struct_field_list(); - fields->push_back(Struct_field(Typed_identifier("fn", - thunk->func_value()->type(), - loc))); - fields->push_back(Struct_field(Typed_identifier("val", - this->expr_->type(), - loc))); - Struct_type* st = Type::make_struct_type(fields, loc); - st->set_is_struct_incomparable(); +Field_reference_expression* +Expression::make_field_reference(Expression* expr, unsigned int field_index, + Location location) +{ + return new Field_reference_expression(expr, field_index, location); +} - Expression_list* vals = new Expression_list(); - vals->push_back(Expression::make_func_code_reference(thunk, loc)); - vals->push_back(this->expr_); +// Class Interface_field_reference_expression. - Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); - Bexpression* bclosure = - Expression::make_heap_expression(expr, loc)->get_backend(context); +// Return an expression for the pointer to the function to call. - Gogo* gogo = context->gogo(); - Btype* btype = this->type()->get_backend(gogo); - bclosure = gogo->backend()->convert_expression(btype, bclosure, loc); +Expression* +Interface_field_reference_expression::get_function() +{ + Expression* ref = this->expr_; + Location loc = this->location(); + if (ref->type()->points_to() != NULL) + ref = Expression::make_dereference(ref, NIL_CHECK_DEFAULT, loc); - Expression* nil_check = - Expression::make_binary(OPERATOR_EQEQ, this->expr_, - Expression::make_nil(loc), loc); - Bexpression* bnil_check = nil_check->get_backend(context); + Expression* mtable = + Expression::make_interface_info(ref, INTERFACE_INFO_METHODS, loc); + Struct_type* mtable_type = mtable->type()->points_to()->struct_type(); - Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_MEM, loc, 0); - Bexpression* bcrash = crash->get_backend(context); + std::string name = Gogo::unpack_hidden_name(this->name_); + unsigned int index; + const Struct_field* field = mtable_type->find_local_field(name, &index); + go_assert(field != NULL); - Bfunction* bfn = context->function()->func_value()->get_decl(); - Bexpression* bcond = - gogo->backend()->conditional_expression(bfn, NULL, - bnil_check, bcrash, NULL, loc); - Bfunction* bfunction = context->function()->func_value()->get_decl(); - Bstatement* cond_statement = - gogo->backend()->expression_statement(bfunction, bcond); - return gogo->backend()->compound_expression(cond_statement, bclosure, loc); + mtable = Expression::make_dereference(mtable, NIL_CHECK_NOT_NEEDED, loc); + return Expression::make_field_reference(mtable, index, loc); } -// Dump ast representation for an interface field reference. +// Return an expression for the first argument to pass to the interface +// function. -void -Interface_field_reference_expression::do_dump_expression( - Ast_dump_context* ast_dump_context) const +Expression* +Interface_field_reference_expression::get_underlying_object() { - this->expr_->dump_expression(ast_dump_context); - ast_dump_context->ostream() << "." << this->name_; + Expression* expr = this->expr_; + if (expr->type()->points_to() != NULL) + expr = Expression::make_dereference(expr, NIL_CHECK_DEFAULT, + this->location()); + return Expression::make_interface_info(expr, INTERFACE_INFO_OBJECT, + this->location()); } -// Make a reference to a field in an interface. +// Traversal. -Expression* -Expression::make_interface_field_reference(Expression* expr, - const std::string& field, - Location location) +int +Interface_field_reference_expression::do_traverse(Traverse* traverse) { - return new Interface_field_reference_expression(expr, field, location); + return Expression::traverse(&this->expr_, traverse); } -// A general selector. This is a Parser_expression for LEFT.NAME. It -// is lowered after we know the type of the left hand side. +// 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. -class Selector_expression : public Parser_expression +Expression* +Interface_field_reference_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) { - public: - Selector_expression(Expression* left, const std::string& name, - Location location) - : Parser_expression(EXPRESSION_SELECTOR, location), - left_(left), name_(name) - { } + if (this->expr_->is_error_expression() + || this->expr_->type()->is_error_type()) + { + go_assert(saw_errors()); + return Expression::make_error(this->location()); + } - protected: - int - do_traverse(Traverse* traverse) - { return Expression::traverse(&this->left_, traverse); } + if (!this->expr_->is_multi_eval_safe()) + { + Temporary_statement* temp = + Statement::make_temporary(NULL, this->expr_, this->location()); + inserter->insert(temp); + this->expr_ = Expression::make_temporary_reference(temp, this->location()); + } + return this; +} - Expression* - do_lower(Gogo*, Named_object*, Statement_inserter*, int); +// Return the type of an interface field reference. - Expression* - do_copy() - { - return new Selector_expression(this->left_->copy(), this->name_, - this->location()); - } +Type* +Interface_field_reference_expression::do_type() +{ + Type* expr_type = this->expr_->type(); - void - do_dump_expression(Ast_dump_context* ast_dump_context) const; + Type* points_to = expr_type->points_to(); + if (points_to != NULL) + expr_type = points_to; - private: - Expression* - lower_method_expression(Gogo*); + Interface_type* interface_type = expr_type->interface_type(); + if (interface_type == NULL) + return Type::make_error_type(); - // The expression on the left hand side. - Expression* left_; - // The name on the right hand side. - std::string name_; -}; + const Typed_identifier* method = interface_type->find_method(this->name_); + if (method == NULL) + return Type::make_error_type(); -// Lower a selector expression once we know the real type of the left -// hand side. + return method->type(); +} -Expression* -Selector_expression::do_lower(Gogo* gogo, Named_object*, Statement_inserter*, - int) +// Determine types. + +void +Interface_field_reference_expression::do_determine_type(Gogo* gogo, + const Type_context*) { - 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()); + this->expr_->determine_type_no_context(gogo); } -// Lower a method expression T.M or (*T).M. We turn this into a -// function literal. +// Check the types for an interface field reference. -Expression* -Selector_expression::lower_method_expression(Gogo* gogo) +void +Interface_field_reference_expression::do_check_types(Gogo*) { - Location location = this->location(); - Type* left_type = this->left_->type(); - Type* type = left_type; - const std::string& name(this->name_); + Type* type = this->expr_->type(); - bool is_pointer; - if (type->points_to() == NULL) - is_pointer = false; - else - { - is_pointer = true; - type = type->points_to(); - } + Type* points_to = type->points_to(); + if (points_to != NULL) + 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* interface_type = type->interface_type(); + if (interface_type == NULL) { - Interface_type* it = type->interface_type(); - if (it != NULL) - imethod = it->find_method(name); + if (!type->is_error_type()) + this->report_error(_("expected interface or pointer to interface")); } - - if ((method == NULL && imethod == NULL) - || (left_type->named_type() != NULL && left_type->points_to() != NULL)) + else { - 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 + const Typed_identifier* method = + interface_type->find_method(this->name_); + if (method == NULL) { - 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()); + go_error_at(this->location(), "method %qs not in interface", + Gogo::message_name(this->name_).c_str()); + this->set_is_error(); } - return Expression::make_error(location); } +} + +// 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 the C function type. But it + // doesn't really matter. + Type* vt = Type::make_pointer_type(Type::make_void_type()); + sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val", type, loc))); + Struct_type* st = Type::make_struct_type(sfl, loc); + st->set_is_struct_incomparable(); + Type* closure_type = Type::make_pointer_type(st); + + Function_type* new_fntype = orig_fntype->copy_with_names(); - 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); - } + std::string thunk_name = gogo->thunk_name(); + Named_object* new_no = gogo->start_function(thunk_name, new_fntype, + false, loc); - // 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()); - } + Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); + cvar->set_is_used(); + cvar->set_is_closure(); + Named_object* cp = Named_object::make_variable("$closure" + thunk_name, + NULL, cvar); + new_no->func_value()->set_closure_var(cp); - 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)); + gogo->start_block(loc); - 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())); - } - } - } + // 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_dereference(arg, NIL_CHECK_NOT_NEEDED, loc); + arg = Expression::make_field_reference(arg, 1, loc); - const Typed_identifier_list* method_results = method_type->results(); - Typed_identifier_list* results; - if (method_results == NULL) - results = NULL; + 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 { - results = new Typed_identifier_list(); - for (Typed_identifier_list::const_iterator p = method_results->begin(); - p != method_results->end(); + const Typed_identifier_list* new_params = new_fntype->parameters(); + args = new Expression_list(); + for (Typed_identifier_list::const_iterator p = new_params->begin(); + p != new_params->end(); ++p) - results->push_back(*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)); + } } - Function_type* fntype = Type::make_function_type(NULL, parameters, results, - location); - if (method_type->is_varargs()) - fntype->set_is_varargs(); + Call_expression* call = Expression::make_call(ifre, args, + orig_fntype->is_varargs(), + loc); + call->set_varargs_are_lowered(); - // 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(f); - tce->set_may_convert_function_types(); - return f; - } + Statement* s = Statement::make_return_from_call(call, loc); + gogo->add_statement(s); + Block* b = gogo->finish_block(loc); + gogo->add_block(b, loc); - Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false, - location); + // This is called after lowering but before determine_types. + gogo->lower_block(new_no, b); - 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); + gogo->finish_function(loc); - // Even though we found the method above, if it has an error type we - // may see an error here. - if (bm->is_error_expression()) + 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* +Interface_field_reference_expression::do_get_backend(Translate_context* context) +{ + Interface_type* type = this->expr_->type()->interface_type(); + if (type == NULL) { - gogo->finish_function(location); - return bm; + go_assert(saw_errors()); + return context->backend()->error_expression(); } - Expression_list* args; - if (parameters->size() <= 1) - args = NULL; - else + Named_object* thunk = + 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()) { - 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)); - } + go_assert(saw_errors()); + return context->backend()->error_expression(); } - gogo->start_block(location); + // 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. - Call_expression* call = Expression::make_call(bm, args, - method_type->is_varargs(), - location); + Location loc = this->location(); - Statement* s = Statement::make_return_from_call(call, location); - gogo->add_statement(s); + Struct_field_list* fields = new Struct_field_list(); + fields->push_back(Struct_field(Typed_identifier("fn", + thunk->func_value()->type(), + loc))); + fields->push_back(Struct_field(Typed_identifier("val", + this->expr_->type(), + loc))); + Struct_type* st = Type::make_struct_type(fields, loc); + st->set_is_struct_incomparable(); - Block* b = gogo->finish_block(location); + Expression_list* vals = new Expression_list(); + vals->push_back(Expression::make_func_code_reference(thunk, loc)); + vals->push_back(this->expr_); - gogo->add_block(b, location); + Expression* expr = Expression::make_struct_composite_literal(st, vals, loc); + Bexpression* bclosure = + Expression::make_heap_expression(expr, loc)->get_backend(context); - // Lower the call in case there are multiple results. - gogo->lower_block(no, b); - gogo->flatten_block(no, b); + Gogo* gogo = context->gogo(); + Btype* btype = this->type()->get_backend(gogo); + bclosure = gogo->backend()->convert_expression(btype, bclosure, loc); - gogo->finish_function(location); + Expression* nil_check = + Expression::make_binary(OPERATOR_EQEQ, this->expr_, + Expression::make_nil(loc), loc); + Bexpression* bnil_check = nil_check->get_backend(context); - return Expression::make_func_reference(no, NULL, location); + Expression* crash = Runtime::make_call(gogo, Runtime::PANIC_MEM, loc, 0); + Bexpression* bcrash = crash->get_backend(context); + + Bfunction* bfn = context->function()->func_value()->get_decl(); + Bexpression* bcond = + gogo->backend()->conditional_expression(bfn, NULL, + bnil_check, bcrash, NULL, loc); + Bfunction* bfunction = context->function()->func_value()->get_decl(); + Bstatement* cond_statement = + gogo->backend()->expression_statement(bfunction, bcond); + return gogo->backend()->compound_expression(cond_statement, bclosure, loc); } -// Dump the ast for a selector expression. +// Dump ast representation for an interface field reference. void -Selector_expression::do_dump_expression(Ast_dump_context* ast_dump_context) - const +Interface_field_reference_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_; + this->expr_->dump_expression(ast_dump_context); + ast_dump_context->ostream() << "." << this->name_; } -// Make a selector expression. +// Make a reference to a field in an interface. Expression* -Expression::make_selector(Expression* left, const std::string& name, - Location location) +Expression::make_interface_field_reference(Expression* expr, + const std::string& field, + Location location) { - return new Selector_expression(left, name, location); + return new Interface_field_reference_expression(expr, field, location); } // Class Allocation_expression.