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).