public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-4873] compiler: move Selector_expression up in file
@ 2023-10-23 21:16 Ian Lance Taylor
0 siblings, 0 replies; only message in thread
From: Ian Lance Taylor @ 2023-10-23 21:16 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:02aa322c8cfd3f60fa5a3a0eee4340bb644261fe
commit r14-4873-g02aa322c8cfd3f60fa5a3a0eee4340bb644261fe
Author: Ian Lance Taylor <iant@golang.org>
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<Type_conversion_expression*>(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<Bexpression*> 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<Bexpression*> 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<Bexpression*> 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<Bexpression*> 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<Bexpression*> 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<unsigned char>(*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<Interface_type*, Method_thunks*> val(type, NULL);
- std::pair<Interface_method_thunks::iterator, bool> 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<unsigned char>(*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<Interface_type*, Method_thunks*> val(type, NULL);
+ std::pair<Interface_method_thunks::iterator, bool> 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<Type_conversion_expression*>(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.
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2023-10-23 21:16 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-23 21:16 [gcc r14-4873] compiler: move Selector_expression up in file Ian Lance Taylor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).