This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gccgo] For *(*T)&x don't move x to the heap


Normally when you take the address of a variable in Go the variable is
allocated on the heap.  However, this is not necessary if the address
of the variable does not escape the function.  I didn't implement real
escape analysis, but I did implement the case of *(*T)&x.  This is
important because it is actually used in libgo/go/math/unsafe.go.

Most of this patch is just moving class Type_conversion_expression
before class Unary_expression.

Committed to gccgo branch.

Ian


Index: gcc/go/expressions.h
===================================================================
--- gcc/go/expressions.h	(revision 154273)
+++ gcc/go/expressions.h	(working copy)
@@ -514,10 +514,11 @@ class Expression
   { return this->do_is_lvalue(); }
 
   // We are taking the address of this expression.  If this is
-  // invalid, report an error and return false.
+  // invalid, report an error and return false.  ESCAPES is true if
+  // this address escapes the current function.
   bool
-  address_taken(source_location location)
-  { return this->do_address_taken(location); }
+  address_taken(source_location location, bool escapes)
+  { return this->do_address_taken(location, escapes); }
 
   // This is called when the value of an expression is being stored
   // somewhere.  In some cases this requires that the reference count
@@ -652,7 +653,7 @@ class Expression
 
   // Child class implements taking the address of an expression.
   virtual bool
-  do_address_taken(source_location);
+  do_address_taken(source_location, bool);
 
   // Child class implements incrementing reference count.
   virtual Expression*
@@ -885,7 +886,7 @@ class Var_expression : public Expression
   { return true; }
 
   bool
-  do_address_taken(source_location);
+  do_address_taken(source_location, bool);
 
   Expression*
   do_being_copied(Refcounts*, bool);
@@ -1405,7 +1406,7 @@ class Map_index_expression : public Expr
   { return this->is_lvalue_; }
 
   bool
-  do_address_taken(source_location);
+  do_address_taken(source_location, bool);
 
   Expression*
   do_being_copied(Refcounts*, bool);
@@ -1557,7 +1558,7 @@ class Field_reference_expression : publi
   { return true; }
 
   bool
-  do_address_taken(source_location);
+  do_address_taken(source_location, bool);
 
   Expression*
   do_being_copied(Refcounts*, bool);
@@ -1634,7 +1635,7 @@ class Interface_field_reference_expressi
   }
 
   bool
-  do_address_taken(source_location)
+  do_address_taken(source_location, bool)
   { return true; }
 
   tree
Index: gcc/go/expressions.cc
===================================================================
--- gcc/go/expressions.cc	(revision 154273)
+++ gcc/go/expressions.cc	(working copy)
@@ -121,7 +121,7 @@ Expression::do_discarding_value()
 // address of an expression.
 
 bool
-Expression::do_address_taken(source_location location)
+Expression::do_address_taken(source_location location, bool)
 {
   this->report_address_taken_error(location);
   return false;
@@ -738,7 +738,7 @@ class Error_expression : public Expressi
   { return true; }
 
   bool
-  do_address_taken(source_location)
+  do_address_taken(source_location, bool)
   { return true; }
 
   Expression*
@@ -844,9 +844,11 @@ Var_expression::do_type()
 // may want to move the variable onto the heap.
 
 bool
-Var_expression::do_address_taken(source_location location)
+Var_expression::do_address_taken(source_location location, bool escapes)
 {
-  if (this->variable_->is_variable())
+  if (!escapes)
+    return true;
+  else if (this->variable_->is_variable())
     {
       this->variable_->var_value()->set_address_taken();
       return true;
@@ -1892,8 +1894,11 @@ class Const_expression : public Expressi
   { return this; }
 
   bool
-  do_address_taken(source_location location)
-  { return this->constant_->const_value()->expr()->address_taken(location); }
+  do_address_taken(source_location location, bool escapes)
+  {
+    return this->constant_->const_value()->expr()->address_taken(location,
+								 escapes);
+  }
 
   Expression*
   do_being_copied(Refcounts*, bool);
@@ -2260,50 +2265,40 @@ Expression::make_iota()
   return &iota_expression;
 }
 
-// Unary expressions.
+// A type conversion expression.
 
-class Unary_expression : public Expression
+class Type_conversion_expression : public Expression
 {
  public:
-  Unary_expression(Operator op, Expression* expr, source_location location)
-    : Expression(EXPRESSION_UNARY, location),
-      op_(op), expr_(expr)
+  Type_conversion_expression(Type* type, Expression* expr,
+			     source_location location)
+    : Expression(EXPRESSION_CONVERSION, location),
+      type_(type), expr_(expr), is_being_copied_(false)
   { }
 
-  // Return the operator.
-  Operator
-  op() const
-  { return this->op_; }
+  // Return the type to which we are converting.
+  Type*
+  type() const
+  { return this->type_; }
 
-  // Return the operand.
+  // Return the expression which we are converting.
   Expression*
-  operand() const
+  expr() const
   { return this->expr_; }
 
-  // Apply unary opcode OP to UVAL, setting VAL.  Return true if this
-  // could be done, false if not.
-  static bool
-  eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
-	       source_location);
-
-  // Apply unary opcode OP to UVAL, setting VAL.  Return true if this
-  // could be done, false if not.
-  static bool
-  eval_float(Operator op, mpfr_t uval, mpfr_t val);
-
   static Expression*
   do_import(Import*);
 
  protected:
   int
-  do_traverse(Traverse* traverse)
-  { return Expression::traverse(&this->expr_, traverse); }
+  do_traverse(Traverse* traverse);
 
   Expression*
   do_lower(Gogo*, int);
 
   bool
-  do_is_constant() const;
+  do_is_constant() const
+  { return this->expr_->is_constant(); }
 
   bool
   do_integer_constant_value(bool, mpz_t, Type**) const;
@@ -2311,11 +2306,19 @@ class Unary_expression : public Expressi
   bool
   do_float_constant_value(mpfr_t, Type**) const;
 
+  bool
+  do_string_constant_value(std::string*) const;
+
   Type*
-  do_type();
+  do_type()
+  { return this->type_; }
 
   void
-  do_determine_type(const Type_context*);
+  do_determine_type(const Type_context*)
+  {
+    Type_context subcontext(this->type_, true);
+    this->expr_->determine_type(&subcontext);
+  }
 
   void
   do_check_types(Gogo*);
@@ -2323,1180 +2326,1256 @@ class Unary_expression : public Expressi
   Expression*
   do_copy()
   {
-    return Expression::make_unary(this->op_, this->expr_->copy(),
-				  this->location());
+    return new Type_conversion_expression(this->type_, this->expr_->copy(),
+					  this->location());
   }
 
-  bool
-  do_is_lvalue() const
-  { return this->op_ == OPERATOR_MULT; }
-
-  bool
-  do_address_taken(source_location);
-
   Expression*
   do_being_copied(Refcounts*, bool);
 
   Expression*
   do_note_decrements(Refcounts*);
 
-  Expression*
-  do_being_set(Refcounts*);
-
   tree
-  do_get_tree(Translate_context*);
+  do_get_tree(Translate_context* context);
 
   void
   do_export(Export*) const;
 
  private:
-  // The unary operator to apply.
-  Operator op_;
-  // The operand.
+  // The type to convert to.
+  Type* type_;
+  // The expression to convert.
   Expression* expr_;
+  // Whether this expression is being copied.
+  bool is_being_copied_;
 };
 
-// If we are taking the address of a composite literal, and the
-// contents are not constant, then we want to make a heap composite
-// instead.
+// Traversal.
 
-Expression*
-Unary_expression::do_lower(Gogo*, int)
+int
+Type_conversion_expression::do_traverse(Traverse* traverse)
 {
-  source_location loc = this->location();
-  Operator op = this->op_;
-  Expression* expr = this->expr_;
+  if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT
+      || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  return TRAVERSE_CONTINUE;
+}
 
-  if (op == OPERATOR_MULT && expr->is_type_expression())
-    return Expression::make_type(Type::make_pointer_type(expr->type()), loc);
+// Convert to a constant at lowering time.
 
-  if (op == OPERATOR_MULT && expr->classification() == EXPRESSION_UNARY)
-    {
-      Unary_expression* uexpr = static_cast<Unary_expression*>(expr);
-      if (uexpr->op_ == OPERATOR_AND)
-	return uexpr->expr_;
-    }
+Expression*
+Type_conversion_expression::do_lower(Gogo*, int)
+{
+  Type* type = this->type_;
+  Expression* val = this->expr_;
+  source_location location = this->location();
 
-  if (op == OPERATOR_PLUS || op == OPERATOR_MINUS
-      || op == OPERATOR_NOT || op == OPERATOR_XOR)
+  if (type->integer_type() != NULL)
     {
-      Expression* ret = NULL;
-
-      mpz_t eval;
-      mpz_init(eval);
-      Type* etype;
-      if (expr->integer_constant_value(false, eval, &etype))
+      mpz_t ival;
+      mpz_init(ival);
+      Type* dummy;
+      if (val->integer_constant_value(false, ival, &dummy))
 	{
-	  mpz_t val;
-	  mpz_init(val);
-	  if (Unary_expression::eval_integer(op, etype, eval, val, loc))
-	    ret = Expression::make_integer(&val, etype, loc);
-	  mpz_clear(val);
+	  if (!Integer_expression::check_constant(ival, type, location))
+	    mpz_set_ui(ival, 0);
+	  return Expression::make_integer(&ival, type, location);
 	}
-      mpz_clear(eval);
-      if (ret != NULL)
-	return ret;
 
-      if (op == OPERATOR_PLUS || op == OPERATOR_MINUS)
+      mpfr_t fval;
+      mpfr_init(fval);
+      if (val->float_constant_value(fval, &dummy))
 	{
-	  mpfr_t fval;
-	  mpfr_init(fval);
-	  Type* ftype;
-	  if (expr->float_constant_value(fval, &ftype))
+	  if (!mpfr_integer_p(fval))
 	    {
-	      mpfr_t val;
-	      mpfr_init(val);
-	      if (Unary_expression::eval_float(op, fval, val))
-		ret = Expression::make_float(&val, ftype, loc);
-	      mpfr_clear(val);
+	      error_at(location,
+		       "floating point constant truncated to integer");
+	      return Expression::make_error(location);
 	    }
-	  mpfr_clear(fval);
-	  if (ret != NULL)
-	    return ret;
+	  mpfr_get_z(ival, fval, GMP_RNDN);
+	  if (!Integer_expression::check_constant(ival, type, location))
+	    mpz_set_ui(ival, 0);
+	  return Expression::make_integer(&ival, type, location);
+	}
+      mpfr_clear(fval);
+      mpz_clear(ival);
+    }
+
+  if (type->float_type() != NULL)
+    {
+      mpfr_t fval;
+      mpfr_init(fval);
+      Type* dummy;
+      if (val->float_constant_value(fval, &dummy))
+	{
+	  if (!Float_expression::check_constant(fval, type, location))
+	    mpfr_set_ui(fval, 0, GMP_RNDN);
+	  Float_expression::constrain_float(fval, type);
+	  return Expression::make_float(&fval, type, location);
 	}
+      mpfr_clear(fval);
     }
 
   return this;
 }
 
-// Return whether a unary expression is a constant.
+// Return the constant integer value if there is one.
 
 bool
-Unary_expression::do_is_constant() const
+Type_conversion_expression::do_integer_constant_value(bool iota_is_constant,
+						      mpz_t val,
+						      Type** ptype) const
 {
-  if (this->op_ == OPERATOR_MULT)
-    {
-      // Indirecting through a pointer is only constant if the object
-      // to which the expression points is constant, but we currently
-      // have no way to determine that.
-      return false;
-    }
-  else if (this->op_ == OPERATOR_AND)
+  if (this->type_->integer_type() == NULL)
+    return false;
+
+  mpz_t ival;
+  mpz_init(ival);
+  Type* dummy;
+  if (this->expr_->integer_constant_value(iota_is_constant, ival, &dummy))
     {
-      // Taking the address of a variable is constant if it is a
-      // global variable, not constant otherwise.  In other cases
-      // taking the address is probably not a constant.
-      Var_expression* ve = this->expr_->var_expression();
-      if (ve != NULL)
+      if (!Integer_expression::check_constant(ival, this->type_,
+					      this->location()))
 	{
-	  Named_object* no = ve->named_object();
-	  return no->is_variable() && no->var_value()->is_global();
+	  mpz_clear(ival);
+	  return false;
 	}
-      return false;
+      mpz_set(val, ival);
+      mpz_clear(ival);
+      *ptype = this->type_;
+      return true;
     }
-  else
-    return this->expr_->is_constant();
+  mpz_clear(ival);
+
+  mpfr_t fval;
+  mpfr_init(fval);
+  if (this->expr_->float_constant_value(fval, &dummy))
+    {
+      mpfr_get_z(val, fval, GMP_RNDN);
+      mpfr_clear(fval);
+      if (!Integer_expression::check_constant(val, this->type_,
+					      this->location()))
+	return false;
+      *ptype = this->type_;
+      return true;
+    }
+  mpfr_clear(fval);
+
+  return false;
 }
 
-// Apply unary opcode OP to UVAL, setting VAL.  UTYPE is the type of
-// UVAL, if known; it may be NULL.  Return true if this could be done,
-// false if not.
+// Return the constant floating point value if there is one.
 
 bool
-Unary_expression::eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
-			       source_location location)
+Type_conversion_expression::do_float_constant_value(mpfr_t val,
+						    Type** ptype) const
 {
-  switch (op)
+  if (this->type_->float_type() == NULL)
+    return false;
+
+  mpfr_t fval;
+  mpfr_init(fval);
+  Type* dummy;
+  if (this->expr_->float_constant_value(fval, &dummy))
     {
-    case OPERATOR_PLUS:
-      mpz_set(val, uval);
+      if (!Float_expression::check_constant(fval, this->type_,
+					    this->location()))
+	{
+	  mpfr_clear(fval);
+	  return false;
+	}
+      mpfr_set(val, fval, GMP_RNDN);
+      mpfr_clear(fval);
+      Float_expression::constrain_float(val, this->type_);
+      *ptype = this->type_;
       return true;
-    case OPERATOR_MINUS:
-      mpz_neg(val, uval);
-      return Integer_expression::check_constant(val, utype, location);
-    case OPERATOR_NOT:
-      mpz_set_ui(val, mpz_cmp_si(uval, 0) == 0 ? 1 : 0);
+    }
+  mpfr_clear(fval);
+
+  mpz_t ival;
+  mpz_init(ival);
+  if (this->expr_->integer_constant_value(false, ival, &dummy))
+    {
+      mpfr_set_z(val, ival, GMP_RNDN);
+      mpz_clear(ival);
+      if (!Float_expression::check_constant(val, this->type_,
+					    this->location()))
+	return false;
+      Float_expression::constrain_float(val, this->type_);
+      *ptype = this->type_;
       return true;
-    case OPERATOR_XOR:
-      if (utype == NULL
-	  || utype->integer_type() == NULL
-	  || utype->integer_type()->is_abstract())
-	mpz_com(val, uval);
-      else
-	{
-	  // The number of HOST_WIDE_INTs that it takes to represent
-	  // UVAL.
-	  size_t count = ((mpz_sizeinbase(uval, 2)
-			   + HOST_BITS_PER_WIDE_INT
-			   - 1)
-			  / HOST_BITS_PER_WIDE_INT);
+    }
+  mpz_clear(ival);
 
-	  unsigned HOST_WIDE_INT* phwi = new unsigned HOST_WIDE_INT[count];
-	  memset(phwi, 0, count * sizeof(HOST_WIDE_INT));
+  return false;
+}
 
-	  size_t ecount;
-	  mpz_export(phwi, &ecount, -1, sizeof(HOST_WIDE_INT), 0, 0, uval);
-	  gcc_assert(ecount <= count);
+// Return the constant string value if there is one.
 
-	  // Trim down to the number of words required by the type.
-	  size_t obits = utype->integer_type()->bits();
-	  if (!utype->integer_type()->is_unsigned())
-	    ++obits;
-	  size_t ocount = ((obits + HOST_BITS_PER_WIDE_INT - 1)
-			   / HOST_BITS_PER_WIDE_INT);
-	  gcc_assert(ocount <= ocount);
+bool
+Type_conversion_expression::do_string_constant_value(std::string* val) const
+{
+  if (this->type_->is_string_type()
+      && this->expr_->type()->integer_type() != NULL)
+    {
+      mpz_t ival;
+      mpz_init(ival);
+      Type* dummy;
+      if (this->expr_->integer_constant_value(false, ival, &dummy))
+	{
+	  unsigned long ulval = mpz_get_ui(ival);
+	  if (mpz_cmp_ui(ival, ulval) == 0)
+	    {
+	      Lex::append_char(ulval, true, val, this->location());
+	      mpz_clear(ival);
+	      return true;
+	    }
+	}
+      mpz_clear(ival);
+    }
 
-	  for (size_t i = 0; i < ocount; ++i)
-	    phwi[i] = ~phwi[i];
+  // FIXME: Could handle conversion from const []int here.
 
-	  size_t clearbits = ocount * HOST_BITS_PER_WIDE_INT - obits;
-	  if (clearbits != 0)
-	    phwi[ocount - 1] &= (((unsigned HOST_WIDE_INT) (HOST_WIDE_INT) -1)
-				 >> clearbits);
+  return false;
+}
 
-	  mpz_import(val, ocount, -1, sizeof(HOST_WIDE_INT), 0, 0, phwi);
+// Check that types are convertible.
 
-	  delete[] phwi;
+void
+Type_conversion_expression::do_check_types(Gogo*)
+{
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+  std::string reason;
+
+  if (Type::are_compatible_for_conversion(type, expr_type, &reason))
+    return;
+
+  bool ok = false;
+  if ((type->integer_type() != NULL || type->float_type() != NULL)
+      && (expr_type->integer_type() != NULL
+	  || expr_type->float_type() != NULL))
+    ok = true;
+  else if (type->is_string_type())
+    {
+      if (expr_type->integer_type() != NULL)
+	ok = true;
+      else
+	{
+	  Type* t = expr_type->deref();
+	  if (t->array_type() != NULL)
+	    {
+	      Type* e = t->array_type()->element_type()->forwarded();
+	      if (e->integer_type() != NULL
+		  && (e == Type::lookup_integer_type("uint8")
+		      || e == Type::lookup_integer_type("int")))
+		ok = true;
+	    }
 	}
-      return Integer_expression::check_constant(val, utype, location);
-    case OPERATOR_AND:
-    case OPERATOR_MULT:
-      return false;
-    default:
-      gcc_unreachable();
     }
-}
+  else if ((type->is_unsafe_pointer_type()
+	    && (expr_type->points_to() != NULL
+		|| (expr_type->integer_type() != NULL
+		    && expr_type->integer_type()->bits() == POINTER_SIZE
+		    && expr_type->integer_type()->is_unsigned())))
+	   || (expr_type->is_unsafe_pointer_type()
+	       && (type->points_to() != NULL
+		   || (type->integer_type() != NULL
+		       && type->integer_type()->bits() == POINTER_SIZE
+		       && type->integer_type()->is_unsigned()))))
+    {
+      // Conversions between unsafe pointers and other pointers or
+      // integers of appropriate size are permitted.
+      ok = true;
+    }
 
-// Apply unary opcode OP to UVAL, setting VAL.  Return true if this
-// could be done, false if not.
+  // FIXME: Conversions between interfaces and pointers are supported.
 
-bool
-Unary_expression::eval_float(Operator op, mpfr_t uval, mpfr_t val)
-{
-  switch (op)
+  if (!ok)
     {
-    case OPERATOR_PLUS:
-      mpfr_set(val, uval, GMP_RNDN);
-      return true;
-    case OPERATOR_MINUS:
-      mpfr_neg(val, uval, GMP_RNDN);
-      return true;
-    case OPERATOR_NOT:
-    case OPERATOR_XOR:
-    case OPERATOR_AND:
-    case OPERATOR_MULT:
-      return false;
-    default:
-      gcc_unreachable();
+      if (reason.empty())
+	this->report_error(_("invalid type conversion"));
+      else
+	{
+	  error_at(this->location(), "invalid type conversion (%s)",
+		   reason.c_str());
+	  this->set_is_error();
+	}
     }
 }
 
-// Return the integral constant value of a unary expression, if it has one.
+// The type conversion is being copied elsewhere.  If we do not call a
+// function which creates a new reference, then we need to pass this
+// on to the subsidiary expression.
 
-bool
-Unary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
-					    Type** ptype) const
+Expression*
+Type_conversion_expression::do_being_copied(Refcounts* refcounts,
+					    bool for_local)
 {
-  mpz_t uval;
-  mpz_init(uval);
-  bool ret;
-  if (!this->expr_->integer_constant_value(iota_is_constant, uval, ptype))
-    ret = false;
+  this->is_being_copied_ = true;
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+  bool copy_base;
+  if (type == expr_type)
+    copy_base = true;
+  else if (type->interface_type() != NULL
+	   || expr_type->interface_type() != NULL)
+    copy_base = false;
+  else if (type->is_string_type()
+	   && (expr_type->integer_type() != NULL
+	       || expr_type->deref()->array_type() != NULL))
+    copy_base = false;
   else
-    ret = Unary_expression::eval_integer(this->op_, *ptype, uval, val,
-					 this->location());
-  mpz_clear(uval);
-  return ret;
+    copy_base = true;
+  if (copy_base && expr_type->has_refcounted_component())
+    this->expr_ = this->expr_->being_copied(refcounts, for_local);
+  return this;
 }
 
-// Return the floating point constant value of a unary expression, if
-// it has one.
+// A type conversion may introduce a reference count.
 
-bool
-Unary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+Expression*
+Type_conversion_expression::do_note_decrements(Refcounts* refcounts)
 {
-  mpfr_t uval;
-  mpfr_init(uval);
-  bool ret;
-  if (!this->expr_->float_constant_value(uval, ptype))
-    ret = false;
+  if (this->is_being_copied_)
+    return this;
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+  bool need_decrement;
+  if (type == expr_type)
+    need_decrement = false;
+  else if (type->interface_type() != NULL)
+    need_decrement = true;
+  else if (expr_type->interface_type() != NULL)
+    need_decrement = type->has_refcounted_component();
+  else if (type->is_string_type()
+	   && (expr_type->integer_type() != NULL
+	       || expr_type->deref()->array_type() != NULL))
+    need_decrement = true;
   else
-    ret = Unary_expression::eval_float(this->op_, uval, val);
-  mpfr_clear(uval);
-  return ret;
+    need_decrement = false;
+  if (!need_decrement)
+    return this;
+  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+					  this, false);
 }
 
-// Return the type of a unary expression.
+// Get a tree for a type conversion.
 
-Type*
-Unary_expression::do_type()
+tree
+Type_conversion_expression::do_get_tree(Translate_context* context)
 {
-  switch (this->op_)
-    {
-    case OPERATOR_PLUS:
-    case OPERATOR_MINUS:
-    case OPERATOR_NOT:
-    case OPERATOR_XOR:
-      return this->expr_->type();
+  Gogo* gogo = context->gogo();
+  tree type_tree = this->type_->get_tree(gogo);
+  tree expr_tree = this->expr_->get_tree(context);
 
-    case OPERATOR_AND:
-      return Type::make_pointer_type(this->expr_->type());
+  if (type_tree == error_mark_node
+      || expr_tree == error_mark_node
+      || TREE_TYPE(expr_tree) == error_mark_node)
+    return error_mark_node;
 
-    case OPERATOR_MULT:
-      {
-	Type* subtype = this->expr_->type();
-	Type* points_to = subtype->points_to();
-	if (points_to == NULL)
-	  return Type::make_error_type();
-	return points_to;
-      }
+  if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(expr_tree)))
+    return fold_convert(type_tree, expr_tree);
 
-    default:
-      gcc_unreachable();
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+  tree ret;
+  if (type->interface_type() != NULL || expr_type->interface_type() != NULL)
+    ret = Expression::convert_for_assignment(context, type, expr_type,
+					     expr_tree, this->location());
+  else if (type->integer_type() != NULL)
+    {
+      if (expr_type->integer_type() != NULL
+	  || expr_type->float_type() != NULL
+	  || expr_type->is_unsafe_pointer_type())
+	ret = fold(convert_to_integer(type_tree, expr_tree));
+      else
+	gcc_unreachable();
     }
-}
-
-// Determine abstract types for a unary expression.
-
-void
-Unary_expression::do_determine_type(const Type_context* context)
-{
-  switch (this->op_)
+  else if (type->float_type() != NULL)
     {
-    case OPERATOR_PLUS:
-    case OPERATOR_MINUS:
-    case OPERATOR_NOT:
-    case OPERATOR_XOR:
-      this->expr_->determine_type(context);
-      break;
-
-    case OPERATOR_AND:
-      // Taking the address of something.
-      {
-	Type* subtype = (context->type == NULL
-			 ? NULL
-			 : context->type->points_to());
-	Type_context subcontext(subtype, false);
-	this->expr_->determine_type(&subcontext);
-      }
-      break;
-
-    case OPERATOR_MULT:
-      // Indirecting through a pointer.
-      {
-	Type* subtype = (context->type == NULL
-			 ? NULL
-			 : Type::make_pointer_type(context->type));
-	Type_context subcontext(subtype, false);
-	this->expr_->determine_type(&subcontext);
-      }
-      break;
-
-    default:
-      gcc_unreachable();
+      if (expr_type->integer_type() != NULL
+	  || expr_type->float_type() != NULL)
+	ret = fold(convert_to_real(type_tree, expr_tree));
+      else
+	gcc_unreachable();
     }
-}
-
-// Check types for a unary expression.
-
-void
-Unary_expression::do_check_types(Gogo*)
-{
-  switch (this->op_)
+  else if (type->is_string_type()
+	   && expr_type->integer_type() != NULL)
     {
-    case OPERATOR_PLUS:
-    case OPERATOR_MINUS:
-      {
-	Type* type = this->expr_->type();
-	if (type->integer_type() == NULL
-	    && type->float_type() == NULL
-	    && !type->is_error_type())
-	  this->report_error(_("expected numeric type"));
-      }
-      break;
-
-    case OPERATOR_NOT:
-    case OPERATOR_XOR:
-      {
-	Type* type = this->expr_->type();
-	if (type->integer_type() == NULL
-	    && !type->is_boolean_type()
-	    && !type->is_error_type())
-	  this->report_error(_("expected integer or boolean type"));
-      }
-      break;
-
-    case OPERATOR_AND:
-      if (!this->expr_->address_taken(this->location()))
-	this->set_is_error();
-      break;
-
-    case OPERATOR_MULT:
-      // Indirecting through a pointer.
-      {
-	Type* type = this->expr_->type();
-	if (type->points_to() == NULL
-	    && !type->is_error_type())
-	  this->report_error(_("expected pointer"));
-      }
-      break;
+      expr_tree = fold_convert(integer_type_node, expr_tree);
+      if (host_integerp(expr_tree, 0))
+	{
+	  HOST_WIDE_INT intval = tree_low_cst(expr_tree, 0);
+	  std::string s;
+	  Lex::append_char(intval, true, &s, this->location());
+	  Expression* se = Expression::make_string(s, this->location());
+	  return se->get_tree(context);
+	}
 
-    default:
-      gcc_unreachable();
+      static tree int_to_string_fndecl;
+      ret = Gogo::call_builtin(&int_to_string_fndecl,
+			       this->location(),
+			       "__go_int_to_string",
+			       1,
+			       type_tree,
+			       integer_type_node,
+			       fold_convert(integer_type_node, expr_tree));
+    }
+  else if (type->is_string_type()
+	   && (expr_type->array_type() != NULL
+	       || (expr_type->points_to() != NULL
+		   && expr_type->points_to()->array_type() != NULL)))
+    {
+      Type* t = expr_type;
+      if (t->points_to() != NULL)
+	{
+	  t = t->points_to();
+	  expr_tree = build_fold_indirect_ref(expr_tree);
+	}
+      if (!DECL_P(expr_tree))
+	expr_tree = save_expr(expr_tree);
+      Array_type* a = t->array_type();
+      Type* e = a->element_type()->forwarded();
+      gcc_assert(e->integer_type() != NULL);
+      tree valptr = fold_convert(const_ptr_type_node,
+				 a->value_pointer_tree(gogo, expr_tree));
+      tree len = a->length_tree(gogo, expr_tree);
+      if (e->integer_type()->is_unsigned()
+	  && e->integer_type()->bits() == 8)
+	{
+	  static tree byte_array_to_string_fndecl;
+	  ret = Gogo::call_builtin(&byte_array_to_string_fndecl,
+				   this->location(),
+				   "__go_byte_array_to_string",
+				   2,
+				   type_tree,
+				   const_ptr_type_node,
+				   valptr,
+				   size_type_node,
+				   len);
+	}
+      else
+	{
+	  gcc_assert(e == Type::lookup_integer_type("int"));
+	  static tree int_array_to_string_fndecl;
+	  ret = Gogo::call_builtin(&int_array_to_string_fndecl,
+				   this->location(),
+				   "__go_int_array_to_string",
+				   2,
+				   type_tree,
+				   const_ptr_type_node,
+				   valptr,
+				   size_type_node,
+				   len);
+	}
     }
+  else if ((type->is_unsafe_pointer_type()
+	    && expr_type->points_to() != NULL)
+	   || (expr_type->is_unsafe_pointer_type()
+	       && type->points_to() != NULL))
+    ret = fold_convert(type_tree, expr_tree);
+  else if (type->is_unsafe_pointer_type()
+	   && expr_type->integer_type() != NULL)
+    ret = convert_to_pointer(type_tree, expr_tree);
+  else
+    ret = Expression::convert_for_assignment(context, type, expr_type,
+					     expr_tree, this->location());
+
+  return ret;
 }
 
-// &*p is OK.
+// Output a type conversion in a constant expression.
 
-bool
-Unary_expression::do_address_taken(source_location location)
+void
+Type_conversion_expression::do_export(Export* exp) const
 {
-  if (this->op_ == OPERATOR_MULT)
-    return true;
-  this->report_address_taken_error(location);
-  return false;
+  exp->write_c_string("convert(");
+  exp->write_type(this->type_);
+  exp->write_c_string(", ");
+  this->expr_->export_expression(exp);
+  exp->write_c_string(")");
 }
 
-// Copying a unary expression may require incrementing a reference
-// count.
+// Import a type conversion or a struct construction.
 
 Expression*
-Unary_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+Type_conversion_expression::do_import(Import* imp)
 {
-  if (!this->type()->has_refcounted_component())
-    return this;
-  if (this->op_ == OPERATOR_PLUS)
-    {
-      this->expr_ = this->expr_->being_copied(refcounts, for_local);
-      return this;
-    }
-  else if (this->op_ == OPERATOR_AND && this->is_constant())
-    {
-      // No need to increment the reference count when the address is
-      // a constant.
-      return this;
-    }
-  else
-    return Expression::make_refcount_adjust(refcounts,
-					    REFCOUNT_INCREMENT_COPIED,
-					    this, for_local);
+  imp->require_c_string("convert(");
+  Type* type = imp->read_type();
+  imp->require_c_string(", ");
+  Expression* val = Expression::import_expression(imp);
+  imp->require_c_string(")");
+  return Expression::make_cast(type, val, imp->location());
 }
 
-// A unary expression does not increment any reference counts, so
-// there are no reference counts to decrement afterward.
+// Make a type cast expression.
 
 Expression*
-Unary_expression::do_note_decrements(Refcounts*)
+Expression::make_cast(Type* type, Expression* val, source_location location)
 {
-  return this;
+  if (type->is_error_type() || val->is_error_expression())
+    return Expression::make_error(location);
+  return new Type_conversion_expression(type, val, location);
 }
 
-// Assigning to *p requires decrementing the reference count of the
-// old value.
+// Unary expressions.
 
-Expression*
-Unary_expression::do_being_set(Refcounts* refcounts)
+class Unary_expression : public Expression
 {
-  gcc_assert(this->op_ == OPERATOR_MULT);
-  if (!this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_decrement_lvalue(refcounts, this);
-}
+ public:
+  Unary_expression(Operator op, Expression* expr, source_location location)
+    : Expression(EXPRESSION_UNARY, location),
+      op_(op), expr_(expr), escapes_(true)
+  { }
 
-// Get a tree for a unary expression.
+  // Return the operator.
+  Operator
+  op() const
+  { return this->op_; }
 
-tree
-Unary_expression::do_get_tree(Translate_context* context)
-{
-  tree expr = this->expr_->get_tree(context);
-  if (expr == error_mark_node)
-    return error_mark_node;
+  // Return the operand.
+  Expression*
+  operand() const
+  { return this->expr_; }
 
-  source_location loc = this->location();
-  switch (this->op_)
-    {
-    case OPERATOR_PLUS:
-      return expr;
+  // Record that an address expression does not escape.
+  void
+  set_does_not_escape()
+  {
+    gcc_assert(this->op_ == OPERATOR_AND);
+    this->escapes_ = false;
+  }
 
-    case OPERATOR_MINUS:
-      {
-	tree type = TREE_TYPE(expr);
-	tree compute_type = excess_precision_type(type);
-	if (compute_type != NULL_TREE)
-	  expr = convert_to_real(compute_type, expr);
-	tree ret = fold_build1_loc(loc, NEGATE_EXPR,
-				   (compute_type != NULL_TREE
-				    ? compute_type
-				    : type),
-				   expr);
-	if (compute_type != NULL_TREE)
-	  ret = convert_to_real(type, ret);
-	return ret;
-      }
+  // Apply unary opcode OP to UVAL, setting VAL.  Return true if this
+  // could be done, false if not.
+  static bool
+  eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
+	       source_location);
 
-    case OPERATOR_NOT:
-      if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
-	return fold_build1_loc(loc, TRUTH_NOT_EXPR, TREE_TYPE(expr), expr);
-      else
-	return fold_build2_loc(loc, NE_EXPR, boolean_type_node, expr,
-			       build_int_cst(TREE_TYPE(expr), 0));
+  // Apply unary opcode OP to UVAL, setting VAL.  Return true if this
+  // could be done, false if not.
+  static bool
+  eval_float(Operator op, mpfr_t uval, mpfr_t val);
 
-    case OPERATOR_XOR:
-      return fold_build1_loc(loc, BIT_NOT_EXPR, TREE_TYPE(expr), expr);
+  static Expression*
+  do_import(Import*);
 
-    case OPERATOR_AND:
-      // We should not see a non-constant constructor here; cases
-      // where we would see one should have been moved onto the heap
-      // at parse time.  Taking the address of a nonconstant
-      // constructor will not do what the programmer expects.
-      gcc_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return Expression::traverse(&this->expr_, traverse); }
 
-      // Build a decl for a constant constructor.
-      if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr))
-	{
-	  tree decl = build_decl(this->location(), VAR_DECL,
-				 create_tmp_var_name("C"), TREE_TYPE(expr));
-	  DECL_EXTERNAL(decl) = 0;
-	  TREE_PUBLIC(decl) = 0;
-	  TREE_READONLY(decl) = 1;
-	  TREE_CONSTANT(decl) = 1;
-	  TREE_STATIC(decl) = 1;
-	  DECL_ARTIFICIAL(decl) = 1;
-	  DECL_INITIAL(decl) = expr;
-	  rest_of_decl_compilation(decl, 1, 0);
-	  expr = decl;
-	}
+  Expression*
+  do_lower(Gogo*, int);
 
-      return build_fold_addr_expr_loc(loc, expr);
+  bool
+  do_is_constant() const;
 
-    case OPERATOR_MULT:
-      gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr)));
-      return build_fold_indirect_ref_loc(loc, expr);
+  bool
+  do_integer_constant_value(bool, mpz_t, Type**) const;
 
-    default:
-      gcc_unreachable();
-    }
-}
+  bool
+  do_float_constant_value(mpfr_t, Type**) const;
 
-// Export a unary expression.
+  Type*
+  do_type();
 
-void
-Unary_expression::do_export(Export* exp) const
-{
-  switch (this->op_)
-    {
-    case OPERATOR_PLUS:
-      exp->write_c_string("+ ");
-      break;
-    case OPERATOR_MINUS:
-      exp->write_c_string("- ");
-      break;
-    case OPERATOR_NOT:
-      exp->write_c_string("! ");
-      break;
-    case OPERATOR_XOR:
-      exp->write_c_string("^ ");
-      break;
-    case OPERATOR_AND:
-    case OPERATOR_MULT:
-    default:
-      gcc_unreachable();
-    }
-  this->expr_->export_expression(exp);
-}
+  void
+  do_determine_type(const Type_context*);
 
-// Import a unary expression.
+  void
+  do_check_types(Gogo*);
 
-Expression*
-Unary_expression::do_import(Import* imp)
-{
-  Operator op;
-  switch (imp->get_char())
-    {
-    case '+':
-      op = OPERATOR_PLUS;
-      break;
-    case '-':
-      op = OPERATOR_MINUS;
-      break;
-    case '!':
-      op = OPERATOR_NOT;
-      break;
-    case '^':
-      op = OPERATOR_XOR;
-      break;
-    default:
-      gcc_unreachable();
-    }
-  imp->require_c_string(" ");
-  Expression* expr = Expression::import_expression(imp);
-  return Expression::make_unary(op, expr, imp->location());
-}
+  Expression*
+  do_copy()
+  {
+    return Expression::make_unary(this->op_, this->expr_->copy(),
+				  this->location());
+  }
 
-// Make a unary expression.
+  bool
+  do_is_lvalue() const
+  { return this->op_ == OPERATOR_MULT; }
 
-Expression*
-Expression::make_unary(Operator op, Expression* expr, source_location location)
-{
-  return new Unary_expression(op, expr, location);
-}
+  bool
+  do_address_taken(source_location, bool);
 
-// Class Binary_expression.
+  Expression*
+  do_being_copied(Refcounts*, bool);
 
-// Traversal.
+  Expression*
+  do_note_decrements(Refcounts*);
 
-int
-Binary_expression::do_traverse(Traverse* traverse)
-{
-  int t = Expression::traverse(&this->left_, traverse);
-  if (t == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  return Expression::traverse(&this->right_, traverse);
-}
+  Expression*
+  do_being_set(Refcounts*);
 
-// Compare integer constants according to OP.
+  tree
+  do_get_tree(Translate_context*);
 
-bool
-Binary_expression::compare_integer(Operator op, mpz_t left_val,
-				   mpz_t right_val)
+  void
+  do_export(Export*) const;
+
+ private:
+  // The unary operator to apply.
+  Operator op_;
+  // Normally true.  False if this is an address expression which does
+  // not escape the current function.
+  bool escapes_;
+  // The operand.
+  Expression* expr_;
+};
+
+// If we are taking the address of a composite literal, and the
+// contents are not constant, then we want to make a heap composite
+// instead.
+
+Expression*
+Unary_expression::do_lower(Gogo*, int)
 {
-  int i = mpz_cmp(left_val, right_val);
-  switch (op)
+  source_location loc = this->location();
+  Operator op = this->op_;
+  Expression* expr = this->expr_;
+
+  if (op == OPERATOR_MULT && expr->is_type_expression())
+    return Expression::make_type(Type::make_pointer_type(expr->type()), loc);
+
+  // *&x simplifies to x.  *(*T)(unsafe.Pointer)(&x) does not require
+  // *moving x to the heap.  FIXME: Is it worth doing a real escape
+  // *analysis here?  This case is found in math/unsafe.go and is
+  // *therefore worth special casing.
+  if (op == OPERATOR_MULT)
     {
-    case OPERATOR_EQEQ:
-      return i == 0;
-    case OPERATOR_NOTEQ:
-      return i != 0;
-    case OPERATOR_LT:
-      return i < 0;
-    case OPERATOR_LE:
-      return i <= 0;
-    case OPERATOR_GT:
-      return i > 0;
-    case OPERATOR_GE:
-      return i >= 0;
-    default:
-      gcc_unreachable();
+      Expression* e = expr;
+      while (e->classification() == EXPRESSION_CONVERSION)
+	{
+	  Type_conversion_expression* te
+	    = static_cast<Type_conversion_expression*>(e);
+	  e = te->expr();
+	}
+
+      if (e->classification() == EXPRESSION_UNARY)
+	{
+	  Unary_expression* ue = static_cast<Unary_expression*>(e);
+	  if (ue->op_ == OPERATOR_AND)
+	    {
+	      if (e == expr)
+		{
+		  // *&x == x.
+		  return ue->expr_;
+		}
+	      ue->set_does_not_escape();
+	    }
+	}
     }
-}
 
-// Compare floating point constants according to OP.
+  if (op == OPERATOR_PLUS || op == OPERATOR_MINUS
+      || op == OPERATOR_NOT || op == OPERATOR_XOR)
+    {
+      Expression* ret = NULL;
 
-bool
-Binary_expression::compare_float(Operator op, Type* type, mpfr_t left_val,
-				 mpfr_t right_val)
-{
-  int i;
-  if (type == NULL)
-    i = mpfr_cmp(left_val, right_val);
-  else
+      mpz_t eval;
+      mpz_init(eval);
+      Type* etype;
+      if (expr->integer_constant_value(false, eval, &etype))
+	{
+	  mpz_t val;
+	  mpz_init(val);
+	  if (Unary_expression::eval_integer(op, etype, eval, val, loc))
+	    ret = Expression::make_integer(&val, etype, loc);
+	  mpz_clear(val);
+	}
+      mpz_clear(eval);
+      if (ret != NULL)
+	return ret;
+
+      if (op == OPERATOR_PLUS || op == OPERATOR_MINUS)
+	{
+	  mpfr_t fval;
+	  mpfr_init(fval);
+	  Type* ftype;
+	  if (expr->float_constant_value(fval, &ftype))
+	    {
+	      mpfr_t val;
+	      mpfr_init(val);
+	      if (Unary_expression::eval_float(op, fval, val))
+		ret = Expression::make_float(&val, ftype, loc);
+	      mpfr_clear(val);
+	    }
+	  mpfr_clear(fval);
+	  if (ret != NULL)
+	    return ret;
+	}
+    }
+
+  return this;
+}
+
+// Return whether a unary expression is a constant.
+
+bool
+Unary_expression::do_is_constant() const
+{
+  if (this->op_ == OPERATOR_MULT)
     {
-      mpfr_t lv;
-      mpfr_init_set(lv, left_val, GMP_RNDN);
-      mpfr_t rv;
-      mpfr_init_set(rv, right_val, GMP_RNDN);
-      Float_expression::constrain_float(lv, type);
-      Float_expression::constrain_float(rv, type);
-      i = mpfr_cmp(lv, rv);
-      mpfr_clear(lv);
-      mpfr_clear(rv);
+      // Indirecting through a pointer is only constant if the object
+      // to which the expression points is constant, but we currently
+      // have no way to determine that.
+      return false;
     }
-  switch (op)
+  else if (this->op_ == OPERATOR_AND)
     {
-    case OPERATOR_EQEQ:
-      return i == 0;
-    case OPERATOR_NOTEQ:
-      return i != 0;
-    case OPERATOR_LT:
-      return i < 0;
-    case OPERATOR_LE:
-      return i <= 0;
-    case OPERATOR_GT:
-      return i > 0;
-    case OPERATOR_GE:
-      return i >= 0;
-    default:
-      gcc_unreachable();
+      // Taking the address of a variable is constant if it is a
+      // global variable, not constant otherwise.  In other cases
+      // taking the address is probably not a constant.
+      Var_expression* ve = this->expr_->var_expression();
+      if (ve != NULL)
+	{
+	  Named_object* no = ve->named_object();
+	  return no->is_variable() && no->var_value()->is_global();
+	}
+      return false;
     }
+  else
+    return this->expr_->is_constant();
 }
 
-// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
-// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
-// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL.  Return true if
-// this could be done, false if not.
+// Apply unary opcode OP to UVAL, setting VAL.  UTYPE is the type of
+// UVAL, if known; it may be NULL.  Return true if this could be done,
+// false if not.
 
 bool
-Binary_expression::eval_integer(Operator op, Type* left_type, mpz_t left_val,
-				Type* right_type, mpz_t right_val,
-				source_location location, mpz_t val)
+Unary_expression::eval_integer(Operator op, Type* utype, mpz_t uval, mpz_t val,
+			       source_location location)
 {
-  bool is_shift_op = false;
   switch (op)
     {
-    case OPERATOR_OROR:
-    case OPERATOR_ANDAND:
-    case OPERATOR_EQEQ:
-    case OPERATOR_NOTEQ:
-    case OPERATOR_LT:
-    case OPERATOR_LE:
-    case OPERATOR_GT:
-    case OPERATOR_GE:
-      // These return boolean values.  We should probably handle them
-      // anyhow in case a type conversion is used on the result.
-      return false;
     case OPERATOR_PLUS:
-      mpz_add(val, left_val, right_val);
-      break;
+      mpz_set(val, uval);
+      return true;
     case OPERATOR_MINUS:
-      mpz_sub(val, left_val, right_val);
-      break;
-    case OPERATOR_OR:
-      mpz_ior(val, left_val, right_val);
-      break;
+      mpz_neg(val, uval);
+      return Integer_expression::check_constant(val, utype, location);
+    case OPERATOR_NOT:
+      mpz_set_ui(val, mpz_cmp_si(uval, 0) == 0 ? 1 : 0);
+      return true;
     case OPERATOR_XOR:
-      mpz_xor(val, left_val, right_val);
-      break;
-    case OPERATOR_MULT:
-      mpz_mul(val, left_val, right_val);
-      break;
-    case OPERATOR_DIV:
-      if (mpz_sgn(right_val) != 0)
-	mpz_tdiv_q(val, left_val, right_val);
-      else
-	{
-	  error_at(location, "division by zero");
-	  mpz_set_ui(val, 0);
-	  return true;
-	}
-      break;
-    case OPERATOR_MOD:
-      if (mpz_sgn(right_val) != 0)
-	mpz_tdiv_r(val, left_val, right_val);
+      if (utype == NULL
+	  || utype->integer_type() == NULL
+	  || utype->integer_type()->is_abstract())
+	mpz_com(val, uval);
       else
 	{
-	  error_at(location, "division by zero");
-	  mpz_set_ui(val, 0);
-	  return true;
+	  // The number of HOST_WIDE_INTs that it takes to represent
+	  // UVAL.
+	  size_t count = ((mpz_sizeinbase(uval, 2)
+			   + HOST_BITS_PER_WIDE_INT
+			   - 1)
+			  / HOST_BITS_PER_WIDE_INT);
+
+	  unsigned HOST_WIDE_INT* phwi = new unsigned HOST_WIDE_INT[count];
+	  memset(phwi, 0, count * sizeof(HOST_WIDE_INT));
+
+	  size_t ecount;
+	  mpz_export(phwi, &ecount, -1, sizeof(HOST_WIDE_INT), 0, 0, uval);
+	  gcc_assert(ecount <= count);
+
+	  // Trim down to the number of words required by the type.
+	  size_t obits = utype->integer_type()->bits();
+	  if (!utype->integer_type()->is_unsigned())
+	    ++obits;
+	  size_t ocount = ((obits + HOST_BITS_PER_WIDE_INT - 1)
+			   / HOST_BITS_PER_WIDE_INT);
+	  gcc_assert(ocount <= ocount);
+
+	  for (size_t i = 0; i < ocount; ++i)
+	    phwi[i] = ~phwi[i];
+
+	  size_t clearbits = ocount * HOST_BITS_PER_WIDE_INT - obits;
+	  if (clearbits != 0)
+	    phwi[ocount - 1] &= (((unsigned HOST_WIDE_INT) (HOST_WIDE_INT) -1)
+				 >> clearbits);
+
+	  mpz_import(val, ocount, -1, sizeof(HOST_WIDE_INT), 0, 0, phwi);
+
+	  delete[] phwi;
 	}
-      break;
-    case OPERATOR_LSHIFT:
-      {
-	unsigned long shift = mpz_get_ui(right_val);
-	if (mpz_cmp_ui(right_val, shift) != 0)
-	  {
-	    error_at(location, "shift count overflow");
-	    mpz_set_ui(val, 0);
-	    return true;
-	  }
-	mpz_mul_2exp(val, left_val, shift);
-	is_shift_op = true;
-	break;
-      }
-      break;
-    case OPERATOR_RSHIFT:
-      {
-	unsigned long shift = mpz_get_ui(right_val);
-	if (mpz_cmp_ui(right_val, shift) != 0)
-	  {
-	    error_at(location, "shift count overflow");
-	    mpz_set_ui(val, 0);
-	    return true;
-	  }
-	if (mpz_cmp_ui(left_val, 0) >= 0)
-	  mpz_tdiv_q_2exp(val, left_val, shift);
-	else
-	  mpz_fdiv_q_2exp(val, left_val, shift);
-	is_shift_op = true;
-	break;
-      }
-      break;
+      return Integer_expression::check_constant(val, utype, location);
     case OPERATOR_AND:
-      mpz_and(val, left_val, right_val);
-      break;
-    case OPERATOR_BITCLEAR:
-      {
-	mpz_t tval;
-	mpz_init(tval);
-	mpz_com(tval, right_val);
-	mpz_and(val, left_val, tval);
-	mpz_clear(tval);
-      }
-      break;
+    case OPERATOR_MULT:
+      return false;
     default:
       gcc_unreachable();
     }
+}
 
-  Type* type = left_type;
-  if (!is_shift_op)
-    {
-      if (type == NULL)
-	type = right_type;
-      else if (type != right_type && right_type != NULL)
-	{
-	  if (type->is_abstract())
-	    type = right_type;
-	  else if (!right_type->is_abstract())
-	    {
-	      // This look like a type error which should be diagnosed
-	      // elsewhere.  Don't do anything here, to avoid an
-	      // unhelpful chain of error messages.
-	      return true;
-	    }
-	}
-    }
+// Apply unary opcode OP to UVAL, setting VAL.  Return true if this
+// could be done, false if not.
 
-  if (type != NULL && !type->is_abstract())
+bool
+Unary_expression::eval_float(Operator op, mpfr_t uval, mpfr_t val)
+{
+  switch (op)
     {
-      // We have to check the operands too, as we have implicitly
-      // coerced them to TYPE.
-      if ((type != left_type
-	   && !Integer_expression::check_constant(left_val, type, location))
-	  || (!is_shift_op
-	      && type != right_type
-	      && !Integer_expression::check_constant(right_val, type,
-						     location))
-	  || !Integer_expression::check_constant(val, type, location))
-	mpz_set_ui(val, 0);
+    case OPERATOR_PLUS:
+      mpfr_set(val, uval, GMP_RNDN);
+      return true;
+    case OPERATOR_MINUS:
+      mpfr_neg(val, uval, GMP_RNDN);
+      return true;
+    case OPERATOR_NOT:
+    case OPERATOR_XOR:
+    case OPERATOR_AND:
+    case OPERATOR_MULT:
+      return false;
+    default:
+      gcc_unreachable();
     }
+}
 
-  return true;
+// Return the integral constant value of a unary expression, if it has one.
+
+bool
+Unary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
+					    Type** ptype) const
+{
+  mpz_t uval;
+  mpz_init(uval);
+  bool ret;
+  if (!this->expr_->integer_constant_value(iota_is_constant, uval, ptype))
+    ret = false;
+  else
+    ret = Unary_expression::eval_integer(this->op_, *ptype, uval, val,
+					 this->location());
+  mpz_clear(uval);
+  return ret;
 }
 
-// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
-// Return true if this could be done, false if not.
+// Return the floating point constant value of a unary expression, if
+// it has one.
 
 bool
-Binary_expression::eval_float(Operator op, Type* left_type, mpfr_t left_val,
-			      Type* right_type, mpfr_t right_val,
-			      mpfr_t val, source_location location)
+Unary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
 {
-  switch (op)
+  mpfr_t uval;
+  mpfr_init(uval);
+  bool ret;
+  if (!this->expr_->float_constant_value(uval, ptype))
+    ret = false;
+  else
+    ret = Unary_expression::eval_float(this->op_, uval, val);
+  mpfr_clear(uval);
+  return ret;
+}
+
+// Return the type of a unary expression.
+
+Type*
+Unary_expression::do_type()
+{
+  switch (this->op_)
     {
-    case OPERATOR_OROR:
-    case OPERATOR_ANDAND:
-    case OPERATOR_EQEQ:
-    case OPERATOR_NOTEQ:
-    case OPERATOR_LT:
-    case OPERATOR_LE:
-    case OPERATOR_GT:
-    case OPERATOR_GE:
-      // These return boolean values.  We should probably handle them
-      // anyhow in case a type conversion is used on the result.
-      return false;
     case OPERATOR_PLUS:
-      mpfr_add(val, left_val, right_val, GMP_RNDN);
-      break;
     case OPERATOR_MINUS:
-      mpfr_sub(val, left_val, right_val, GMP_RNDN);
-      break;
-    case OPERATOR_OR:
+    case OPERATOR_NOT:
     case OPERATOR_XOR:
+      return this->expr_->type();
+
     case OPERATOR_AND:
-    case OPERATOR_BITCLEAR:
-      return false;
+      return Type::make_pointer_type(this->expr_->type());
+
     case OPERATOR_MULT:
-      mpfr_mul(val, left_val, right_val, GMP_RNDN);
-      break;
-    case OPERATOR_DIV:
-      if (mpfr_zero_p(right_val))
-	error_at(location, "division by zero");
-      mpfr_div(val, left_val, right_val, GMP_RNDN);
-      break;
-    case OPERATOR_MOD:
-      return false;
-    case OPERATOR_LSHIFT:
-    case OPERATOR_RSHIFT:
-      return false;
+      {
+	Type* subtype = this->expr_->type();
+	Type* points_to = subtype->points_to();
+	if (points_to == NULL)
+	  return Type::make_error_type();
+	return points_to;
+      }
+
     default:
       gcc_unreachable();
     }
+}
 
-  Type* type = left_type;
-  if (type == NULL)
-    type = right_type;
-  else if (type != right_type && right_type != NULL)
+// Determine abstract types for a unary expression.
+
+void
+Unary_expression::do_determine_type(const Type_context* context)
+{
+  switch (this->op_)
     {
-      if (type->is_abstract())
-	type = right_type;
-      else if (!right_type->is_abstract())
-	{
-	  // This looks like a type error which should be diagnosed
-	  // elsewhere.  Don't do anything here, to avoid an unhelpful
-	  // chain of error messages.
-	  return true;
-	}
+    case OPERATOR_PLUS:
+    case OPERATOR_MINUS:
+    case OPERATOR_NOT:
+    case OPERATOR_XOR:
+      this->expr_->determine_type(context);
+      break;
+
+    case OPERATOR_AND:
+      // Taking the address of something.
+      {
+	Type* subtype = (context->type == NULL
+			 ? NULL
+			 : context->type->points_to());
+	Type_context subcontext(subtype, false);
+	this->expr_->determine_type(&subcontext);
+      }
+      break;
+
+    case OPERATOR_MULT:
+      // Indirecting through a pointer.
+      {
+	Type* subtype = (context->type == NULL
+			 ? NULL
+			 : Type::make_pointer_type(context->type));
+	Type_context subcontext(subtype, false);
+	this->expr_->determine_type(&subcontext);
+      }
+      break;
+
+    default:
+      gcc_unreachable();
     }
+}
 
-  if (type != NULL && !type->is_abstract())
+// Check types for a unary expression.
+
+void
+Unary_expression::do_check_types(Gogo*)
+{
+  switch (this->op_)
     {
-      if ((type != left_type
-	   && !Float_expression::check_constant(left_val, type, location))
-	  || (type != right_type
-	      && !Float_expression::check_constant(right_val, type,
-						   location))
-	  || !Float_expression::check_constant(val, type, location))
-	mpfr_set_ui(val, 0, GMP_RNDN);
+    case OPERATOR_PLUS:
+    case OPERATOR_MINUS:
+      {
+	Type* type = this->expr_->type();
+	if (type->integer_type() == NULL
+	    && type->float_type() == NULL
+	    && !type->is_error_type())
+	  this->report_error(_("expected numeric type"));
+      }
+      break;
+
+    case OPERATOR_NOT:
+    case OPERATOR_XOR:
+      {
+	Type* type = this->expr_->type();
+	if (type->integer_type() == NULL
+	    && !type->is_boolean_type()
+	    && !type->is_error_type())
+	  this->report_error(_("expected integer or boolean type"));
+      }
+      break;
+
+    case OPERATOR_AND:
+      if (!this->expr_->address_taken(this->location(), this->escapes_))
+	this->set_is_error();
+      break;
+
+    case OPERATOR_MULT:
+      // Indirecting through a pointer.
+      {
+	Type* type = this->expr_->type();
+	if (type->points_to() == NULL
+	    && !type->is_error_type())
+	  this->report_error(_("expected pointer"));
+      }
+      break;
+
+    default:
+      gcc_unreachable();
     }
+}
 
-  return true;
+// &*p is OK.
+
+bool
+Unary_expression::do_address_taken(source_location location, bool)
+{
+  if (this->op_ == OPERATOR_MULT)
+    return true;
+  this->report_address_taken_error(location);
+  return false;
 }
 
-// Lower a binary expression.  We have to evaluate constant
-// expressions now, in order t implemented Go's unlimited precision
-// constants.
+// Copying a unary expression may require incrementing a reference
+// count.
 
 Expression*
-Binary_expression::do_lower(Gogo*, int)
+Unary_expression::do_being_copied(Refcounts* refcounts, bool for_local)
 {
-  source_location location = this->location();
-  Operator op = this->op_;
-  Expression* left = this->left_;
-  Expression* right = this->right_;
+  if (!this->type()->has_refcounted_component())
+    return this;
+  if (this->op_ == OPERATOR_PLUS)
+    {
+      this->expr_ = this->expr_->being_copied(refcounts, for_local);
+      return this;
+    }
+  else if (this->op_ == OPERATOR_AND && this->is_constant())
+    {
+      // No need to increment the reference count when the address is
+      // a constant.
+      return this;
+    }
+  else
+    return Expression::make_refcount_adjust(refcounts,
+					    REFCOUNT_INCREMENT_COPIED,
+					    this, for_local);
+}
 
-  const bool is_comparison = (op == OPERATOR_EQEQ
-			      || op == OPERATOR_NOTEQ
-			      || op == OPERATOR_LT
-			      || op == OPERATOR_LE
-			      || op == OPERATOR_GT
-			      || op == OPERATOR_GE);
+// A unary expression does not increment any reference counts, so
+// there are no reference counts to decrement afterward.
 
-  // Integer constant expressions.
-  {
-    mpz_t left_val;
-    mpz_init(left_val);
-    Type* left_type;
-    mpz_t right_val;
-    mpz_init(right_val);
-    Type* right_type;
-    if (left->integer_constant_value(false, left_val, &left_type)
-	&& right->integer_constant_value(false, right_val, &right_type))
-      {
-	Expression* ret = NULL;
-	if (left_type != right_type
-	    && left_type != NULL
-	    && right_type != NULL
-	    && left_type->base() != right_type->base()
-	    && op != OPERATOR_LSHIFT
-	    && op != OPERATOR_RSHIFT)
-	  {
-	    // May be a type error--let it be diagnosed later.
-	  }
-	else if (is_comparison)
-	  {
-	    bool b = Binary_expression::compare_integer(op, left_val,
-							right_val);
-	    ret = Expression::make_cast(Type::lookup_bool_type(),
-					Expression::make_boolean(b, location),
-					location);
-	  }
-	else
-	  {
-	    mpz_t val;
-	    mpz_init(val);
+Expression*
+Unary_expression::do_note_decrements(Refcounts*)
+{
+  return this;
+}
 
-	    if (Binary_expression::eval_integer(op, left_type, left_val,
-						right_type, right_val,
-						location, val))
-	      {
-		gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND);
-		Type* type;
-		if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT)
-		  type = left_type;
-		else if (left_type == NULL)
-		  type = right_type;
-		else if (right_type == NULL)
-		  type = left_type;
-		else if (!left_type->is_abstract()
-			 && left_type->named_type() != NULL)
-		  type = left_type;
-		else if (!right_type->is_abstract()
-			 && right_type->named_type() != NULL)
-		  type = right_type;
-		else if (!left_type->is_abstract())
-		  type = left_type;
-		else if (!right_type->is_abstract())
-		  type = right_type;
-		else if (left_type->float_type() != NULL)
-		  type = left_type;
-		else if (right_type->float_type() != NULL)
-		  type = right_type;
-		else
-		  type = left_type;
-		ret = Expression::make_integer(&val, type, location);
-	      }
+// Assigning to *p requires decrementing the reference count of the
+// old value.
 
-	    mpz_clear(val);
-	  }
+Expression*
+Unary_expression::do_being_set(Refcounts* refcounts)
+{
+  gcc_assert(this->op_ == OPERATOR_MULT);
+  if (!this->type()->has_refcounted_component())
+    return this;
+  return Expression::make_refcount_decrement_lvalue(refcounts, this);
+}
 
-	if (ret != NULL)
-	  {
-	    mpz_clear(right_val);
-	    mpz_clear(left_val);
-	    return ret;
-	  }
-      }
-    mpz_clear(right_val);
-    mpz_clear(left_val);
-  }
+// Get a tree for a unary expression.
 
-  // Floating point constant expressions.
-  {
-    mpfr_t left_val;
-    mpfr_init(left_val);
-    Type* left_type;
-    mpfr_t right_val;
-    mpfr_init(right_val);
-    Type* right_type;
-    if (left->float_constant_value(left_val, &left_type)
-	&& right->float_constant_value(right_val, &right_type))
+tree
+Unary_expression::do_get_tree(Translate_context* context)
+{
+  tree expr = this->expr_->get_tree(context);
+  if (expr == error_mark_node)
+    return error_mark_node;
+
+  source_location loc = this->location();
+  switch (this->op_)
+    {
+    case OPERATOR_PLUS:
+      return expr;
+
+    case OPERATOR_MINUS:
       {
-	Expression* ret = NULL;
-	if (left_type != right_type
-	    && left_type != NULL
-	    && right_type != NULL
-	    && left_type->base() != right_type->base()
-	    && op != OPERATOR_LSHIFT
-	    && op != OPERATOR_RSHIFT)
-	  {
-	    // May be a type error--let it be diagnosed later.
-	  }
-	else if (is_comparison)
-	  {
-	    bool b = Binary_expression::compare_float(op,
-						      (left_type != NULL
-						       ? left_type
-						       : right_type),
-						      left_val, right_val);
-	    ret = Expression::make_boolean(b, location);
-	  }
-	else
-	  {
-	    mpfr_t val;
-	    mpfr_init(val);
+	tree type = TREE_TYPE(expr);
+	tree compute_type = excess_precision_type(type);
+	if (compute_type != NULL_TREE)
+	  expr = convert_to_real(compute_type, expr);
+	tree ret = fold_build1_loc(loc, NEGATE_EXPR,
+				   (compute_type != NULL_TREE
+				    ? compute_type
+				    : type),
+				   expr);
+	if (compute_type != NULL_TREE)
+	  ret = convert_to_real(type, ret);
+	return ret;
+      }
 
-	    if (Binary_expression::eval_float(op, left_type, left_val,
-					      right_type, right_val, val,
-					      location))
-	      {
-		gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND
-			   && op != OPERATOR_LSHIFT && op != OPERATOR_RSHIFT);
-		Type* type;
-		if (left_type == NULL)
-		  type = right_type;
-		else if (right_type == NULL)
-		  type = left_type;
-		else if (!left_type->is_abstract()
-		    && left_type->named_type() != NULL)
-		  type = left_type;
-		else if (!right_type->is_abstract()
-			 && right_type->named_type() != NULL)
-		  type = right_type;
-		else if (!left_type->is_abstract())
-		  type = left_type;
-		else if (!right_type->is_abstract())
-		  type = right_type;
-		else if (left_type->float_type() != NULL)
-		  type = left_type;
-		else if (right_type->float_type() != NULL)
-		  type = right_type;
-		else
-		  type = left_type;
-		ret = Expression::make_float(&val, type, location);
-	      }
+    case OPERATOR_NOT:
+      if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
+	return fold_build1_loc(loc, TRUTH_NOT_EXPR, TREE_TYPE(expr), expr);
+      else
+	return fold_build2_loc(loc, NE_EXPR, boolean_type_node, expr,
+			       build_int_cst(TREE_TYPE(expr), 0));
 
-	    mpfr_clear(val);
-	  }
+    case OPERATOR_XOR:
+      return fold_build1_loc(loc, BIT_NOT_EXPR, TREE_TYPE(expr), expr);
 
-	if (ret != NULL)
-	  {
-	    mpfr_clear(right_val);
-	    mpfr_clear(left_val);
-	    return ret;
-	  }
-      }
-    mpfr_clear(right_val);
-    mpfr_clear(left_val);
-  }
+    case OPERATOR_AND:
+      // We should not see a non-constant constructor here; cases
+      // where we would see one should have been moved onto the heap
+      // at parse time.  Taking the address of a nonconstant
+      // constructor will not do what the programmer expects.
+      gcc_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
 
-  // String constant expressions.
-  if (op == OPERATOR_PLUS
-      && left->type()->is_string_type()
-      && right->type()->is_string_type())
-    {
-      std::string left_string;
-      std::string right_string;
-      if (left->string_constant_value(&left_string)
-	  && right->string_constant_value(&right_string))
-	return Expression::make_string(left_string + right_string, location);
-    }
+      // Build a decl for a constant constructor.
+      if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr))
+	{
+	  tree decl = build_decl(this->location(), VAR_DECL,
+				 create_tmp_var_name("C"), TREE_TYPE(expr));
+	  DECL_EXTERNAL(decl) = 0;
+	  TREE_PUBLIC(decl) = 0;
+	  TREE_READONLY(decl) = 1;
+	  TREE_CONSTANT(decl) = 1;
+	  TREE_STATIC(decl) = 1;
+	  DECL_ARTIFICIAL(decl) = 1;
+	  DECL_INITIAL(decl) = expr;
+	  rest_of_decl_compilation(decl, 1, 0);
+	  expr = decl;
+	}
 
-  return this;
+      return build_fold_addr_expr_loc(loc, expr);
+
+    case OPERATOR_MULT:
+      gcc_assert(POINTER_TYPE_P(TREE_TYPE(expr)));
+      return build_fold_indirect_ref_loc(loc, expr);
+
+    default:
+      gcc_unreachable();
+    }
 }
 
-// Return the integer constant value, if it has one.
+// Export a unary expression.
 
-bool
-Binary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
-					     Type** ptype) const
+void
+Unary_expression::do_export(Export* exp) const
 {
-  mpz_t left_val;
-  mpz_init(left_val);
-  Type* left_type;
-  if (!this->left_->integer_constant_value(iota_is_constant, left_val,
-					   &left_type))
+  switch (this->op_)
     {
-      mpz_clear(left_val);
-      return false;
+    case OPERATOR_PLUS:
+      exp->write_c_string("+ ");
+      break;
+    case OPERATOR_MINUS:
+      exp->write_c_string("- ");
+      break;
+    case OPERATOR_NOT:
+      exp->write_c_string("! ");
+      break;
+    case OPERATOR_XOR:
+      exp->write_c_string("^ ");
+      break;
+    case OPERATOR_AND:
+    case OPERATOR_MULT:
+    default:
+      gcc_unreachable();
     }
+  this->expr_->export_expression(exp);
+}
 
-  mpz_t right_val;
-  mpz_init(right_val);
-  Type* right_type;
-  if (!this->right_->integer_constant_value(iota_is_constant, right_val,
-					    &right_type))
+// Import a unary expression.
+
+Expression*
+Unary_expression::do_import(Import* imp)
+{
+  Operator op;
+  switch (imp->get_char())
     {
-      mpz_clear(right_val);
-      mpz_clear(left_val);
-      return false;
+    case '+':
+      op = OPERATOR_PLUS;
+      break;
+    case '-':
+      op = OPERATOR_MINUS;
+      break;
+    case '!':
+      op = OPERATOR_NOT;
+      break;
+    case '^':
+      op = OPERATOR_XOR;
+      break;
+    default:
+      gcc_unreachable();
     }
+  imp->require_c_string(" ");
+  Expression* expr = Expression::import_expression(imp);
+  return Expression::make_unary(op, expr, imp->location());
+}
 
-  bool ret;
-  if (left_type != right_type
-      && left_type != NULL
-      && right_type != NULL
-      && left_type->base() != right_type->base()
-      && this->op_ != OPERATOR_RSHIFT
-      && this->op_ != OPERATOR_LSHIFT)
-    ret = false;
-  else
-    ret = Binary_expression::eval_integer(this->op_, left_type, left_val,
-					  right_type, right_val,
-					  this->location(), val);
+// Make a unary expression.
 
-  mpz_clear(right_val);
-  mpz_clear(left_val);
+Expression*
+Expression::make_unary(Operator op, Expression* expr, source_location location)
+{
+  return new Unary_expression(op, expr, location);
+}
 
-  if (ret)
-    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
-	      ? right_type
-	      : left_type);
+// Class Binary_expression.
 
-  return ret;
+// Traversal.
+
+int
+Binary_expression::do_traverse(Traverse* traverse)
+{
+  int t = Expression::traverse(&this->left_, traverse);
+  if (t == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  return Expression::traverse(&this->right_, traverse);
 }
 
-// Return the floating point constant value, if it has one.
+// Compare integer constants according to OP.
 
 bool
-Binary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+Binary_expression::compare_integer(Operator op, mpz_t left_val,
+				   mpz_t right_val)
 {
-  mpfr_t left_val;
-  mpfr_init(left_val);
-  Type* left_type;
-  if (!this->left_->float_constant_value(left_val, &left_type))
-    {
-      mpfr_clear(left_val);
-      return false;
-    }
-
-  mpfr_t right_val;
-  mpfr_init(right_val);
-  Type* right_type;
-  if (!this->right_->float_constant_value(right_val, &right_type))
+  int i = mpz_cmp(left_val, right_val);
+  switch (op)
     {
-      mpfr_clear(right_val);
-      mpfr_clear(left_val);
-      return false;
+    case OPERATOR_EQEQ:
+      return i == 0;
+    case OPERATOR_NOTEQ:
+      return i != 0;
+    case OPERATOR_LT:
+      return i < 0;
+    case OPERATOR_LE:
+      return i <= 0;
+    case OPERATOR_GT:
+      return i > 0;
+    case OPERATOR_GE:
+      return i >= 0;
+    default:
+      gcc_unreachable();
     }
-
-  bool ret;
-  if (left_type != right_type
-      && left_type != NULL
-      && right_type != NULL
-      && left_type->base() != right_type->base())
-    ret = false;
-  else
-    ret = Binary_expression::eval_float(this->op_, left_type, left_val,
-					right_type, right_val,
-					val, this->location());
-
-  mpfr_clear(left_val);
-  mpfr_clear(right_val);
-
-  if (ret)
-    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
-	      ? right_type
-	      : left_type);
-
-  return ret;
 }
 
-// Note that the value is being discarded.
+// Compare floating point constants according to OP.
 
-void
-Binary_expression::do_discarding_value()
+bool
+Binary_expression::compare_float(Operator op, Type* type, mpfr_t left_val,
+				 mpfr_t right_val)
 {
-  if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND)
-    this->right_->discarding_value();
+  int i;
+  if (type == NULL)
+    i = mpfr_cmp(left_val, right_val);
   else
-    this->warn_unused_value();
+    {
+      mpfr_t lv;
+      mpfr_init_set(lv, left_val, GMP_RNDN);
+      mpfr_t rv;
+      mpfr_init_set(rv, right_val, GMP_RNDN);
+      Float_expression::constrain_float(lv, type);
+      Float_expression::constrain_float(rv, type);
+      i = mpfr_cmp(lv, rv);
+      mpfr_clear(lv);
+      mpfr_clear(rv);
+    }
+  switch (op)
+    {
+    case OPERATOR_EQEQ:
+      return i == 0;
+    case OPERATOR_NOTEQ:
+      return i != 0;
+    case OPERATOR_LT:
+      return i < 0;
+    case OPERATOR_LE:
+      return i <= 0;
+    case OPERATOR_GT:
+      return i > 0;
+    case OPERATOR_GE:
+      return i >= 0;
+    default:
+      gcc_unreachable();
+    }
 }
 
-// Get type.
+// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
+// LEFT_TYPE is the type of LEFT_VAL, RIGHT_TYPE is the type of
+// RIGHT_VAL; LEFT_TYPE and/or RIGHT_TYPE may be NULL.  Return true if
+// this could be done, false if not.
 
-Type*
-Binary_expression::do_type()
+bool
+Binary_expression::eval_integer(Operator op, Type* left_type, mpz_t left_val,
+				Type* right_type, mpz_t right_val,
+				source_location location, mpz_t val)
 {
-  switch (this->op_)
+  bool is_shift_op = false;
+  switch (op)
     {
     case OPERATOR_OROR:
     case OPERATOR_ANDAND:
@@ -3506,2991 +3585,3098 @@ Binary_expression::do_type()
     case OPERATOR_LE:
     case OPERATOR_GT:
     case OPERATOR_GE:
-      return Type::lookup_bool_type();
-
+      // These return boolean values.  We should probably handle them
+      // anyhow in case a type conversion is used on the result.
+      return false;
     case OPERATOR_PLUS:
+      mpz_add(val, left_val, right_val);
+      break;
     case OPERATOR_MINUS:
+      mpz_sub(val, left_val, right_val);
+      break;
     case OPERATOR_OR:
+      mpz_ior(val, left_val, right_val);
+      break;
     case OPERATOR_XOR:
+      mpz_xor(val, left_val, right_val);
+      break;
     case OPERATOR_MULT:
+      mpz_mul(val, left_val, right_val);
+      break;
     case OPERATOR_DIV:
+      if (mpz_sgn(right_val) != 0)
+	mpz_tdiv_q(val, left_val, right_val);
+      else
+	{
+	  error_at(location, "division by zero");
+	  mpz_set_ui(val, 0);
+	  return true;
+	}
+      break;
     case OPERATOR_MOD:
-    case OPERATOR_AND:
-    case OPERATOR_BITCLEAR:
+      if (mpz_sgn(right_val) != 0)
+	mpz_tdiv_r(val, left_val, right_val);
+      else
+	{
+	  error_at(location, "division by zero");
+	  mpz_set_ui(val, 0);
+	  return true;
+	}
+      break;
+    case OPERATOR_LSHIFT:
       {
-	Type* left_type = this->left_->type();
-	Type* right_type = this->right_->type();
-	if (!left_type->is_abstract() && left_type->named_type() != NULL)
-	  return left_type;
-	else if (!right_type->is_abstract() && right_type->named_type() != NULL)
-	  return right_type;
-	else if (!left_type->is_abstract())
-	  return left_type;
-	else if (!right_type->is_abstract())
-	  return right_type;
-	else if (left_type->float_type() != NULL)
-	  return left_type;
-	else if (right_type->float_type() != NULL)
-	  return right_type;
-	else
-	  return left_type;
+	unsigned long shift = mpz_get_ui(right_val);
+	if (mpz_cmp_ui(right_val, shift) != 0)
+	  {
+	    error_at(location, "shift count overflow");
+	    mpz_set_ui(val, 0);
+	    return true;
+	  }
+	mpz_mul_2exp(val, left_val, shift);
+	is_shift_op = true;
+	break;
       }
-
-    case OPERATOR_LSHIFT:
+      break;
     case OPERATOR_RSHIFT:
-      return this->left_->type();
-
+      {
+	unsigned long shift = mpz_get_ui(right_val);
+	if (mpz_cmp_ui(right_val, shift) != 0)
+	  {
+	    error_at(location, "shift count overflow");
+	    mpz_set_ui(val, 0);
+	    return true;
+	  }
+	if (mpz_cmp_ui(left_val, 0) >= 0)
+	  mpz_tdiv_q_2exp(val, left_val, shift);
+	else
+	  mpz_fdiv_q_2exp(val, left_val, shift);
+	is_shift_op = true;
+	break;
+      }
+      break;
+    case OPERATOR_AND:
+      mpz_and(val, left_val, right_val);
+      break;
+    case OPERATOR_BITCLEAR:
+      {
+	mpz_t tval;
+	mpz_init(tval);
+	mpz_com(tval, right_val);
+	mpz_and(val, left_val, tval);
+	mpz_clear(tval);
+      }
+      break;
     default:
       gcc_unreachable();
     }
-}
-
-// Set type for a binary expression.
-
-void
-Binary_expression::do_determine_type(const Type_context* context)
-{
-  Type* tleft = this->left_->type();
-  Type* tright = this->right_->type();
-
-  // Both sides should have the same type, except for the shift
-  // operations.  For a comparison, we should ignore the incoming
-  // type.
-
-  bool is_shift_op = (this->op_ == OPERATOR_LSHIFT
-		      || this->op_ == OPERATOR_RSHIFT);
-
-  bool is_comparison = (this->op_ == OPERATOR_EQEQ
-			|| this->op_ == OPERATOR_NOTEQ
-			|| this->op_ == OPERATOR_LT
-			|| this->op_ == OPERATOR_LE
-			|| this->op_ == OPERATOR_GT
-			|| this->op_ == OPERATOR_GE);
-
-  Type_context subcontext(*context);
-
-  if (is_comparison)
-    {
-      // In a comparison, the context does not determine the types of
-      // the operands.
-      subcontext.type = NULL;
-    }
 
-  // Set the context for the left hand operand.
-  if (is_shift_op)
-    {
-      // The right hand operand plays no role in determining the type
-      // of the left hand operand.  A shift of an abstract integer in
-      // a string context gets special treatment, which may be a
-      // language bug.
-      if (subcontext.type != NULL
-	  && subcontext.type->is_string_type()
-	  && tleft->is_abstract())
-	error_at(this->location(), "shift of non-integer operand");
-    }
-  else if (!tleft->is_abstract())
-    subcontext.type = tleft;
-  else if (!tright->is_abstract())
-    subcontext.type = tright;
-  else if (subcontext.type == NULL)
+  Type* type = left_type;
+  if (!is_shift_op)
     {
-      if ((tleft->integer_type() != NULL && tright->integer_type() != NULL)
-	  || (tleft->float_type() != NULL && tright->float_type() != NULL))
-	{
-	  // Both sides have an abstract integer type or both sides
-	  // have an abstract float type.  Just let CONTEXT determine
-	  // whether they may remain abstract or not.
-	}
-      else
+      if (type == NULL)
+	type = right_type;
+      else if (type != right_type && right_type != NULL)
 	{
-	  // Both sides are abstract, but one is integer and one is
-	  // floating point.  Convert the abstract integer to floating
-	  // point.
-	  subcontext.type = tleft->float_type() != NULL ? tleft : tright;
+	  if (type->is_abstract())
+	    type = right_type;
+	  else if (!right_type->is_abstract())
+	    {
+	      // This look like a type error which should be diagnosed
+	      // elsewhere.  Don't do anything here, to avoid an
+	      // unhelpful chain of error messages.
+	      return true;
+	    }
 	}
     }
 
-  this->left_->determine_type(&subcontext);
-
-  // The context for the right hand operand is the same as for the
-  // left hand operand, except for a shift operator.
-  if (is_shift_op)
+  if (type != NULL && !type->is_abstract())
     {
-      subcontext.type = Type::lookup_integer_type("uint");
-      subcontext.may_be_abstract = false;
+      // We have to check the operands too, as we have implicitly
+      // coerced them to TYPE.
+      if ((type != left_type
+	   && !Integer_expression::check_constant(left_val, type, location))
+	  || (!is_shift_op
+	      && type != right_type
+	      && !Integer_expression::check_constant(right_val, type,
+						     location))
+	  || !Integer_expression::check_constant(val, type, location))
+	mpz_set_ui(val, 0);
     }
 
-  this->right_->determine_type(&subcontext);
+  return true;
 }
 
-// Report an error if the binary operator OP does not support TYPE.
-// Return whether the operation is OK.  This should not be used for
-// shift.
+// Apply binary opcode OP to LEFT_VAL and RIGHT_VAL, setting VAL.
+// Return true if this could be done, false if not.
 
 bool
-Binary_expression::check_operator_type(Operator op, Type* type,
-				       source_location location)
+Binary_expression::eval_float(Operator op, Type* left_type, mpfr_t left_val,
+			      Type* right_type, mpfr_t right_val,
+			      mpfr_t val, source_location location)
 {
   switch (op)
     {
     case OPERATOR_OROR:
     case OPERATOR_ANDAND:
-      if (!type->is_boolean_type())
-	{
-	  error_at(location, _("expected boolean type"));
-	  return false;
-	}
-      break;
-
     case OPERATOR_EQEQ:
     case OPERATOR_NOTEQ:
-      if (type->integer_type() == NULL
-	  && type->float_type() == NULL
-	  && !type->is_string_type()
-	  && type->points_to() == NULL
-	  && !type->is_nil_type()
-	  && !type->is_boolean_type()
-	  && type->interface_type() == NULL
-	  && (type->array_type() == NULL
-	      || type->array_type()->length() != NULL)
-	  && type->map_type() == NULL
-	  && type->channel_type() == NULL
-	  && type->function_type() == NULL)
-	{
-	  error_at(location,
-		   _("expected integer, floating, string, pointer, boolean, "
-		     "interface, slice, map, channel, or function type"));
-	  return false;
-	}
-      break;
-
     case OPERATOR_LT:
     case OPERATOR_LE:
     case OPERATOR_GT:
     case OPERATOR_GE:
+      // These return boolean values.  We should probably handle them
+      // anyhow in case a type conversion is used on the result.
+      return false;
     case OPERATOR_PLUS:
-    case OPERATOR_PLUSEQ:
-      if (type->integer_type() == NULL
-	  && type->float_type() == NULL
-	  && !type->is_string_type())
-	{
-	  error_at(location,
-		   _("expected integer, floating, or string type"));
-	  return false;
-	}
+      mpfr_add(val, left_val, right_val, GMP_RNDN);
       break;
-
     case OPERATOR_MINUS:
-    case OPERATOR_MINUSEQ:
-    case OPERATOR_MULT:
-    case OPERATOR_MULTEQ:
-    case OPERATOR_DIV:
-    case OPERATOR_DIVEQ:
-      if (type->integer_type() == NULL
-	  && type->float_type() == NULL)
-	{
-	  error_at(location, _("expected integer or floating type"));
-	  return false;
-	}
+      mpfr_sub(val, left_val, right_val, GMP_RNDN);
       break;
-
-    case OPERATOR_MOD:
-    case OPERATOR_MODEQ:
     case OPERATOR_OR:
-    case OPERATOR_OREQ:
-    case OPERATOR_AND:
-    case OPERATOR_ANDEQ:
     case OPERATOR_XOR:
-    case OPERATOR_XOREQ:
+    case OPERATOR_AND:
     case OPERATOR_BITCLEAR:
-    case OPERATOR_BITCLEAREQ:
-      if (type->integer_type() == NULL)
-	{
-	  error_at(location, _("expected integer type"));
-	  return false;
-	}
+      return false;
+    case OPERATOR_MULT:
+      mpfr_mul(val, left_val, right_val, GMP_RNDN);
       break;
-
+    case OPERATOR_DIV:
+      if (mpfr_zero_p(right_val))
+	error_at(location, "division by zero");
+      mpfr_div(val, left_val, right_val, GMP_RNDN);
+      break;
+    case OPERATOR_MOD:
+      return false;
+    case OPERATOR_LSHIFT:
+    case OPERATOR_RSHIFT:
+      return false;
     default:
       gcc_unreachable();
     }
 
-  return true;
-}
-
-// Check types.
-
-void
-Binary_expression::do_check_types(Gogo*)
-{
-  if (this->op_ != OPERATOR_LSHIFT && this->op_ != OPERATOR_RSHIFT)
+  Type* type = left_type;
+  if (type == NULL)
+    type = right_type;
+  else if (type != right_type && right_type != NULL)
     {
-      Type* type = this->left_->type();
-
-      if (!Type::are_compatible_for_binop(type, this->right_->type()))
+      if (type->is_abstract())
+	type = right_type;
+      else if (!right_type->is_abstract())
 	{
-	  this->report_error(_("incompatible types in binary expression"));
-	  return;
+	  // This looks like a type error which should be diagnosed
+	  // elsewhere.  Don't do anything here, to avoid an unhelpful
+	  // chain of error messages.
+	  return true;
 	}
+    }
 
-      if (!type->is_error_type()
-	  && !this->right_->type()->is_error_type())
-	{
-	  if (!Binary_expression::check_operator_type(this->op_, type,
-						      this->location()))
-	    this->set_is_error();
-	}
-    }
-  else
+  if (type != NULL && !type->is_abstract())
     {
-      if (this->left_->type()->integer_type() == NULL
-	  && !this->left_->type()->is_error_type())
-	this->report_error(_("shift of non-integer operand"));
-
-      Type* shift_type = this->right_->type();
-      if (!shift_type->is_error_type())
-	{
-	  if (!shift_type->is_abstract()
-	      && (shift_type->integer_type() == NULL
-		  || !shift_type->integer_type()->is_unsigned()))
-	    this->report_error(_("shift count not unsigned integer"));
-	  else
-	    {
-	      mpz_t val;
-	      mpz_init(val);
-	      Type* type;
-	      if (this->right_->integer_constant_value(true, val, &type))
-		{
-		  if (mpz_sgn(val) < 0)
-		    this->report_error(_("negative shift count"));
-		}
-	      mpz_clear(val);
-	    }
-	}
+      if ((type != left_type
+	   && !Float_expression::check_constant(left_val, type, location))
+	  || (type != right_type
+	      && !Float_expression::check_constant(right_val, type,
+						   location))
+	  || !Float_expression::check_constant(val, type, location))
+	mpfr_set_ui(val, 0, GMP_RNDN);
     }
-}
-
-// Copying a binary expression never requires incrementing a reference
-// count, but we do have to disable incrementing the reference count.
 
-Expression*
-Binary_expression::do_being_copied(Refcounts*, bool)
-{
-  this->is_being_copied_ = true;
-  return this;
+  return true;
 }
 
-// A binary expression increments a reference count when adding
-// strings.
+// Lower a binary expression.  We have to evaluate constant
+// expressions now, in order t implemented Go's unlimited precision
+// constants.
 
 Expression*
-Binary_expression::do_note_decrements(Refcounts* refcounts)
-{
-  if (this->op_ != OPERATOR_PLUS
-      || !this->left_->type()->is_string_type()
-      || this->is_being_copied_)
-    return this;
-  return Expression::make_refcount_adjust(refcounts,
-					  REFCOUNT_DECREMENT_NEW,
-					  this, false);
-}
-
-// Get a tree for a binary expression.
-
-tree
-Binary_expression::do_get_tree(Translate_context* context)
+Binary_expression::do_lower(Gogo*, int)
 {
-  tree left = this->left_->get_tree(context);
-  tree right = this->right_->get_tree(context);
-
-  if (left == error_mark_node || right == error_mark_node)
-    return error_mark_node;
+  source_location location = this->location();
+  Operator op = this->op_;
+  Expression* left = this->left_;
+  Expression* right = this->right_;
 
-  enum tree_code code;
-  bool use_left_type = true;
-  bool is_shift_op = false;
-  switch (this->op_)
-    {
-    case OPERATOR_EQEQ:
-    case OPERATOR_NOTEQ:
-    case OPERATOR_LT:
-    case OPERATOR_LE:
-    case OPERATOR_GT:
-    case OPERATOR_GE:
-      return Expression::comparison_tree(context, this->op_,
-					 this->left_->type(), left,
-					 this->right_->type(), right,
-					 this->location());
+  const bool is_comparison = (op == OPERATOR_EQEQ
+			      || op == OPERATOR_NOTEQ
+			      || op == OPERATOR_LT
+			      || op == OPERATOR_LE
+			      || op == OPERATOR_GT
+			      || op == OPERATOR_GE);
 
-    case OPERATOR_OROR:
-      code = TRUTH_ORIF_EXPR;
-      use_left_type = false;
-      break;
-    case OPERATOR_ANDAND:
-      code = TRUTH_ANDIF_EXPR;
-      use_left_type = false;
-      break;
-    case OPERATOR_PLUS:
-      code = PLUS_EXPR;
-      break;
-    case OPERATOR_MINUS:
-      code = MINUS_EXPR;
-      break;
-    case OPERATOR_OR:
-      code = BIT_IOR_EXPR;
-      break;
-    case OPERATOR_XOR:
-      code = BIT_XOR_EXPR;
-      break;
-    case OPERATOR_MULT:
-      code = MULT_EXPR;
-      break;
-    case OPERATOR_DIV:
-      // FIXME: Code depends on whether integer or floating point.
-      code = TRUNC_DIV_EXPR;
-      break;
-    case OPERATOR_MOD:
-      // FIXME: Code depends on whether integer or floating point.
-      code = TRUNC_MOD_EXPR;
-      break;
-    case OPERATOR_LSHIFT:
-      code = LSHIFT_EXPR;
-      is_shift_op = true;
-      break;
-    case OPERATOR_RSHIFT:
-      code = RSHIFT_EXPR;
-      is_shift_op = true;
-      break;
-    case OPERATOR_AND:
-      code = BIT_AND_EXPR;
-      break;
-    case OPERATOR_BITCLEAR:
-      right = fold_build1(BIT_NOT_EXPR, TREE_TYPE(right), right);
-      code = BIT_AND_EXPR;
-      break;
-    default:
-      gcc_unreachable();
-    }
+  // Integer constant expressions.
+  {
+    mpz_t left_val;
+    mpz_init(left_val);
+    Type* left_type;
+    mpz_t right_val;
+    mpz_init(right_val);
+    Type* right_type;
+    if (left->integer_constant_value(false, left_val, &left_type)
+	&& right->integer_constant_value(false, right_val, &right_type))
+      {
+	Expression* ret = NULL;
+	if (left_type != right_type
+	    && left_type != NULL
+	    && right_type != NULL
+	    && left_type->base() != right_type->base()
+	    && op != OPERATOR_LSHIFT
+	    && op != OPERATOR_RSHIFT)
+	  {
+	    // May be a type error--let it be diagnosed later.
+	  }
+	else if (is_comparison)
+	  {
+	    bool b = Binary_expression::compare_integer(op, left_val,
+							right_val);
+	    ret = Expression::make_cast(Type::lookup_bool_type(),
+					Expression::make_boolean(b, location),
+					location);
+	  }
+	else
+	  {
+	    mpz_t val;
+	    mpz_init(val);
 
-  tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right);
+	    if (Binary_expression::eval_integer(op, left_type, left_val,
+						right_type, right_val,
+						location, val))
+	      {
+		gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND);
+		Type* type;
+		if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT)
+		  type = left_type;
+		else if (left_type == NULL)
+		  type = right_type;
+		else if (right_type == NULL)
+		  type = left_type;
+		else if (!left_type->is_abstract()
+			 && left_type->named_type() != NULL)
+		  type = left_type;
+		else if (!right_type->is_abstract()
+			 && right_type->named_type() != NULL)
+		  type = right_type;
+		else if (!left_type->is_abstract())
+		  type = left_type;
+		else if (!right_type->is_abstract())
+		  type = right_type;
+		else if (left_type->float_type() != NULL)
+		  type = left_type;
+		else if (right_type->float_type() != NULL)
+		  type = right_type;
+		else
+		  type = left_type;
+		ret = Expression::make_integer(&val, type, location);
+	      }
 
-  if (this->left_->type()->is_string_type())
-    {
-      gcc_assert(this->op_ == OPERATOR_PLUS);
-      tree string_type = Type::make_string_type()->get_tree(context->gogo());
-      static tree string_plus_decl;
-      return Gogo::call_builtin(&string_plus_decl,
-				this->location(),
-				"__go_string_plus",
-				2,
-				string_type,
-				string_type,
-				left,
-				string_type,
-				right);
-    }
+	    mpz_clear(val);
+	  }
 
-  tree compute_type = excess_precision_type(type);
-  if (compute_type != NULL_TREE)
-    {
-      left = convert_to_real(compute_type, left);
-      right = convert_to_real(compute_type, right);
-    }
+	if (ret != NULL)
+	  {
+	    mpz_clear(right_val);
+	    mpz_clear(left_val);
+	    return ret;
+	  }
+      }
+    mpz_clear(right_val);
+    mpz_clear(left_val);
+  }
 
-  tree eval_saved = NULL_TREE;
-  if (is_shift_op)
-    {
-      if (!DECL_P(left))
-	left = save_expr(left);
-      if (!DECL_P(right))
-	right = save_expr(right);
-      // Make sure the values are evaluated.
-      eval_saved = fold_build2_loc(this->location(), COMPOUND_EXPR,
-				   void_type_node, left, right);
+  // Floating point constant expressions.
+  {
+    mpfr_t left_val;
+    mpfr_init(left_val);
+    Type* left_type;
+    mpfr_t right_val;
+    mpfr_init(right_val);
+    Type* right_type;
+    if (left->float_constant_value(left_val, &left_type)
+	&& right->float_constant_value(right_val, &right_type))
+      {
+	Expression* ret = NULL;
+	if (left_type != right_type
+	    && left_type != NULL
+	    && right_type != NULL
+	    && left_type->base() != right_type->base()
+	    && op != OPERATOR_LSHIFT
+	    && op != OPERATOR_RSHIFT)
+	  {
+	    // May be a type error--let it be diagnosed later.
+	  }
+	else if (is_comparison)
+	  {
+	    bool b = Binary_expression::compare_float(op,
+						      (left_type != NULL
+						       ? left_type
+						       : right_type),
+						      left_val, right_val);
+	    ret = Expression::make_boolean(b, location);
+	  }
+	else
+	  {
+	    mpfr_t val;
+	    mpfr_init(val);
+
+	    if (Binary_expression::eval_float(op, left_type, left_val,
+					      right_type, right_val, val,
+					      location))
+	      {
+		gcc_assert(op != OPERATOR_OROR && op != OPERATOR_ANDAND
+			   && op != OPERATOR_LSHIFT && op != OPERATOR_RSHIFT);
+		Type* type;
+		if (left_type == NULL)
+		  type = right_type;
+		else if (right_type == NULL)
+		  type = left_type;
+		else if (!left_type->is_abstract()
+		    && left_type->named_type() != NULL)
+		  type = left_type;
+		else if (!right_type->is_abstract()
+			 && right_type->named_type() != NULL)
+		  type = right_type;
+		else if (!left_type->is_abstract())
+		  type = left_type;
+		else if (!right_type->is_abstract())
+		  type = right_type;
+		else if (left_type->float_type() != NULL)
+		  type = left_type;
+		else if (right_type->float_type() != NULL)
+		  type = right_type;
+		else
+		  type = left_type;
+		ret = Expression::make_float(&val, type, location);
+	      }
+
+	    mpfr_clear(val);
+	  }
+
+	if (ret != NULL)
+	  {
+	    mpfr_clear(right_val);
+	    mpfr_clear(left_val);
+	    return ret;
+	  }
+      }
+    mpfr_clear(right_val);
+    mpfr_clear(left_val);
+  }
+
+  // String constant expressions.
+  if (op == OPERATOR_PLUS
+      && left->type()->is_string_type()
+      && right->type()->is_string_type())
+    {
+      std::string left_string;
+      std::string right_string;
+      if (left->string_constant_value(&left_string)
+	  && right->string_constant_value(&right_string))
+	return Expression::make_string(left_string + right_string, location);
     }
 
-  tree ret = fold_build2_loc(this->location(),
-			     code,
-			     compute_type != NULL_TREE ? compute_type : type,
-			     left, right);
+  return this;
+}
 
-  if (compute_type != NULL_TREE)
-    ret = convert_to_real(type, ret);
+// Return the integer constant value, if it has one.
 
-  // In Go, a shift larger than the size of the type is well-defined.
-  // This is not true in GENERIC, so we need to insert a conditional.
-  if (is_shift_op)
+bool
+Binary_expression::do_integer_constant_value(bool iota_is_constant, mpz_t val,
+					     Type** ptype) const
+{
+  mpz_t left_val;
+  mpz_init(left_val);
+  Type* left_type;
+  if (!this->left_->integer_constant_value(iota_is_constant, left_val,
+					   &left_type))
     {
-      gcc_assert(INTEGRAL_TYPE_P(TREE_TYPE(left)));
-      gcc_assert(this->left_->type()->integer_type() != NULL);
-      int bits = TYPE_PRECISION(TREE_TYPE(left));
+      mpz_clear(left_val);
+      return false;
+    }
 
-      tree compare = fold_build2(LT_EXPR, boolean_type_node, right,
-				 build_int_cst_type(TREE_TYPE(right), bits));
+  mpz_t right_val;
+  mpz_init(right_val);
+  Type* right_type;
+  if (!this->right_->integer_constant_value(iota_is_constant, right_val,
+					    &right_type))
+    {
+      mpz_clear(right_val);
+      mpz_clear(left_val);
+      return false;
+    }
 
-      tree overflow_result = fold_convert_loc(this->location(),
-					      TREE_TYPE(left),
-					      integer_zero_node);
-      if (this->op_ == OPERATOR_RSHIFT
-	  && !this->left_->type()->integer_type()->is_unsigned())
-	{
-	  tree neg = fold_build2_loc(this->location(), LT_EXPR,
-				     boolean_type_node, left,
-				     fold_convert_loc(this->location(),
-						      TREE_TYPE(left),
-						      integer_zero_node));
-	  tree neg_one = fold_build2_loc(this->location(),
-					 MINUS_EXPR, TREE_TYPE(left),
-					 fold_convert_loc(this->location(),
-							  TREE_TYPE(left),
-							  integer_zero_node),
-					 fold_convert_loc(this->location(),
-							  TREE_TYPE(left),
-							  integer_one_node));
-	  overflow_result = fold_build3_loc(this->location(), COND_EXPR,
-					    TREE_TYPE(left), neg, neg_one,
-					    overflow_result);
-	}
+  bool ret;
+  if (left_type != right_type
+      && left_type != NULL
+      && right_type != NULL
+      && left_type->base() != right_type->base()
+      && this->op_ != OPERATOR_RSHIFT
+      && this->op_ != OPERATOR_LSHIFT)
+    ret = false;
+  else
+    ret = Binary_expression::eval_integer(this->op_, left_type, left_val,
+					  right_type, right_val,
+					  this->location(), val);
 
-      ret = fold_build3_loc(this->location(), COND_EXPR, TREE_TYPE(left),
-			    compare, ret, overflow_result);
+  mpz_clear(right_val);
+  mpz_clear(left_val);
 
-      ret = fold_build2_loc(this->location(), COMPOUND_EXPR,
-			    TREE_TYPE(ret), eval_saved, ret);
+  if (ret)
+    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
+	      ? right_type
+	      : left_type);
+
+  return ret;
+}
+
+// Return the floating point constant value, if it has one.
+
+bool
+Binary_expression::do_float_constant_value(mpfr_t val, Type** ptype) const
+{
+  mpfr_t left_val;
+  mpfr_init(left_val);
+  Type* left_type;
+  if (!this->left_->float_constant_value(left_val, &left_type))
+    {
+      mpfr_clear(left_val);
+      return false;
+    }
+
+  mpfr_t right_val;
+  mpfr_init(right_val);
+  Type* right_type;
+  if (!this->right_->float_constant_value(right_val, &right_type))
+    {
+      mpfr_clear(right_val);
+      mpfr_clear(left_val);
+      return false;
     }
 
+  bool ret;
+  if (left_type != right_type
+      && left_type != NULL
+      && right_type != NULL
+      && left_type->base() != right_type->base())
+    ret = false;
+  else
+    ret = Binary_expression::eval_float(this->op_, left_type, left_val,
+					right_type, right_val,
+					val, this->location());
+
+  mpfr_clear(left_val);
+  mpfr_clear(right_val);
+
+  if (ret)
+    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
+	      ? right_type
+	      : left_type);
+
   return ret;
 }
 
-// Export a binary expression.
+// Note that the value is being discarded.
 
 void
-Binary_expression::do_export(Export* exp) const
+Binary_expression::do_discarding_value()
+{
+  if (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_ANDAND)
+    this->right_->discarding_value();
+  else
+    this->warn_unused_value();
+}
+
+// Get type.
+
+Type*
+Binary_expression::do_type()
 {
-  exp->write_c_string("(");
-  this->left_->export_expression(exp);
   switch (this->op_)
     {
     case OPERATOR_OROR:
-      exp->write_c_string(" || ");
-      break;
     case OPERATOR_ANDAND:
-      exp->write_c_string(" && ");
-      break;
     case OPERATOR_EQEQ:
-      exp->write_c_string(" == ");
-      break;
     case OPERATOR_NOTEQ:
-      exp->write_c_string(" != ");
-      break;
     case OPERATOR_LT:
-      exp->write_c_string(" < ");
-      break;
     case OPERATOR_LE:
-      exp->write_c_string(" <= ");
-      break;
     case OPERATOR_GT:
-      exp->write_c_string(" > ");
-      break;
     case OPERATOR_GE:
-      exp->write_c_string(" >= ");
-      break;
+      return Type::lookup_bool_type();
+
     case OPERATOR_PLUS:
-      exp->write_c_string(" + ");
-      break;
     case OPERATOR_MINUS:
-      exp->write_c_string(" - ");
-      break;
     case OPERATOR_OR:
-      exp->write_c_string(" | ");
-      break;
     case OPERATOR_XOR:
-      exp->write_c_string(" ^ ");
-      break;
     case OPERATOR_MULT:
-      exp->write_c_string(" * ");
-      break;
     case OPERATOR_DIV:
-      exp->write_c_string(" / ");
-      break;
     case OPERATOR_MOD:
-      exp->write_c_string(" % ");
-      break;
-    case OPERATOR_LSHIFT:
-      exp->write_c_string(" << ");
-      break;
-    case OPERATOR_RSHIFT:
-      exp->write_c_string(" >> ");
-      break;
     case OPERATOR_AND:
-      exp->write_c_string(" & ");
-      break;
     case OPERATOR_BITCLEAR:
-      exp->write_c_string(" &^ ");
-      break;
+      {
+	Type* left_type = this->left_->type();
+	Type* right_type = this->right_->type();
+	if (!left_type->is_abstract() && left_type->named_type() != NULL)
+	  return left_type;
+	else if (!right_type->is_abstract() && right_type->named_type() != NULL)
+	  return right_type;
+	else if (!left_type->is_abstract())
+	  return left_type;
+	else if (!right_type->is_abstract())
+	  return right_type;
+	else if (left_type->float_type() != NULL)
+	  return left_type;
+	else if (right_type->float_type() != NULL)
+	  return right_type;
+	else
+	  return left_type;
+      }
+
+    case OPERATOR_LSHIFT:
+    case OPERATOR_RSHIFT:
+      return this->left_->type();
+
     default:
       gcc_unreachable();
     }
-  this->right_->export_expression(exp);
-  exp->write_c_string(")");
 }
 
-// Import a binary expression.
+// Set type for a binary expression.
 
-Expression*
-Binary_expression::do_import(Import* imp)
+void
+Binary_expression::do_determine_type(const Type_context* context)
 {
-  imp->require_c_string("(");
+  Type* tleft = this->left_->type();
+  Type* tright = this->right_->type();
 
-  Expression* left = Expression::import_expression(imp);
+  // Both sides should have the same type, except for the shift
+  // operations.  For a comparison, we should ignore the incoming
+  // type.
 
-  Operator op;
-  if (imp->match_c_string(" || "))
-    {
-      op = OPERATOR_OROR;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" && "))
-    {
-      op = OPERATOR_ANDAND;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" == "))
-    {
-      op = OPERATOR_EQEQ;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" != "))
-    {
-      op = OPERATOR_NOTEQ;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" < "))
-    {
-      op = OPERATOR_LT;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" <= "))
-    {
-      op = OPERATOR_LE;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" > "))
-    {
-      op = OPERATOR_GT;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" >= "))
-    {
-      op = OPERATOR_GE;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" + "))
-    {
-      op = OPERATOR_PLUS;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" - "))
-    {
-      op = OPERATOR_MINUS;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" | "))
-    {
-      op = OPERATOR_OR;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" ^ "))
-    {
-      op = OPERATOR_XOR;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" * "))
-    {
-      op = OPERATOR_MULT;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" / "))
-    {
-      op = OPERATOR_DIV;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" % "))
-    {
-      op = OPERATOR_MOD;
-      imp->advance(3);
-    }
-  else if (imp->match_c_string(" << "))
-    {
-      op = OPERATOR_LSHIFT;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" >> "))
-    {
-      op = OPERATOR_RSHIFT;
-      imp->advance(4);
-    }
-  else if (imp->match_c_string(" & "))
+  bool is_shift_op = (this->op_ == OPERATOR_LSHIFT
+		      || this->op_ == OPERATOR_RSHIFT);
+
+  bool is_comparison = (this->op_ == OPERATOR_EQEQ
+			|| this->op_ == OPERATOR_NOTEQ
+			|| this->op_ == OPERATOR_LT
+			|| this->op_ == OPERATOR_LE
+			|| this->op_ == OPERATOR_GT
+			|| this->op_ == OPERATOR_GE);
+
+  Type_context subcontext(*context);
+
+  if (is_comparison)
     {
-      op = OPERATOR_AND;
-      imp->advance(3);
+      // In a comparison, the context does not determine the types of
+      // the operands.
+      subcontext.type = NULL;
     }
-  else if (imp->match_c_string(" &^ "))
+
+  // Set the context for the left hand operand.
+  if (is_shift_op)
     {
-      op = OPERATOR_BITCLEAR;
-      imp->advance(4);
+      // The right hand operand plays no role in determining the type
+      // of the left hand operand.  A shift of an abstract integer in
+      // a string context gets special treatment, which may be a
+      // language bug.
+      if (subcontext.type != NULL
+	  && subcontext.type->is_string_type()
+	  && tleft->is_abstract())
+	error_at(this->location(), "shift of non-integer operand");
     }
-  else
+  else if (!tleft->is_abstract())
+    subcontext.type = tleft;
+  else if (!tright->is_abstract())
+    subcontext.type = tright;
+  else if (subcontext.type == NULL)
     {
-      error_at(imp->location(), "unrecognized binary operator");
-      return Expression::make_error(imp->location());
+      if ((tleft->integer_type() != NULL && tright->integer_type() != NULL)
+	  || (tleft->float_type() != NULL && tright->float_type() != NULL))
+	{
+	  // Both sides have an abstract integer type or both sides
+	  // have an abstract float type.  Just let CONTEXT determine
+	  // whether they may remain abstract or not.
+	}
+      else
+	{
+	  // Both sides are abstract, but one is integer and one is
+	  // floating point.  Convert the abstract integer to floating
+	  // point.
+	  subcontext.type = tleft->float_type() != NULL ? tleft : tright;
+	}
     }
 
-  Expression* right = Expression::import_expression(imp);
-
-  imp->require_c_string(")");
-
-  return Expression::make_binary(op, left, right, imp->location());
-}
+  this->left_->determine_type(&subcontext);
 
-// Make a binary expression.
+  // The context for the right hand operand is the same as for the
+  // left hand operand, except for a shift operator.
+  if (is_shift_op)
+    {
+      subcontext.type = Type::lookup_integer_type("uint");
+      subcontext.may_be_abstract = false;
+    }
 
-Expression*
-Expression::make_binary(Operator op, Expression* left, Expression* right,
-			source_location location)
-{
-  return new Binary_expression(op, left, right, location);
+  this->right_->determine_type(&subcontext);
 }
 
-// Implement a comparison.
+// Report an error if the binary operator OP does not support TYPE.
+// Return whether the operation is OK.  This should not be used for
+// shift.
 
-tree
-Expression::comparison_tree(Translate_context* context, Operator op,
-			    Type* left_type, tree left_tree,
-			    Type* right_type, tree right_tree,
-			    source_location location)
+bool
+Binary_expression::check_operator_type(Operator op, Type* type,
+				       source_location location)
 {
-  enum tree_code code;
   switch (op)
     {
-    case OPERATOR_EQEQ:
-      code = EQ_EXPR;
+    case OPERATOR_OROR:
+    case OPERATOR_ANDAND:
+      if (!type->is_boolean_type())
+	{
+	  error_at(location, _("expected boolean type"));
+	  return false;
+	}
       break;
+
+    case OPERATOR_EQEQ:
     case OPERATOR_NOTEQ:
-      code = NE_EXPR;
-      break;
-    case OPERATOR_LT:
-      code = LT_EXPR;
-      break;
-    case OPERATOR_LE:
-      code = LE_EXPR;
-      break;
-    case OPERATOR_GT:
-      code = GT_EXPR;
+      if (type->integer_type() == NULL
+	  && type->float_type() == NULL
+	  && !type->is_string_type()
+	  && type->points_to() == NULL
+	  && !type->is_nil_type()
+	  && !type->is_boolean_type()
+	  && type->interface_type() == NULL
+	  && (type->array_type() == NULL
+	      || type->array_type()->length() != NULL)
+	  && type->map_type() == NULL
+	  && type->channel_type() == NULL
+	  && type->function_type() == NULL)
+	{
+	  error_at(location,
+		   _("expected integer, floating, string, pointer, boolean, "
+		     "interface, slice, map, channel, or function type"));
+	  return false;
+	}
       break;
+
+    case OPERATOR_LT:
+    case OPERATOR_LE:
+    case OPERATOR_GT:
     case OPERATOR_GE:
-      code = GE_EXPR;
+    case OPERATOR_PLUS:
+    case OPERATOR_PLUSEQ:
+      if (type->integer_type() == NULL
+	  && type->float_type() == NULL
+	  && !type->is_string_type())
+	{
+	  error_at(location,
+		   _("expected integer, floating, or string type"));
+	  return false;
+	}
       break;
-    default:
-      gcc_unreachable();
-    }
-
-  if (left_type->is_string_type())
-    {
-      gcc_assert(right_type->is_string_type());
-      tree string_type = Type::make_string_type()->get_tree(context->gogo());
-      static tree string_compare_decl;
-      left_tree = Gogo::call_builtin(&string_compare_decl,
-				     location,
-				     "__go_strcmp",
-				     2,
-				     integer_type_node,
-				     string_type,
-				     left_tree,
-				     string_type,
-				     right_tree);
-      right_tree = build_int_cst_type(integer_type_node, 0);
-    }
-
-  if (left_type->interface_type() != NULL
-      && right_type->interface_type() != NULL)
-    {
-      static tree interface_compare_decl;
-      left_tree = Gogo::call_builtin(&interface_compare_decl,
-				     location,
-				     "__go_interface_compare",
-				     2,
-				     integer_type_node,
-				     const_ptr_type_node,
-				     fold_convert(const_ptr_type_node,
-						  left_tree),
-				     const_ptr_type_node,
-				     fold_convert(const_ptr_type_node,
-						  right_tree));
-      right_tree = build_int_cst_type(integer_type_node, 0);
-    }
-
-  if (left_type->is_nil_type()
-      && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ))
-    {
-      std::swap(left_type, right_type);
-      std::swap(left_tree, right_tree);
-    }
 
-  if (right_type->is_nil_type())
-    {
-      if (left_type->array_type() != NULL
-	  && left_type->array_type()->length() == NULL)
+    case OPERATOR_MINUS:
+    case OPERATOR_MINUSEQ:
+    case OPERATOR_MULT:
+    case OPERATOR_MULTEQ:
+    case OPERATOR_DIV:
+    case OPERATOR_DIVEQ:
+      if (type->integer_type() == NULL
+	  && type->float_type() == NULL)
 	{
-	  Array_type* at = left_type->array_type();
-	  left_tree = at->value_pointer_tree(context->gogo(), left_tree);
-	  right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+	  error_at(location, _("expected integer or floating type"));
+	  return false;
 	}
-      else
+      break;
+
+    case OPERATOR_MOD:
+    case OPERATOR_MODEQ:
+    case OPERATOR_OR:
+    case OPERATOR_OREQ:
+    case OPERATOR_AND:
+    case OPERATOR_ANDEQ:
+    case OPERATOR_XOR:
+    case OPERATOR_XOREQ:
+    case OPERATOR_BITCLEAR:
+    case OPERATOR_BITCLEAREQ:
+      if (type->integer_type() == NULL)
 	{
-	  gcc_assert(POINTER_TYPE_P(TREE_TYPE(left_tree)));
-	  right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+	  error_at(location, _("expected integer type"));
+	  return false;
 	}
-    }
-
-  tree ret = fold_build2(code, boolean_type_node, left_tree, right_tree);
-  if (CAN_HAVE_LOCATION_P(ret))
-    SET_EXPR_LOCATION(ret, location);
-  return ret;
-}
-
-// Class Bound_method_expression.
-
-// Traversal.
-
-int
-Bound_method_expression::do_traverse(Traverse* traverse)
-{
-  if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  return Expression::traverse(&this->method_, traverse);
-}
+      break;
 
-// Return the type of a bound method expression.  The type of this
-// object is really the type of the method with no receiver.  We
-// should be able to get away with just returning the type of the
-// method.
+    default:
+      gcc_unreachable();
+    }
 
-Type*
-Bound_method_expression::do_type()
-{
-  return this->method_->type();
+  return true;
 }
 
-// Determine the types of a method expression.
+// Check types.
 
 void
-Bound_method_expression::do_determine_type(const Type_context*)
+Binary_expression::do_check_types(Gogo*)
 {
-  this->method_->determine_type_no_context();
-  Type* mtype = this->method_->type();
-  Function_type* fntype = mtype == NULL ? NULL : mtype->function_type();
-  if (fntype == NULL || !fntype->is_method())
-    this->expr_->determine_type_no_context();
-  else
+  if (this->op_ != OPERATOR_LSHIFT && this->op_ != OPERATOR_RSHIFT)
     {
-      Type_context subcontext(fntype->receiver()->type(), false);
-      this->expr_->determine_type(&subcontext);
-    }
-}
+      Type* type = this->left_->type();
 
-// Check the types of a method expression.
+      if (!Type::are_compatible_for_binop(type, this->right_->type()))
+	{
+	  this->report_error(_("incompatible types in binary expression"));
+	  return;
+	}
 
-void
-Bound_method_expression::do_check_types(Gogo*)
-{
-  Type* type = this->method_->type()->deref();
-  if (type == NULL
-      || type->function_type() == NULL
-      || !type->function_type()->is_method())
-    this->report_error(_("object is not a method"));
+      if (!type->is_error_type()
+	  && !this->right_->type()->is_error_type())
+	{
+	  if (!Binary_expression::check_operator_type(this->op_, type,
+						      this->location()))
+	    this->set_is_error();
+	}
+    }
   else
     {
-      Type* rtype = type->function_type()->receiver()->type()->deref();
-      Type* etype = (this->expr_type_ != NULL
-		     ? this->expr_type_
-		     : this->expr_->type());
-      etype = etype->deref();
-      if (!Type::are_identical(rtype, etype))
-	this->report_error(_("method type does not match object type"));
+      if (this->left_->type()->integer_type() == NULL
+	  && !this->left_->type()->is_error_type())
+	this->report_error(_("shift of non-integer operand"));
+
+      Type* shift_type = this->right_->type();
+      if (!shift_type->is_error_type())
+	{
+	  if (!shift_type->is_abstract()
+	      && (shift_type->integer_type() == NULL
+		  || !shift_type->integer_type()->is_unsigned()))
+	    this->report_error(_("shift count not unsigned integer"));
+	  else
+	    {
+	      mpz_t val;
+	      mpz_init(val);
+	      Type* type;
+	      if (this->right_->integer_constant_value(true, val, &type))
+		{
+		  if (mpz_sgn(val) < 0)
+		    this->report_error(_("negative shift count"));
+		}
+	      mpz_clear(val);
+	    }
+	}
     }
 }
 
-// Get the tree for a method expression.  There is no standard tree
-// representation for this.  The only places it may currently be used
-// are in a Call_expression or a Go_statement, which will take it
-// apart directly.  So this has nothing to do at present.
+// Copying a binary expression never requires incrementing a reference
+// count, but we do have to disable incrementing the reference count.
 
-tree
-Bound_method_expression::do_get_tree(Translate_context*)
+Expression*
+Binary_expression::do_being_copied(Refcounts*, bool)
 {
-  gcc_unreachable();
+  this->is_being_copied_ = true;
+  return this;
 }
 
-// Make a method expression.
+// A binary expression increments a reference count when adding
+// strings.
 
-Bound_method_expression*
-Expression::make_bound_method(Expression* expr, Expression* method,
-			      source_location location)
+Expression*
+Binary_expression::do_note_decrements(Refcounts* refcounts)
 {
-  return new Bound_method_expression(expr, method, location);
+  if (this->op_ != OPERATOR_PLUS
+      || !this->left_->type()->is_string_type()
+      || this->is_being_copied_)
+    return this;
+  return Expression::make_refcount_adjust(refcounts,
+					  REFCOUNT_DECREMENT_NEW,
+					  this, false);
 }
 
-// Class Builtin_call_expression.  This is used for a call to a
-// builtin function.
+// Get a tree for a binary expression.
 
-class Builtin_call_expression : public Call_expression
+tree
+Binary_expression::do_get_tree(Translate_context* context)
 {
- public:
-  Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
-			  source_location location);
+  tree left = this->left_->get_tree(context);
+  tree right = this->right_->get_tree(context);
 
- protected:
-  // This overrides Call_expression::do_lower.
-  Expression*
-  do_lower(Gogo*, int);
+  if (left == error_mark_node || right == error_mark_node)
+    return error_mark_node;
 
-  bool
-  do_is_constant() const;
+  enum tree_code code;
+  bool use_left_type = true;
+  bool is_shift_op = false;
+  switch (this->op_)
+    {
+    case OPERATOR_EQEQ:
+    case OPERATOR_NOTEQ:
+    case OPERATOR_LT:
+    case OPERATOR_LE:
+    case OPERATOR_GT:
+    case OPERATOR_GE:
+      return Expression::comparison_tree(context, this->op_,
+					 this->left_->type(), left,
+					 this->right_->type(), right,
+					 this->location());
 
-  bool
-  do_integer_constant_value(bool, mpz_t, Type**) const;
+    case OPERATOR_OROR:
+      code = TRUTH_ORIF_EXPR;
+      use_left_type = false;
+      break;
+    case OPERATOR_ANDAND:
+      code = TRUTH_ANDIF_EXPR;
+      use_left_type = false;
+      break;
+    case OPERATOR_PLUS:
+      code = PLUS_EXPR;
+      break;
+    case OPERATOR_MINUS:
+      code = MINUS_EXPR;
+      break;
+    case OPERATOR_OR:
+      code = BIT_IOR_EXPR;
+      break;
+    case OPERATOR_XOR:
+      code = BIT_XOR_EXPR;
+      break;
+    case OPERATOR_MULT:
+      code = MULT_EXPR;
+      break;
+    case OPERATOR_DIV:
+      // FIXME: Code depends on whether integer or floating point.
+      code = TRUNC_DIV_EXPR;
+      break;
+    case OPERATOR_MOD:
+      // FIXME: Code depends on whether integer or floating point.
+      code = TRUNC_MOD_EXPR;
+      break;
+    case OPERATOR_LSHIFT:
+      code = LSHIFT_EXPR;
+      is_shift_op = true;
+      break;
+    case OPERATOR_RSHIFT:
+      code = RSHIFT_EXPR;
+      is_shift_op = true;
+      break;
+    case OPERATOR_AND:
+      code = BIT_AND_EXPR;
+      break;
+    case OPERATOR_BITCLEAR:
+      right = fold_build1(BIT_NOT_EXPR, TREE_TYPE(right), right);
+      code = BIT_AND_EXPR;
+      break;
+    default:
+      gcc_unreachable();
+    }
 
-  Type*
-  do_type();
+  tree type = use_left_type ? TREE_TYPE(left) : TREE_TYPE(right);
 
-  void
-  do_determine_type(const Type_context*);
+  if (this->left_->type()->is_string_type())
+    {
+      gcc_assert(this->op_ == OPERATOR_PLUS);
+      tree string_type = Type::make_string_type()->get_tree(context->gogo());
+      static tree string_plus_decl;
+      return Gogo::call_builtin(&string_plus_decl,
+				this->location(),
+				"__go_string_plus",
+				2,
+				string_type,
+				string_type,
+				left,
+				string_type,
+				right);
+    }
 
-  void
-  do_check_types(Gogo*);
+  tree compute_type = excess_precision_type(type);
+  if (compute_type != NULL_TREE)
+    {
+      left = convert_to_real(compute_type, left);
+      right = convert_to_real(compute_type, right);
+    }
 
-  Expression*
-  do_copy()
-  {
-    return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
-				       this->args()->copy(),
-				       this->location());
-  }
+  tree eval_saved = NULL_TREE;
+  if (is_shift_op)
+    {
+      if (!DECL_P(left))
+	left = save_expr(left);
+      if (!DECL_P(right))
+	right = save_expr(right);
+      // Make sure the values are evaluated.
+      eval_saved = fold_build2_loc(this->location(), COMPOUND_EXPR,
+				   void_type_node, left, right);
+    }
 
-  tree
-  do_get_tree(Translate_context*);
+  tree ret = fold_build2_loc(this->location(),
+			     code,
+			     compute_type != NULL_TREE ? compute_type : type,
+			     left, right);
 
-  void
-  do_export(Export*) const;
+  if (compute_type != NULL_TREE)
+    ret = convert_to_real(type, ret);
 
- private:
-  // The builtin functions.
-  enum Builtin_function_code
+  // In Go, a shift larger than the size of the type is well-defined.
+  // This is not true in GENERIC, so we need to insert a conditional.
+  if (is_shift_op)
     {
-      BUILTIN_INVALID,
-
-      // Predeclared builtin functions.
-      BUILTIN_CAP,
-      BUILTIN_CLOSE,
-      BUILTIN_CLOSED,
-      BUILTIN_LEN,
-      BUILTIN_MAKE,
-      BUILTIN_NEW,
-      BUILTIN_PANIC,
-      BUILTIN_PANICLN,
-      BUILTIN_PRINT,
-      BUILTIN_PRINTLN,
+      gcc_assert(INTEGRAL_TYPE_P(TREE_TYPE(left)));
+      gcc_assert(this->left_->type()->integer_type() != NULL);
+      int bits = TYPE_PRECISION(TREE_TYPE(left));
 
-      // Builtin functions from the unsafe package.
-      BUILTIN_ALIGNOF,
-      BUILTIN_OFFSETOF,
-      BUILTIN_SIZEOF
-    };
+      tree compare = fold_build2(LT_EXPR, boolean_type_node, right,
+				 build_int_cst_type(TREE_TYPE(right), bits));
 
-  Expression*
-  one_arg() const;
+      tree overflow_result = fold_convert_loc(this->location(),
+					      TREE_TYPE(left),
+					      integer_zero_node);
+      if (this->op_ == OPERATOR_RSHIFT
+	  && !this->left_->type()->integer_type()->is_unsigned())
+	{
+	  tree neg = fold_build2_loc(this->location(), LT_EXPR,
+				     boolean_type_node, left,
+				     fold_convert_loc(this->location(),
+						      TREE_TYPE(left),
+						      integer_zero_node));
+	  tree neg_one = fold_build2_loc(this->location(),
+					 MINUS_EXPR, TREE_TYPE(left),
+					 fold_convert_loc(this->location(),
+							  TREE_TYPE(left),
+							  integer_zero_node),
+					 fold_convert_loc(this->location(),
+							  TREE_TYPE(left),
+							  integer_one_node));
+	  overflow_result = fold_build3_loc(this->location(), COND_EXPR,
+					    TREE_TYPE(left), neg, neg_one,
+					    overflow_result);
+	}
 
-  bool
-  check_one_arg();
+      ret = fold_build3_loc(this->location(), COND_EXPR, TREE_TYPE(left),
+			    compare, ret, overflow_result);
 
-  // A pointer back to the general IR structure.  This avoids a global
-  // variable, or passing it around everywhere.
-  Gogo* gogo_;
-  // The builtin function being called.
-  Builtin_function_code code_;
-};
+      ret = fold_build2_loc(this->location(), COMPOUND_EXPR,
+			    TREE_TYPE(ret), eval_saved, ret);
+    }
 
-Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
-						 Expression* fn,
-						 Expression_list* args,
-						 source_location location)
-  : Call_expression(fn, args, location),
-    gogo_(gogo), code_(BUILTIN_INVALID)
-{
-  Func_expression* fnexp = this->fn()->func_expression();
-  gcc_assert(fnexp != NULL);
-  const std::string& name(fnexp->named_object()->name());
-  if (name == "cap")
-    this->code_ = BUILTIN_CAP;
-  else if (name == "close")
-    this->code_ = BUILTIN_CLOSE;
-  else if (name == "closed")
-    this->code_ = BUILTIN_CLOSED;
-  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 == "panicln")
-    this->code_ = BUILTIN_PANICLN;
-  else if (name == "print")
-    this->code_ = BUILTIN_PRINT;
-  else if (name == "println")
-    this->code_ = BUILTIN_PRINTLN;
-  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
-    gcc_unreachable();
+  return ret;
 }
 
-// Lower a builtin call expression.  This turns new and make into
-// specific expressions.
+// Export a binary expression.
 
-Expression*
-Builtin_call_expression::do_lower(Gogo*, int)
+void
+Binary_expression::do_export(Export* exp) const
 {
-  if (this->code_ == BUILTIN_NEW)
+  exp->write_c_string("(");
+  this->left_->export_expression(exp);
+  switch (this->op_)
     {
-      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())
-	    {
-	      error_at(arg->location(), _("expected type"));
-	      this->set_is_error();
-	    }
-	  else
-	    return Expression::make_allocation(arg->type(), this->location());
-	}
-    }
-  else if (this->code_ == BUILTIN_MAKE)
-    {
-      const Expression_list* args = this->args();
-      if (args == NULL || args->size() < 1)
-	this->report_error(_("not enough arguments"));
-      else
-	{
-	  Expression* arg = args->front();
-	  if (!arg->is_type_expression())
-	    {
-	      error_at(arg->location(), _("expected type"));
-	      this->set_is_error();
-	    }
-	  else
-	    {
-	      Expression_list* newargs;
-	      if (args->size() == 1)
-		newargs = NULL;
-	      else
-		{
-		  newargs = new Expression_list();
-		  Expression_list::const_iterator p = args->begin();
-		  ++p;
-		  for (; p != args->end(); ++p)
-		    newargs->push_back(*p);
-		}
-	      return Expression::make_make(arg->type(), newargs,
-					   this->location());
-	    }
-	}
+    case OPERATOR_OROR:
+      exp->write_c_string(" || ");
+      break;
+    case OPERATOR_ANDAND:
+      exp->write_c_string(" && ");
+      break;
+    case OPERATOR_EQEQ:
+      exp->write_c_string(" == ");
+      break;
+    case OPERATOR_NOTEQ:
+      exp->write_c_string(" != ");
+      break;
+    case OPERATOR_LT:
+      exp->write_c_string(" < ");
+      break;
+    case OPERATOR_LE:
+      exp->write_c_string(" <= ");
+      break;
+    case OPERATOR_GT:
+      exp->write_c_string(" > ");
+      break;
+    case OPERATOR_GE:
+      exp->write_c_string(" >= ");
+      break;
+    case OPERATOR_PLUS:
+      exp->write_c_string(" + ");
+      break;
+    case OPERATOR_MINUS:
+      exp->write_c_string(" - ");
+      break;
+    case OPERATOR_OR:
+      exp->write_c_string(" | ");
+      break;
+    case OPERATOR_XOR:
+      exp->write_c_string(" ^ ");
+      break;
+    case OPERATOR_MULT:
+      exp->write_c_string(" * ");
+      break;
+    case OPERATOR_DIV:
+      exp->write_c_string(" / ");
+      break;
+    case OPERATOR_MOD:
+      exp->write_c_string(" % ");
+      break;
+    case OPERATOR_LSHIFT:
+      exp->write_c_string(" << ");
+      break;
+    case OPERATOR_RSHIFT:
+      exp->write_c_string(" >> ");
+      break;
+    case OPERATOR_AND:
+      exp->write_c_string(" & ");
+      break;
+    case OPERATOR_BITCLEAR:
+      exp->write_c_string(" &^ ");
+      break;
+    default:
+      gcc_unreachable();
     }
-
-  return this;
+  this->right_->export_expression(exp);
+  exp->write_c_string(")");
 }
 
-// Return a single argument, or NULL if there isn't one.
+// Import a binary expression.
 
 Expression*
-Builtin_call_expression::one_arg() const
+Binary_expression::do_import(Import* imp)
 {
-  const Expression_list* args = this->args();
-  if (args->size() != 1)
-    return NULL;
-  return args->front();
-}
+  imp->require_c_string("(");
 
-// Return whether this is constant: len of a string, or len or cap of
-// a fixed array, or unsafe.Sizeof, unsafe.Offsetof, unsafe.Alignof.
+  Expression* left = Expression::import_expression(imp);
 
-bool
-Builtin_call_expression::do_is_constant() const
-{
-  if (this->code_ == BUILTIN_LEN
-      || this->code_ == BUILTIN_CAP)
+  Operator op;
+  if (imp->match_c_string(" || "))
     {
-      Expression* arg = this->one_arg();
-      if (arg == NULL)
-	return false;
-      Type* arg_type = arg->type();
-
-      if (arg_type->points_to() != NULL
-	  && arg_type->points_to()->array_type() != NULL
-	  && !arg_type->points_to()->is_open_array_type())
-	arg_type = arg_type->points_to();
-
-      if (arg_type->array_type() != NULL
-	  && arg_type->array_type()->length() != NULL)
-	return arg_type->array_type()->length()->is_constant();
-
-      if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
-	return arg->is_constant();
+      op = OPERATOR_OROR;
+      imp->advance(4);
     }
-  else if (this->code_ == BUILTIN_SIZEOF
-	   || this->code_ == BUILTIN_ALIGNOF)
-    return this->one_arg() != NULL;
-  else if (this->code_ == BUILTIN_OFFSETOF)
+  else if (imp->match_c_string(" && "))
     {
-      Expression* arg = this->one_arg();
-      if (arg == NULL)
-	return false;
-      return arg->field_reference_expression() != NULL;
+      op = OPERATOR_ANDAND;
+      imp->advance(4);
     }
-  return false;
-}
-
-// Return an integer constant value if possible.
-
-bool
-Builtin_call_expression::do_integer_constant_value(bool iota_is_constant,
-						   mpz_t val,
-						   Type** ptype) const
-{
-  if (this->code_ == BUILTIN_LEN
-      || this->code_ == BUILTIN_CAP)
+  else if (imp->match_c_string(" == "))
     {
-      Expression* arg = this->one_arg();
-      if (arg == NULL)
-	return false;
-      Type* arg_type = arg->type();
-
-      if (this->code_ == BUILTIN_LEN
-	  && arg_type->is_string_type()
-	  && arg->classification() == EXPRESSION_STRING)
-	{
-	  String_expression* se = static_cast<String_expression*>(arg);
-	  mpz_set_ui(val, se->val().length());
-	  *ptype = Type::lookup_integer_type("int");
-	  return true;
-	}
-
-      if (arg_type->points_to() != NULL
-	  && arg_type->points_to()->array_type() != NULL
-	  && !arg_type->points_to()->is_open_array_type())
-	arg_type = arg_type->points_to();
-
-      if (arg_type->array_type() != NULL
-	  && arg_type->array_type()->length() != NULL)
-	{
-	  Expression* e = arg_type->array_type()->length();
-	  if (e->integer_constant_value(iota_is_constant, val, ptype))
-	    {
-	      *ptype = Type::lookup_integer_type("int");
-	      return true;
-	    }
-	}
+      op = OPERATOR_EQEQ;
+      imp->advance(4);
     }
-  else if (this->code_ == BUILTIN_SIZEOF
-	   || this->code_ == BUILTIN_ALIGNOF)
+  else if (imp->match_c_string(" != "))
     {
-      Expression* arg = this->one_arg();
-      if (arg == NULL)
-	return false;
-      Type* arg_type = arg->type();
-      if (arg_type->is_error_type())
-	return false;
-      if (arg_type->is_abstract())
-	return false;
-      tree arg_type_tree = arg_type->get_tree(this->gogo_);
-      unsigned long val_long;
-      if (this->code_ == BUILTIN_SIZEOF)
-	{
-	  tree type_size = TYPE_SIZE_UNIT(arg_type_tree);
-	  gcc_assert(TREE_CODE(type_size) == INTEGER_CST);
-	  if (TREE_INT_CST_HIGH(type_size) != 0)
-	    return false;
-	  unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(type_size);
-	  val_long = static_cast<unsigned long>(val_wide);
-	  if (val_long != val_wide)
-	    return false;
-	}
-      else if (this->code_ == BUILTIN_ALIGNOF)
-	{
-	  val_long = TYPE_ALIGN(arg_type_tree);
-	  if (arg->field_reference_expression() != NULL)
-	    {
-	      // Calling unsafe.Alignof(s.f) returns the alignment of
-	      // the type of f when it is used as a field in a struct.
-#ifdef BIGGEST_FIELD_ALIGNMENT
-	      if (val_long > BIGGEST_FIELD_ALIGNMENT)
-		val_long = BIGGEST_FIELD_ALIGNMENT;
-#endif
-#ifdef ADJUST_FIELD_ALIGN
-	      tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL,
-				      arg_type_tree);
-	      val_long = ADJUST_FIELD_ALIGN(field, val_long);
-#endif
-	    }
-	  val_long /= BITS_PER_UNIT;
-	}
-      else
-	gcc_unreachable();
-      mpz_set_ui(val, val_long);
-      *ptype = NULL;
-      return true;
+      op = OPERATOR_NOTEQ;
+      imp->advance(4);
     }
-  else if (this->code_ == BUILTIN_OFFSETOF)
+  else if (imp->match_c_string(" < "))
     {
-      Expression* arg = this->one_arg();
-      if (arg == NULL)
-	return false;
-      Field_reference_expression* farg = arg->field_reference_expression();
-      if (farg == NULL)
-	return false;
-      Expression* struct_expr = farg->expr();
-      Type* st = struct_expr->type();
-      if (st->struct_type() == NULL)
-	return false;
-      tree struct_tree = st->get_tree(this->gogo_);
-      gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
-      tree field = TYPE_FIELDS(struct_tree);
-      for (unsigned int index = farg->field_index(); index > 0; --index)
-	{
-	  field = TREE_CHAIN(field);
-	  gcc_assert(field != NULL_TREE);
-	}
-      HOST_WIDE_INT offset_wide = int_byte_position (field);
-      if (offset_wide < 0)
-	return false;
-      unsigned long offset_long = static_cast<unsigned long>(offset_wide);
-      if (offset_long != static_cast<unsigned HOST_WIDE_INT>(offset_wide))
-	return false;
-      mpz_set_ui(val, offset_long);
-      return true;
+      op = OPERATOR_LT;
+      imp->advance(3);
     }
-  return false;
-}
-
-// Return the type.
-
-Type*
-Builtin_call_expression::do_type()
-{
-  switch (this->code_)
+  else if (imp->match_c_string(" <= "))
     {
-    case BUILTIN_INVALID:
-    default:
-      gcc_unreachable();
-
-    case BUILTIN_NEW:
-    case BUILTIN_MAKE:
-      {
-	const Expression_list* args = this->args();
-	if (args == NULL || args->empty())
-	  return Type::make_error_type();
-	return Type::make_pointer_type(args->front()->type());
-      }
+      op = OPERATOR_LE;
+      imp->advance(4);
+    }
+  else if (imp->match_c_string(" > "))
+    {
+      op = OPERATOR_GT;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" >= "))
+    {
+      op = OPERATOR_GE;
+      imp->advance(4);
+    }
+  else if (imp->match_c_string(" + "))
+    {
+      op = OPERATOR_PLUS;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" - "))
+    {
+      op = OPERATOR_MINUS;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" | "))
+    {
+      op = OPERATOR_OR;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" ^ "))
+    {
+      op = OPERATOR_XOR;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" * "))
+    {
+      op = OPERATOR_MULT;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" / "))
+    {
+      op = OPERATOR_DIV;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" % "))
+    {
+      op = OPERATOR_MOD;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" << "))
+    {
+      op = OPERATOR_LSHIFT;
+      imp->advance(4);
+    }
+  else if (imp->match_c_string(" >> "))
+    {
+      op = OPERATOR_RSHIFT;
+      imp->advance(4);
+    }
+  else if (imp->match_c_string(" & "))
+    {
+      op = OPERATOR_AND;
+      imp->advance(3);
+    }
+  else if (imp->match_c_string(" &^ "))
+    {
+      op = OPERATOR_BITCLEAR;
+      imp->advance(4);
+    }
+  else
+    {
+      error_at(imp->location(), "unrecognized binary operator");
+      return Expression::make_error(imp->location());
+    }
 
-    case BUILTIN_CAP:
-    case BUILTIN_LEN:
-    case BUILTIN_ALIGNOF:
-    case BUILTIN_OFFSETOF:
-    case BUILTIN_SIZEOF:
-      return Type::lookup_integer_type("int");
+  Expression* right = Expression::import_expression(imp);
 
-    case BUILTIN_CLOSE:
-    case BUILTIN_PANIC:
-    case BUILTIN_PANICLN:
-    case BUILTIN_PRINT:
-    case BUILTIN_PRINTLN:
-      return Type::make_void_type();
+  imp->require_c_string(")");
 
-    case BUILTIN_CLOSED:
-      return Type::lookup_bool_type();
-    }
+  return Expression::make_binary(op, left, right, imp->location());
 }
 
-// Determine the type.
+// Make a binary expression.
 
-void
-Builtin_call_expression::do_determine_type(const Type_context*)
+Expression*
+Expression::make_binary(Operator op, Expression* left, Expression* right,
+			source_location location)
 {
-  this->fn()->determine_type_no_context();
-
-  Type_context subcontext(NULL, true);
-  const Expression_list* args = this->args();
-  if (args != NULL)
-    {
-      for (Expression_list::const_iterator pa = args->begin();
-	   pa != args->end();
-	   ++pa)
-	(*pa)->determine_type(&subcontext);
-    }
+  return new Binary_expression(op, left, right, location);
 }
 
-// If there is exactly one argument, return true.  Otherwise give an
-// error message and return false.
+// Implement a comparison.
 
-bool
-Builtin_call_expression::check_one_arg()
+tree
+Expression::comparison_tree(Translate_context* context, Operator op,
+			    Type* left_type, tree left_tree,
+			    Type* right_type, tree right_tree,
+			    source_location location)
 {
-  const Expression_list* args = this->args();
-  if (args == NULL || args->size() < 1)
+  enum tree_code code;
+  switch (op)
     {
-      this->report_error(_("not enough arguments"));
-      return false;
+    case OPERATOR_EQEQ:
+      code = EQ_EXPR;
+      break;
+    case OPERATOR_NOTEQ:
+      code = NE_EXPR;
+      break;
+    case OPERATOR_LT:
+      code = LT_EXPR;
+      break;
+    case OPERATOR_LE:
+      code = LE_EXPR;
+      break;
+    case OPERATOR_GT:
+      code = GT_EXPR;
+      break;
+    case OPERATOR_GE:
+      code = GE_EXPR;
+      break;
+    default:
+      gcc_unreachable();
     }
-  else if (args->size() > 1)
+
+  if (left_type->is_string_type())
     {
-      this->report_error(_("too many arguments"));
-      return false;
+      gcc_assert(right_type->is_string_type());
+      tree string_type = Type::make_string_type()->get_tree(context->gogo());
+      static tree string_compare_decl;
+      left_tree = Gogo::call_builtin(&string_compare_decl,
+				     location,
+				     "__go_strcmp",
+				     2,
+				     integer_type_node,
+				     string_type,
+				     left_tree,
+				     string_type,
+				     right_tree);
+      right_tree = build_int_cst_type(integer_type_node, 0);
     }
-  if (args->front()->is_error_expression()
-      || args->front()->type()->is_error_type())
+
+  if (left_type->interface_type() != NULL
+      && right_type->interface_type() != NULL)
     {
-      this->set_is_error();
-      return false;
+      static tree interface_compare_decl;
+      left_tree = Gogo::call_builtin(&interface_compare_decl,
+				     location,
+				     "__go_interface_compare",
+				     2,
+				     integer_type_node,
+				     const_ptr_type_node,
+				     fold_convert(const_ptr_type_node,
+						  left_tree),
+				     const_ptr_type_node,
+				     fold_convert(const_ptr_type_node,
+						  right_tree));
+      right_tree = build_int_cst_type(integer_type_node, 0);
     }
-  return true;
-}
 
-// Check argument types for a builtin function.
+  if (left_type->is_nil_type()
+      && (op == OPERATOR_EQEQ || op == OPERATOR_NOTEQ))
+    {
+      std::swap(left_type, right_type);
+      std::swap(left_tree, right_tree);
+    }
 
-void
-Builtin_call_expression::do_check_types(Gogo*)
-{
-  switch (this->code_)
+  if (right_type->is_nil_type())
     {
-    case BUILTIN_INVALID:
-    case BUILTIN_NEW:
-    case BUILTIN_MAKE:
-      return;
+      if (left_type->array_type() != NULL
+	  && left_type->array_type()->length() == NULL)
+	{
+	  Array_type* at = left_type->array_type();
+	  left_tree = at->value_pointer_tree(context->gogo(), left_tree);
+	  right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+	}
+      else
+	{
+	  gcc_assert(POINTER_TYPE_P(TREE_TYPE(left_tree)));
+	  right_tree = fold_convert(TREE_TYPE(left_tree), null_pointer_node);
+	}
+    }
 
-    case BUILTIN_LEN:
-    case BUILTIN_CAP:
-      {
-	// The single argument may be either a string or an array or a
-	// map or a channel, or a pointer to a closed array.
-	if (this->check_one_arg())
-	  {
-	    Type* arg_type = this->one_arg()->type();
-	    if (arg_type->points_to() != NULL
-		&& arg_type->points_to()->array_type() != NULL
-		&& !arg_type->points_to()->is_open_array_type())
-	      arg_type = arg_type->points_to();
-	    if (this->code_ == BUILTIN_CAP)
-	      {
-		if (!arg_type->is_error_type()
-		    && arg_type->array_type() == NULL
-		    && arg_type->channel_type() == NULL)
-		  this->report_error(_("argument must be array or slice "
-				       "or channel"));
-	      }
-	    else
-	      {
-		if (!arg_type->is_error_type()
-		    && !arg_type->is_string_type()
-		    && arg_type->array_type() == NULL
-		    && arg_type->map_type() == NULL
-		    && arg_type->channel_type() == NULL)
-		  this->report_error(_("argument must be string or "
-				       "array or slice or map or channel"));
-	      }
-	  }
-      }
-      break;
+  tree ret = fold_build2(code, boolean_type_node, left_tree, right_tree);
+  if (CAN_HAVE_LOCATION_P(ret))
+    SET_EXPR_LOCATION(ret, location);
+  return ret;
+}
 
-    case BUILTIN_PANIC:
-    case BUILTIN_PANICLN:
-    case BUILTIN_PRINT:
-    case BUILTIN_PRINTLN:
-      {
-	const Expression_list* args = this->args();
-	if (args == NULL)
-	  {
-	    if (this->code_ == BUILTIN_PRINT)
-	      warning_at(this->location(), 0,
-			 "no arguments for builtin function %<%s%>",
-			 (this->code_ == BUILTIN_PRINT
-			  ? "print"
-			  : "println"));
-	  }
-	else
-	  {
-	    for (Expression_list::const_iterator p = args->begin();
-		 p != args->end();
-		 ++p)
-	      {
-		Type* type = (*p)->type();
-		if (type->is_error_type()
-		    || type->is_string_type()
-		    || type->integer_type() != NULL
-		    || type->float_type() != NULL
-		    || type->is_boolean_type()
-		    || type->points_to() != NULL
-		    || type->array_type() != NULL
-		    || type->interface_type() != NULL)
-		  ;
-		else
-		  this->report_error(_("unsupported argument type to "
-				       "builtin function"));
-	      }
-	  }
-      }
-      break;
+// Class Bound_method_expression.
 
-    case BUILTIN_CLOSE:
-    case BUILTIN_CLOSED:
-      if (this->check_one_arg())
-	{
-	  if (this->one_arg()->type()->channel_type() == NULL)
-	    this->report_error(_("argument must be channel"));
-	}
-      break;
+// Traversal.
 
-    case BUILTIN_SIZEOF:
-    case BUILTIN_ALIGNOF:
-      this->check_one_arg();
-      break;
+int
+Bound_method_expression::do_traverse(Traverse* traverse)
+{
+  if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  return Expression::traverse(&this->method_, traverse);
+}
 
-    case BUILTIN_OFFSETOF:
-      if (this->check_one_arg())
-	{
-	  Expression* arg = this->one_arg();
-	  if (arg->field_reference_expression() == NULL)
-	    this->report_error(_("argument must be a field reference"));
-	}
-      break;
+// Return the type of a bound method expression.  The type of this
+// object is really the type of the method with no receiver.  We
+// should be able to get away with just returning the type of the
+// method.
 
-    default:
-      gcc_unreachable();
-    }
+Type*
+Bound_method_expression::do_type()
+{
+  return this->method_->type();
 }
 
-// Return the tree for a builtin function.
+// Determine the types of a method expression.
 
-tree
-Builtin_call_expression::do_get_tree(Translate_context* context)
+void
+Bound_method_expression::do_determine_type(const Type_context*)
 {
-  Gogo* gogo = context->gogo();
-  switch (this->code_)
+  this->method_->determine_type_no_context();
+  Type* mtype = this->method_->type();
+  Function_type* fntype = mtype == NULL ? NULL : mtype->function_type();
+  if (fntype == NULL || !fntype->is_method())
+    this->expr_->determine_type_no_context();
+  else
     {
-    case BUILTIN_INVALID:
-    case BUILTIN_NEW:
-    case BUILTIN_MAKE:
-      gcc_unreachable();
+      Type_context subcontext(fntype->receiver()->type(), false);
+      this->expr_->determine_type(&subcontext);
+    }
+}
 
-    case BUILTIN_LEN:
-    case BUILTIN_CAP:
-      {
-	const Expression_list* args = this->args();
-	gcc_assert(args != NULL && args->size() == 1);
-	Expression* arg = *args->begin();
-	Type* arg_type = arg->type();
-	tree arg_tree = arg->get_tree(context);
-	if (arg_tree == error_mark_node)
-	  return error_mark_node;
+// Check the types of a method expression.
 
-	if (arg_type->points_to() != NULL)
-	  {
-	    arg_type = arg_type->points_to();
-	    gcc_assert(arg_type->array_type() != NULL
-		       && !arg_type->is_open_array_type());
-	    gcc_assert(POINTER_TYPE_P(TREE_TYPE(arg_tree)));
-	    arg_tree = build_fold_indirect_ref(arg_tree);
-	  }
+void
+Bound_method_expression::do_check_types(Gogo*)
+{
+  Type* type = this->method_->type()->deref();
+  if (type == NULL
+      || type->function_type() == NULL
+      || !type->function_type()->is_method())
+    this->report_error(_("object is not a method"));
+  else
+    {
+      Type* rtype = type->function_type()->receiver()->type()->deref();
+      Type* etype = (this->expr_type_ != NULL
+		     ? this->expr_type_
+		     : this->expr_->type());
+      etype = etype->deref();
+      if (!Type::are_identical(rtype, etype))
+	this->report_error(_("method type does not match object type"));
+    }
+}
 
-	tree val_tree;
-	if (this->code_ == BUILTIN_LEN)
-	  {
-	    if (arg_type->is_string_type())
-	      val_tree = String_type::length_tree(gogo, arg_tree);
-	    else if (arg_type->array_type() != NULL)
-	      val_tree = arg_type->array_type()->length_tree(gogo, arg_tree);
-	    else if (arg_type->map_type() != NULL)
-	      {
-		static tree map_len_fndecl;
-		val_tree = Gogo::call_builtin(&map_len_fndecl,
-					      this->location(),
-					      "__go_map_len",
-					      1,
-					      sizetype,
-					      arg_type->get_tree(gogo),
-					      arg_tree);
-	      }
-	    else if (arg_type->channel_type() != NULL)
-	      {
-		static tree chan_len_fndecl;
-		val_tree = Gogo::call_builtin(&chan_len_fndecl,
-					      this->location(),
-					      "__go_chan_len",
-					      1,
-					      sizetype,
-					      arg_type->get_tree(gogo),
-					      arg_tree);
-	      }
-	    else
-	      gcc_unreachable();
-	  }
-	else
-	  {
-	    if (arg_type->array_type() != NULL)
-	      val_tree = arg_type->array_type()->capacity_tree(gogo, arg_tree);
-	    else if (arg_type->channel_type() != NULL)
-	      {
-		static tree chan_cap_fndecl;
-		val_tree = Gogo::call_builtin(&chan_cap_fndecl,
-					      this->location(),
-					      "__go_chan_cap",
-					      1,
-					      sizetype,
-					      arg_type->get_tree(gogo),
-					      arg_tree);
-	      }
-	    else
-	      gcc_unreachable();
-	  }
+// Get the tree for a method expression.  There is no standard tree
+// representation for this.  The only places it may currently be used
+// are in a Call_expression or a Go_statement, which will take it
+// apart directly.  So this has nothing to do at present.
 
-	tree type_tree = Type::lookup_integer_type("int")->get_tree(gogo);
-	if (type_tree == TREE_TYPE(val_tree))
-	  return val_tree;
-	else
-	  return fold(convert_to_integer(type_tree, val_tree));
-      }
+tree
+Bound_method_expression::do_get_tree(Translate_context*)
+{
+  gcc_unreachable();
+}
 
-    case BUILTIN_PANIC:
-    case BUILTIN_PANICLN:
-    case BUILTIN_PRINT:
-    case BUILTIN_PRINTLN:
-      {
-	const bool is_panic = (this->code_ == BUILTIN_PANIC
-			       || this->code_ == BUILTIN_PANICLN);
-	const bool is_ln = (this->code_ == BUILTIN_PANICLN
-			    || this->code_ == BUILTIN_PRINTLN);
-	const Expression_list* call_args = this->args();
+// Make a method expression.
 
-	int nargs;
-	tree* args;
-	std::string format;
-	if (call_args == NULL)
-	  {
-	    if (!is_panic && !is_ln)
-	      {
-		// A call to print with no arguments.  There is nothing
-		// to do.
-		return integer_zero_node;
-	      }
-	    nargs = 1;
-	    args = new tree[1];
-	  }
-	else
-	  {
-	    nargs = call_args->size() + 1;
-
-	    // We allocate extra space because we use three arguments
-	    // to print arrays.
-	    args = new tree[nargs * 3];
+Bound_method_expression*
+Expression::make_bound_method(Expression* expr, Expression* method,
+			      source_location location)
+{
+  return new Bound_method_expression(expr, method, location);
+}
 
-	    int i = 1;
-	    for (Expression_list::const_iterator p = call_args->begin();
-		 p != call_args->end();
-		 ++p, ++i)
-	      {
-		if (is_ln && i > 1)
-		  format += " ";
+// Class Builtin_call_expression.  This is used for a call to a
+// builtin function.
 
-		args[i] = (*p)->get_tree(context);
+class Builtin_call_expression : public Call_expression
+{
+ public:
+  Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
+			  source_location location);
 
-		if (args[i] == error_mark_node)
-		  {
-		    args[i] = integer_zero_node;
-		    format += "%d";
-		  }
-		else if ((*p)->type()->is_string_type())
-		  {
-		    // We use a precision to print the right number of
-		    // characters.  FIXME: If the string has embedded
-		    // null bytes, it won't be printed correctly.
-		    tree string = args[i];
-		    tree len = String_type::length_tree(gogo, string);
-		    args[i] = convert_to_integer(integer_type_node, len);
-		    ++i;
-		    ++nargs;
-		    args[i] = String_type::bytes_tree(gogo, string);
-		    format += "%.*s";
-		  }
-		else if ((*p)->type()->integer_type() != NULL)
-		  {
-		    const Integer_type* itype = (*p)->type()->integer_type();
-		    int bits = TYPE_PRECISION(TREE_TYPE(args[i]));
-		    if (bits <= INT_TYPE_SIZE)
-		      {
-			args[i] = fold_convert((itype->is_unsigned()
-						? unsigned_type_node
-						: integer_type_node),
-					       args[i]);
-			format += itype->is_unsigned() ? "%u" : "%d";
-		      }
-		    else if (bits <= LONG_TYPE_SIZE)
-		      {
-			args[i] = fold_convert((itype->is_unsigned()
-						? long_unsigned_type_node
-						: long_integer_type_node),
-					       args[i]);
-			format += itype->is_unsigned() ? "%lu" : "%ld";
-		      }
-		    else if (bits <= LONG_LONG_TYPE_SIZE)
-		      {
-			args[i] = fold_convert((itype->is_unsigned()
-						? long_long_unsigned_type_node
-						: long_long_integer_type_node),
-					       args[i]);
-			format += itype->is_unsigned() ? "%llu" : "%lld";
-		      }
-		    else
-		      gcc_unreachable();
-		  }
-		else if ((*p)->type()->float_type() != NULL)
-		  {
-		    args[i] = fold_convert(double_type_node, args[i]);
-		    format += "%.24g";
-		  }
-		else if ((*p)->type()->is_boolean_type())
-		  {
-		    tree string_type = Gogo::const_char_pointer_type_tree();
-		    tree true_string = Gogo::string_constant_tree("true");
-		    true_string = build_fold_addr_expr(true_string);
-		    true_string = fold_convert(string_type, true_string);
-		    tree false_string = Gogo::string_constant_tree("false");
-		    false_string = build_fold_addr_expr(false_string);
-		    false_string = fold_convert(string_type, false_string);
-		    args[i] = fold_build3(COND_EXPR, string_type, args[i],
-					  true_string, false_string);
-		    format += "%s";
-		  }
-		else if ((*p)->type()->points_to() != NULL
-			 || (*p)->type()->interface_type() != NULL)
-		  {
-		    args[i] = fold_convert(ptr_type_node, args[i]);
-		    format += "%p";
-		  }
-		else if ((*p)->type()->array_type() != NULL)
-		  {
-		    Array_type* at = (*p)->type()->array_type();
-		    tree v = save_expr(args[i]);
-		    args[i] = at->length_tree(gogo, v);
-		    ++i;
-		    ++nargs;
-		    args[i] = at->capacity_tree(gogo, v);
-		    ++i;
-		    ++nargs;
-		    args[i] = at->value_pointer_tree(gogo, v);
-		    format += "[%zu/%zu]%p";
-		  }
-		else
-		  {
-		    args[i] = integer_zero_node;
-		    format += "%d";
-		  }
-	      }
-	    gcc_assert(i == nargs);
-	  }
+ protected:
+  // This overrides Call_expression::do_lower.
+  Expression*
+  do_lower(Gogo*, int);
 
-	if (is_ln)
-	  format += "\n";
+  bool
+  do_is_constant() const;
 
-	tree string_val = Gogo::string_constant_tree(format);
-	args[0] = build_fold_addr_expr(string_val);
+  bool
+  do_integer_constant_value(bool, mpz_t, Type**) const;
 
-	static tree panic_fndecl;
-	static tree print_fndecl;
-	static tree* pfndecl;
-	if (is_panic)
-	  pfndecl = &panic_fndecl;
-	else
-	  pfndecl = &print_fndecl;
-	if (*pfndecl == NULL_TREE)
-	  {
-	    tree fnid = get_identifier(is_panic ? "__go_panic" : "__go_print");
-	    tree argtypes = tree_cons(NULL_TREE,
-				      Gogo::const_char_pointer_type_tree(),
-				      NULL_TREE);
-	    tree fntype = build_function_type(void_type_node, argtypes);
+  Type*
+  do_type();
 
-	    *pfndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid,
-				  fntype);
-	    Gogo::mark_fndecl_as_builtin_library(*pfndecl);
-	    if (is_panic)
-	      {
-		// Mark the function as noreturn.
-		TREE_THIS_VOLATILE(*pfndecl) = 1;
-	      }
-	    go_preserve_from_gc(*pfndecl);
-	  }
+  void
+  do_determine_type(const Type_context*);
 
-	tree fnptr = build_fold_addr_expr(*pfndecl);
-	tree call = build_call_array(void_type_node, fnptr, nargs, args);
-	delete[] args;
+  void
+  do_check_types(Gogo*);
 
-	SET_EXPR_LOCATION(call, this->location());
+  Expression*
+  do_copy()
+  {
+    return new Builtin_call_expression(this->gogo_, this->fn()->copy(),
+				       this->args()->copy(),
+				       this->location());
+  }
 
-	return call;
-      }
+  tree
+  do_get_tree(Translate_context*);
 
-    case BUILTIN_CLOSE:
-    case BUILTIN_CLOSED:
-      {
-	const Expression_list* args = this->args();
-	gcc_assert(args != NULL && args->size() == 1);
-	Expression* arg = args->front();
-	tree arg_tree = arg->get_tree(context);
-	if (arg_tree == error_mark_node)
-	  return error_mark_node;
-	if (this->code_ == BUILTIN_CLOSE)
-	  {
-	    static tree close_fndecl;
-	    return Gogo::call_builtin(&close_fndecl,
-				      this->location(),
-				      "__go_builtin_close",
-				      1,
-				      void_type_node,
-				      TREE_TYPE(arg_tree),
-				      arg_tree);
-	  }
-	else
-	  {
-	    static tree closed_fndecl;
-	    return Gogo::call_builtin(&closed_fndecl,
-				      this->location(),
-				      "__go_builtin_closed",
-				      1,
-				      boolean_type_node,
-				      TREE_TYPE(arg_tree),
-				      arg_tree);
-	  }
-      }
+  void
+  do_export(Export*) const;
 
-    case BUILTIN_SIZEOF:
-    case BUILTIN_OFFSETOF:
-    case BUILTIN_ALIGNOF:
-      {
-	mpz_t val;
-	mpz_init(val);
-	Type* dummy;
-	bool b = this->integer_constant_value(true, val, &dummy);
-	gcc_assert(b);
-	tree type = Type::lookup_integer_type("int")->get_tree(gogo);
-	tree ret = Expression::integer_constant_tree(val, type);
-	mpz_clear(val);
-	return ret;
-      }
+ private:
+  // The builtin functions.
+  enum Builtin_function_code
+    {
+      BUILTIN_INVALID,
 
-    default:
-      gcc_unreachable();
-    }
-}
+      // Predeclared builtin functions.
+      BUILTIN_CAP,
+      BUILTIN_CLOSE,
+      BUILTIN_CLOSED,
+      BUILTIN_LEN,
+      BUILTIN_MAKE,
+      BUILTIN_NEW,
+      BUILTIN_PANIC,
+      BUILTIN_PANICLN,
+      BUILTIN_PRINT,
+      BUILTIN_PRINTLN,
 
-// We have to support exporting a builtin call expression, because
-// code can set a constant to the result of a builtin expression.
+      // Builtin functions from the unsafe package.
+      BUILTIN_ALIGNOF,
+      BUILTIN_OFFSETOF,
+      BUILTIN_SIZEOF
+    };
 
-void
-Builtin_call_expression::do_export(Export* exp) const
-{
-  mpz_t val;
-  mpz_init(val);
-  Type* dummy;
-  bool b = this->integer_constant_value(true, val, &dummy);
-  if (!b)
-    error_at(this->location(), "value is not constant");
-  char* s = mpz_get_str(NULL, 10, val);
-  exp->write_c_string(s);
-  free(s);
-  // A trailing space lets us reliably identify the end of the number.
-  exp->write_c_string(" ");
-  mpz_clear(val);
-}
+  Expression*
+  one_arg() const;
 
-// Class Call_expression.
+  bool
+  check_one_arg();
 
-// Traversal.
+  // A pointer back to the general IR structure.  This avoids a global
+  // variable, or passing it around everywhere.
+  Gogo* gogo_;
+  // The builtin function being called.
+  Builtin_function_code code_;
+};
 
-int
-Call_expression::do_traverse(Traverse* traverse)
+Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
+						 Expression* fn,
+						 Expression_list* args,
+						 source_location location)
+  : Call_expression(fn, args, location),
+    gogo_(gogo), code_(BUILTIN_INVALID)
 {
-  if (Expression::traverse(&this->fn_, traverse) == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  if (this->args_ != NULL)
-    {
-      if (this->args_->traverse(traverse) == TRAVERSE_EXIT)
-	return TRAVERSE_EXIT;
-    }
-  return TRAVERSE_CONTINUE;
+  Func_expression* fnexp = this->fn()->func_expression();
+  gcc_assert(fnexp != NULL);
+  const std::string& name(fnexp->named_object()->name());
+  if (name == "cap")
+    this->code_ = BUILTIN_CAP;
+  else if (name == "close")
+    this->code_ = BUILTIN_CLOSE;
+  else if (name == "closed")
+    this->code_ = BUILTIN_CLOSED;
+  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 == "panicln")
+    this->code_ = BUILTIN_PANICLN;
+  else if (name == "print")
+    this->code_ = BUILTIN_PRINT;
+  else if (name == "println")
+    this->code_ = BUILTIN_PRINTLN;
+  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
+    gcc_unreachable();
 }
 
-// Lower a call statement.  This is where we handle an argument which
-// is a call to a function which returns multiple results.  We also
-// may discover that we thought was a call is actually a type cast.
+// Lower a builtin call expression.  This turns new and make into
+// specific expressions.
 
 Expression*
-Call_expression::do_lower(Gogo* gogo, int)
+Builtin_call_expression::do_lower(Gogo*, int)
 {
-  if (this->fn_->is_type_expression()
-      && this->args_ != NULL
-      && this->args_->size() == 1)
-    return Expression::make_cast(this->fn_->type(), this->args_->front(),
-				 this->location());
-
-  Func_expression* fne = this->fn_->func_expression();
-  if (fne != NULL
-      && fne->named_object()->is_function_declaration()
-      && fne->named_object()->func_declaration_value()->type()->is_builtin())
-    return new Builtin_call_expression(gogo, this->fn_, this->args_,
-				       this->location());
-
-  if (this->args_ != NULL
-      && this->args_->size() == 1
-      && this->args_->front()->call_expression() != NULL
-      && this->fn_->type()->function_type() != NULL)
+  if (this->code_ == BUILTIN_NEW)
     {
-      Function_type* fntype = this->fn_->type()->function_type();
-      size_t rc = this->args_->front()->call_expression()->result_count();
-      if (rc > 1
-	  && fntype->parameters() != NULL
-	  && (fntype->parameters()->size() == rc
-	      || (fntype->is_varargs()
-		  && fntype->parameters()->size() - 1 <= rc)))
+      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
 	{
-	  Call_expression* call = this->args_->front()->call_expression();
-	  Expression_list* args = new Expression_list;
-	  for (size_t i = 0; i < rc; ++i)
-	    args->push_back(Expression::make_call_result(call, i));
-	  // We can't return a new call expression here, because this
-	  // one may be referenced by Call_result expressions.  FIXME.
-	  delete this->args_;
-	  this->args_ = args;
+	  Expression* arg = args->front();
+	  if (!arg->is_type_expression())
+	    {
+	      error_at(arg->location(), _("expected type"));
+	      this->set_is_error();
+	    }
+	  else
+	    return Expression::make_allocation(arg->type(), this->location());
+	}
+    }
+  else if (this->code_ == BUILTIN_MAKE)
+    {
+      const Expression_list* args = this->args();
+      if (args == NULL || args->size() < 1)
+	this->report_error(_("not enough arguments"));
+      else
+	{
+	  Expression* arg = args->front();
+	  if (!arg->is_type_expression())
+	    {
+	      error_at(arg->location(), _("expected type"));
+	      this->set_is_error();
+	    }
+	  else
+	    {
+	      Expression_list* newargs;
+	      if (args->size() == 1)
+		newargs = NULL;
+	      else
+		{
+		  newargs = new Expression_list();
+		  Expression_list::const_iterator p = args->begin();
+		  ++p;
+		  for (; p != args->end(); ++p)
+		    newargs->push_back(*p);
+		}
+	      return Expression::make_make(arg->type(), newargs,
+					   this->location());
+	    }
 	}
     }
 
   return this;
 }
 
-// Get the function type.  Returns NULL if we don't know the type.  If
-// this returns NULL, and if_ERROR is true, issues an error.
-
-Function_type*
-Call_expression::get_function_type(bool issue_error)
-{
-  Type* type = this->fn_->type()->deref();
-  Function_type* fntype = type->function_type();
-  if (fntype != NULL)
-    return fntype;
-  if (issue_error && !type->is_error_type())
-    this->report_error("expected function");
-  return NULL;
-}
-
-// Return the number of values which this call will return.
+// Return a single argument, or NULL if there isn't one.
 
-size_t
-Call_expression::result_count()
+Expression*
+Builtin_call_expression::one_arg() const
 {
-  Function_type* fntype = this->get_function_type(false);
-  if (fntype == NULL)
-    return 0;
-  if (fntype->results() == NULL)
-    return 0;
-  return fntype->results()->size();
+  const Expression_list* args = this->args();
+  if (args->size() != 1)
+    return NULL;
+  return args->front();
 }
 
-// Get the type.
+// Return whether this is constant: len of a string, or len or cap of
+// a fixed array, or unsafe.Sizeof, unsafe.Offsetof, unsafe.Alignof.
 
-Type*
-Call_expression::do_type()
+bool
+Builtin_call_expression::do_is_constant() const
 {
-  if (this->type_ != NULL)
-    return this->type_;
-
-  Type* ret;
-  Function_type* fntype = this->get_function_type(false);
-  if (fntype == NULL)
-    return Type::make_error_type();
+  if (this->code_ == BUILTIN_LEN
+      || this->code_ == BUILTIN_CAP)
+    {
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+      Type* arg_type = arg->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);
+      if (arg_type->points_to() != NULL
+	  && arg_type->points_to()->array_type() != NULL
+	  && !arg_type->points_to()->is_open_array_type())
+	arg_type = arg_type->points_to();
 
-  this->type_ = ret;
+      if (arg_type->array_type() != NULL
+	  && arg_type->array_type()->length() != NULL)
+	return arg_type->array_type()->length()->is_constant();
 
-  return this->type_;
+      if (this->code_ == BUILTIN_LEN && arg_type->is_string_type())
+	return arg->is_constant();
+    }
+  else if (this->code_ == BUILTIN_SIZEOF
+	   || this->code_ == BUILTIN_ALIGNOF)
+    return this->one_arg() != NULL;
+  else if (this->code_ == BUILTIN_OFFSETOF)
+    {
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+      return arg->field_reference_expression() != NULL;
+    }
+  return false;
 }
 
-// Determine types for a call expression.  We can use the function
-// parameter types to set the types of the arguments.
+// Return an integer constant value if possible.
 
-void
-Call_expression::do_determine_type(const Type_context*)
-{
-  this->fn_->determine_type_no_context();
-  Function_type* fntype = this->get_function_type(false);
-  const Typed_identifier_list* parameters = NULL;
-  if (fntype != NULL)
-    parameters = fntype->parameters();
-  if (this->args_ != NULL)
+bool
+Builtin_call_expression::do_integer_constant_value(bool iota_is_constant,
+						   mpz_t val,
+						   Type** ptype) const
+{
+  if (this->code_ == BUILTIN_LEN
+      || this->code_ == BUILTIN_CAP)
     {
-      Typed_identifier_list::const_iterator pt;
-      if (parameters != NULL)
-	pt = parameters->begin();
-      for (Expression_list::const_iterator pa = this->args_->begin();
-	   pa != this->args_->end();
-	   ++pa)
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+      Type* arg_type = arg->type();
+
+      if (this->code_ == BUILTIN_LEN
+	  && arg_type->is_string_type()
+	  && arg->classification() == EXPRESSION_STRING)
 	{
-	  if (parameters != NULL
-	      && pt != parameters->end()
-	      && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+	  String_expression* se = static_cast<String_expression*>(arg);
+	  mpz_set_ui(val, se->val().length());
+	  *ptype = Type::lookup_integer_type("int");
+	  return true;
+	}
+
+      if (arg_type->points_to() != NULL
+	  && arg_type->points_to()->array_type() != NULL
+	  && !arg_type->points_to()->is_open_array_type())
+	arg_type = arg_type->points_to();
+
+      if (arg_type->array_type() != NULL
+	  && arg_type->array_type()->length() != NULL)
+	{
+	  Expression* e = arg_type->array_type()->length();
+	  if (e->integer_constant_value(iota_is_constant, val, ptype))
 	    {
-	      Type_context subcontext(pt->type(), false);
-	      (*pa)->determine_type(&subcontext);
-	      ++pt;
+	      *ptype = Type::lookup_integer_type("int");
+	      return true;
 	    }
-	  else
-	    (*pa)->determine_type_no_context();
 	}
     }
+  else if (this->code_ == BUILTIN_SIZEOF
+	   || this->code_ == BUILTIN_ALIGNOF)
+    {
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+      Type* arg_type = arg->type();
+      if (arg_type->is_error_type())
+	return false;
+      if (arg_type->is_abstract())
+	return false;
+      tree arg_type_tree = arg_type->get_tree(this->gogo_);
+      unsigned long val_long;
+      if (this->code_ == BUILTIN_SIZEOF)
+	{
+	  tree type_size = TYPE_SIZE_UNIT(arg_type_tree);
+	  gcc_assert(TREE_CODE(type_size) == INTEGER_CST);
+	  if (TREE_INT_CST_HIGH(type_size) != 0)
+	    return false;
+	  unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW(type_size);
+	  val_long = static_cast<unsigned long>(val_wide);
+	  if (val_long != val_wide)
+	    return false;
+	}
+      else if (this->code_ == BUILTIN_ALIGNOF)
+	{
+	  val_long = TYPE_ALIGN(arg_type_tree);
+	  if (arg->field_reference_expression() != NULL)
+	    {
+	      // Calling unsafe.Alignof(s.f) returns the alignment of
+	      // the type of f when it is used as a field in a struct.
+#ifdef BIGGEST_FIELD_ALIGNMENT
+	      if (val_long > BIGGEST_FIELD_ALIGNMENT)
+		val_long = BIGGEST_FIELD_ALIGNMENT;
+#endif
+#ifdef ADJUST_FIELD_ALIGN
+	      tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL,
+				      arg_type_tree);
+	      val_long = ADJUST_FIELD_ALIGN(field, val_long);
+#endif
+	    }
+	  val_long /= BITS_PER_UNIT;
+	}
+      else
+	gcc_unreachable();
+      mpz_set_ui(val, val_long);
+      *ptype = NULL;
+      return true;
+    }
+  else if (this->code_ == BUILTIN_OFFSETOF)
+    {
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+      Field_reference_expression* farg = arg->field_reference_expression();
+      if (farg == NULL)
+	return false;
+      Expression* struct_expr = farg->expr();
+      Type* st = struct_expr->type();
+      if (st->struct_type() == NULL)
+	return false;
+      tree struct_tree = st->get_tree(this->gogo_);
+      gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
+      tree field = TYPE_FIELDS(struct_tree);
+      for (unsigned int index = farg->field_index(); index > 0; --index)
+	{
+	  field = TREE_CHAIN(field);
+	  gcc_assert(field != NULL_TREE);
+	}
+      HOST_WIDE_INT offset_wide = int_byte_position (field);
+      if (offset_wide < 0)
+	return false;
+      unsigned long offset_long = static_cast<unsigned long>(offset_wide);
+      if (offset_long != static_cast<unsigned HOST_WIDE_INT>(offset_wide))
+	return false;
+      mpz_set_ui(val, offset_long);
+      return true;
+    }
+  return false;
 }
 
-// Check types for parameter I.
+// Return the type.
 
-void
-Call_expression::check_argument_type(int i, const Type* parameter_type,
-				     const Type* argument_type,
-				     source_location argument_location)
+Type*
+Builtin_call_expression::do_type()
 {
-  std::string reason;
-  if (!Type::are_compatible_for_assign(parameter_type, argument_type, &reason))
+  switch (this->code_)
     {
-      if (reason.empty())
-	error_at(argument_location, "argument %d has incompatible type", i);
-      else
-	error_at(argument_location, "argument %d has incompatible type (%s)",
-		 i, reason.c_str());
-      this->set_is_error();
+    case BUILTIN_INVALID:
+    default:
+      gcc_unreachable();
+
+    case BUILTIN_NEW:
+    case BUILTIN_MAKE:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->empty())
+	  return Type::make_error_type();
+	return Type::make_pointer_type(args->front()->type());
+      }
+
+    case BUILTIN_CAP:
+    case BUILTIN_LEN:
+    case BUILTIN_ALIGNOF:
+    case BUILTIN_OFFSETOF:
+    case BUILTIN_SIZEOF:
+      return Type::lookup_integer_type("int");
+
+    case BUILTIN_CLOSE:
+    case BUILTIN_PANIC:
+    case BUILTIN_PANICLN:
+    case BUILTIN_PRINT:
+    case BUILTIN_PRINTLN:
+      return Type::make_void_type();
+
+    case BUILTIN_CLOSED:
+      return Type::lookup_bool_type();
     }
 }
 
-// Check types.
+// Determine the type.
 
 void
-Call_expression::do_check_types(Gogo*)
+Builtin_call_expression::do_determine_type(const Type_context*)
 {
-  Function_type* fntype = this->get_function_type(true);
-  if (fntype == NULL)
-    return;
+  this->fn()->determine_type_no_context();
 
-  if (fntype->is_method())
+  Type_context subcontext(NULL, true);
+  const Expression_list* args = this->args();
+  if (args != NULL)
     {
-      // We don't support pointers to methods, so the function has to
-      // be a bound method expression.
-      if (this->fn_->bound_method_expression() == NULL)
-	this->report_error(_("method call without object"));
+      for (Expression_list::const_iterator pa = args->begin();
+	   pa != args->end();
+	   ++pa)
+	(*pa)->determine_type(&subcontext);
     }
+}
 
-  const Typed_identifier_list* parameters = fntype->parameters();
-  if (this->args_ == NULL)
+// If there is exactly one argument, return true.  Otherwise give an
+// error message and return false.
+
+bool
+Builtin_call_expression::check_one_arg()
+{
+  const Expression_list* args = this->args();
+  if (args == NULL || args->size() < 1)
     {
-      // A single varargs parameter can accept zero arguments.
-      if (parameters != NULL
-	  && (!fntype->is_varargs() || parameters->size() > 1))
-	this->report_error(_("not enough arguments"));
+      this->report_error(_("not enough arguments"));
+      return false;
     }
-  else if (parameters == NULL)
-    this->report_error(_("too many arguments"));
-  else
+  else if (args->size() > 1)
     {
-      int i = 0;
-      Typed_identifier_list::const_iterator pt = parameters->begin();
-      for (Expression_list::const_iterator pa = this->args_->begin();
-	   pa != this->args_->end();
-	   ++pa, ++pt, ++i)
-	{
-	  if (fntype->is_varargs() && pt + 1 == parameters->end())
-	    {
-	      // A varargs parameter can accept any number of
-	      // arguments, but you can't pass nil.
-	      for (; pa != this->args_->end(); ++pa)
-		{
-		  if ((*pa)->type()->is_nil_type())
-		    {
-		      this->report_error(_("invalid use of %<nil%> for "
-					   "%<...%> argument"));
-		      return;
-		    }
-		}
-	      break;
-	    }
-	  if (pt == parameters->end())
-	    {
-	      this->report_error(_("too many arguments"));
-	      return;
-	    }
-	  this->check_argument_type(i + 1, pt->type(), (*pa)->type(),
-				    (*pa)->location());
-	}
-      // The varargs parameter can accept zero arguments.
-      if (pt != parameters->end()
-	  && (!fntype->is_varargs() || pt + 1 != parameters->end()))
-	this->report_error(_("not enough arguments"));
+      this->report_error(_("too many arguments"));
+      return false;
     }
-}
-
-// Get the function and the first argument to use when calling a bound
-// method.
-
-tree
-Call_expression::bound_method_function(Translate_context* context,
-				       Bound_method_expression* bound_method,
-				       tree* first_arg_ptr)
-{
-  Expression* first_argument = bound_method->first_argument();
-  tree first_arg = first_argument->get_tree(context);
-  if (first_arg == error_mark_node)
-    return error_mark_node;
-
-  // We always pass a pointer to the first argument when calling a
-  // method.
-  if (first_argument->type()->points_to() == NULL)
-    {
-      tree pointer_to_arg_type = build_pointer_type(TREE_TYPE(first_arg));
-      if (TREE_ADDRESSABLE(TREE_TYPE(first_arg))
-	  || DECL_P(first_arg)
-	  || TREE_CODE(first_arg) == INDIRECT_REF
-	  || TREE_CODE(first_arg) == COMPONENT_REF)
-	{
-	  first_arg = build_fold_addr_expr(first_arg);
-	  if (DECL_P(first_arg))
-	    TREE_ADDRESSABLE(first_arg) = 1;
-	}
-      else
-	{
-	  tree tmp = create_tmp_var(TREE_TYPE(first_arg),
-				    get_name(first_arg));
-	  DECL_IGNORED_P(tmp) = 0;
-	  DECL_INITIAL(tmp) = first_arg;
-	  first_arg = build2(COMPOUND_EXPR, pointer_to_arg_type,
-			     build1(DECL_EXPR, void_type_node, tmp),
-			     build_fold_addr_expr(tmp));
-	  TREE_ADDRESSABLE(tmp) = 1;
-	}
-      if (first_arg == error_mark_node)
-	return error_mark_node;
-    }
-
-  Type* fatype = bound_method->first_argument_type();
-  if (fatype != NULL)
+  if (args->front()->is_error_expression()
+      || args->front()->type()->is_error_type())
     {
-      if (fatype->points_to() == NULL)
-	fatype = Type::make_pointer_type(fatype);
-      first_arg = fold_convert(fatype->get_tree(context->gogo()), first_arg);
-      if (first_arg == error_mark_node
-	  || TREE_TYPE(first_arg) == error_mark_node)
-	return error_mark_node;
+      this->set_is_error();
+      return false;
     }
-
-  *first_arg_ptr = first_arg;
-
-  return bound_method->method()->get_tree(context);
+  return true;
 }
 
-// Get the function and the first argument to use when calling an
-// interface method.
+// Check argument types for a builtin function.
 
-tree
-Call_expression::interface_method_function(
-    Translate_context* context,
-    Interface_field_reference_expression* interface_method,
-    tree* first_arg_ptr)
+void
+Builtin_call_expression::do_check_types(Gogo*)
 {
-  tree expr = interface_method->expr()->get_tree(context);
-  if (expr == error_mark_node)
-    return error_mark_node;
-  expr = save_expr(expr);
-  tree first_arg = interface_method->get_underlying_object_tree(context, expr);
-  if (first_arg == error_mark_node)
-    return error_mark_node;
-  *first_arg_ptr = first_arg;
-  return interface_method->get_function_tree(context, expr);
-}
+  switch (this->code_)
+    {
+    case BUILTIN_INVALID:
+    case BUILTIN_NEW:
+    case BUILTIN_MAKE:
+      return;
 
-// The call expression is being copied.  There is nothing to do here
-// except to disable any decrements--the function should return with
-// an appropriate reference count.
+    case BUILTIN_LEN:
+    case BUILTIN_CAP:
+      {
+	// The single argument may be either a string or an array or a
+	// map or a channel, or a pointer to a closed array.
+	if (this->check_one_arg())
+	  {
+	    Type* arg_type = this->one_arg()->type();
+	    if (arg_type->points_to() != NULL
+		&& arg_type->points_to()->array_type() != NULL
+		&& !arg_type->points_to()->is_open_array_type())
+	      arg_type = arg_type->points_to();
+	    if (this->code_ == BUILTIN_CAP)
+	      {
+		if (!arg_type->is_error_type()
+		    && arg_type->array_type() == NULL
+		    && arg_type->channel_type() == NULL)
+		  this->report_error(_("argument must be array or slice "
+				       "or channel"));
+	      }
+	    else
+	      {
+		if (!arg_type->is_error_type()
+		    && !arg_type->is_string_type()
+		    && arg_type->array_type() == NULL
+		    && arg_type->map_type() == NULL
+		    && arg_type->channel_type() == NULL)
+		  this->report_error(_("argument must be string or "
+				       "array or slice or map or channel"));
+	      }
+	  }
+      }
+      break;
 
-Expression*
-Call_expression::do_being_copied(Refcounts*, bool)
-{
-  this->is_being_copied_ = true;
-  return this;
-}
+    case BUILTIN_PANIC:
+    case BUILTIN_PANICLN:
+    case BUILTIN_PRINT:
+    case BUILTIN_PRINTLN:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL)
+	  {
+	    if (this->code_ == BUILTIN_PRINT)
+	      warning_at(this->location(), 0,
+			 "no arguments for builtin function %<%s%>",
+			 (this->code_ == BUILTIN_PRINT
+			  ? "print"
+			  : "println"));
+	  }
+	else
+	  {
+	    for (Expression_list::const_iterator p = args->begin();
+		 p != args->end();
+		 ++p)
+	      {
+		Type* type = (*p)->type();
+		if (type->is_error_type()
+		    || type->is_string_type()
+		    || type->integer_type() != NULL
+		    || type->float_type() != NULL
+		    || type->is_boolean_type()
+		    || type->points_to() != NULL
+		    || type->array_type() != NULL
+		    || type->interface_type() != NULL)
+		  ;
+		else
+		  this->report_error(_("unsupported argument type to "
+				       "builtin function"));
+	      }
+	  }
+      }
+      break;
 
-// Where needed, decrement the reference counts of any values returned
-// by this call.
+    case BUILTIN_CLOSE:
+    case BUILTIN_CLOSED:
+      if (this->check_one_arg())
+	{
+	  if (this->one_arg()->type()->channel_type() == NULL)
+	    this->report_error(_("argument must be channel"));
+	}
+      break;
 
-Expression*
-Call_expression::do_note_decrements(Refcounts* refcounts)
-{
-  if (this->is_being_copied_)
-    return this;
-  Function_type* fntype = this->get_function_type(false);
-  if (fntype == NULL)
-    return this;
-  const Typed_identifier_list* results = fntype->results();
-  if (results == NULL)
-    return this;
-  if (results->size() == 1)
-    {
-      if (!results->begin()->type()->has_refcounted_component())
-	return this;
-      return Expression::make_refcount_adjust(refcounts,
-					      REFCOUNT_DECREMENT_COMPUTED,
-					      this, false);
-    }
-  else if (!this->is_value_discarded_)
-    {
-      // If the value is not discarded, each result will be handled
-      // separately via Call_expression_result.
-      return this;
-    }
-  else
-    {
-      for (Typed_identifier_list::const_iterator p = results->begin();
-	   p != results->end();
-	   p++)
+    case BUILTIN_SIZEOF:
+    case BUILTIN_ALIGNOF:
+      this->check_one_arg();
+      break;
+
+    case BUILTIN_OFFSETOF:
+      if (this->check_one_arg())
 	{
-	  if (p->type()->has_refcounted_component())
-	    {
-	      if (this->refcount_entries_ == NULL)
-		this->refcount_entries_ = new std::vector<Refcount_entry>;
-	      Refcount_entry re = refcounts->add(REFCOUNT_DECREMENT_COMPUTED,
-						 p->type());
-	      this->refcount_entries_->push_back(re);
-	    }
+	  Expression* arg = this->one_arg();
+	  if (arg->field_reference_expression() == NULL)
+	    this->report_error(_("argument must be a field reference"));
 	}
-      return this;
+      break;
+
+    default:
+      gcc_unreachable();
     }
 }
 
-// Build the call expression.
+// Return the tree for a builtin function.
 
 tree
-Call_expression::do_get_tree(Translate_context* context)
+Builtin_call_expression::do_get_tree(Translate_context* context)
 {
-  if (this->tree_ != NULL_TREE)
-    return this->tree_;
-
-  Function_type* fntype = this->get_function_type(false);
-  if (fntype == NULL)
-    return error_mark_node;
-
-  if (this->fn_->is_error_expression())
-    return error_mark_node;
-
   Gogo* gogo = context->gogo();
-  source_location location = this->location();
-
-  Func_expression* func = this->fn_->func_expression();
-  Bound_method_expression* bound_method = this->fn_->bound_method_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_method = bound_method != NULL || interface_method != NULL;
-  gcc_assert(!fntype->is_method() || is_method);
-
-  bool is_varargs = fntype->is_varargs();
-
-  int nargs;
-  tree* args;
-  if (this->args_ == NULL || this->args_->empty())
+  switch (this->code_)
     {
-      nargs = is_method ? 1 : 0;
-      if (is_varargs)
-	++nargs;
-      args = nargs == 0 ? NULL : new tree[nargs];
-      if (is_varargs)
-	{
-	  Type* varargs_type = Function_type::varargs_type();
-	  // The old way, passing array[]interface{}.
-	  // args[nargs - 1] = varargs_type->get_init_tree(gogo, false);
-	  Struct_field_list* fields = new Struct_field_list();
-	  Type* struct_type = Type::make_struct_type(fields, location);
-	  tree val = struct_type->get_init_tree(gogo, false);
-	  args[nargs - 1] = Expression::convert_for_assignment(context,
-							       varargs_type,
-							       struct_type,
-							       val,
-							       location);
-	}
-    }
-  else
-    {
-      const Typed_identifier_list* params = fntype->parameters();
-      gcc_assert(params != NULL);
+    case BUILTIN_INVALID:
+    case BUILTIN_NEW:
+    case BUILTIN_MAKE:
+      gcc_unreachable();
 
-      if (is_varargs)
-	nargs = params->size();
-      else
-	nargs = this->args_->size();
-      int i = is_method ? 1 : 0;
-      nargs += i;
-      args = new tree[nargs];
+    case BUILTIN_LEN:
+    case BUILTIN_CAP:
+      {
+	const Expression_list* args = this->args();
+	gcc_assert(args != NULL && args->size() == 1);
+	Expression* arg = *args->begin();
+	Type* arg_type = arg->type();
+	tree arg_tree = arg->get_tree(context);
+	if (arg_tree == error_mark_node)
+	  return error_mark_node;
 
-      Typed_identifier_list::const_iterator pp = params->begin();
-      Expression_list::const_iterator pe;
-      for (pe = this->args_->begin();
-	   pe != this->args_->end();
-	   ++pe, ++pp, ++i)
-	{
-	  Type* pp_type = pp->type();
-	  if (is_varargs && pp + 1 == params->end())
-	    {
-	      // If the last argument is itself a varargs argument for
-	      // the caller, we pass it without further encapsulation.
-	      if (pe + 1 == this->args_->end()
-		  && (*pe)->var_expression() != NULL
-		  && (*pe)->var_expression()->named_object()->is_variable()
-		  && ((*pe)->var_expression()->named_object()->var_value()->
-		      is_varargs_parameter()))
-		{
-		  pp_type = Function_type::varargs_type();
-		  is_varargs = false;
-		}
-	      else
-		break;
-	    }
-	  tree arg_val = (*pe)->get_tree(context);
-	  args[i] = Expression::convert_for_assignment(context,
-						       pp_type,
-						       (*pe)->type(),
-						       arg_val,
-						       location);
-	  if (args[i] == error_mark_node)
-	    return error_mark_node;
-	}
+	if (arg_type->points_to() != NULL)
+	  {
+	    arg_type = arg_type->points_to();
+	    gcc_assert(arg_type->array_type() != NULL
+		       && !arg_type->is_open_array_type());
+	    gcc_assert(POINTER_TYPE_P(TREE_TYPE(arg_tree)));
+	    arg_tree = build_fold_indirect_ref(arg_tree);
+	  }
 
-      if (is_varargs)
-	{
-	  Type* varargs_type = Function_type::varargs_type();
+	tree val_tree;
+	if (this->code_ == BUILTIN_LEN)
+	  {
+	    if (arg_type->is_string_type())
+	      val_tree = String_type::length_tree(gogo, arg_tree);
+	    else if (arg_type->array_type() != NULL)
+	      val_tree = arg_type->array_type()->length_tree(gogo, arg_tree);
+	    else if (arg_type->map_type() != NULL)
+	      {
+		static tree map_len_fndecl;
+		val_tree = Gogo::call_builtin(&map_len_fndecl,
+					      this->location(),
+					      "__go_map_len",
+					      1,
+					      sizetype,
+					      arg_type->get_tree(gogo),
+					      arg_tree);
+	      }
+	    else if (arg_type->channel_type() != NULL)
+	      {
+		static tree chan_len_fndecl;
+		val_tree = Gogo::call_builtin(&chan_len_fndecl,
+					      this->location(),
+					      "__go_chan_len",
+					      1,
+					      sizetype,
+					      arg_type->get_tree(gogo),
+					      arg_tree);
+	      }
+	    else
+	      gcc_unreachable();
+	  }
+	else
+	  {
+	    if (arg_type->array_type() != NULL)
+	      val_tree = arg_type->array_type()->capacity_tree(gogo, arg_tree);
+	    else if (arg_type->channel_type() != NULL)
+	      {
+		static tree chan_cap_fndecl;
+		val_tree = Gogo::call_builtin(&chan_cap_fndecl,
+					      this->location(),
+					      "__go_chan_cap",
+					      1,
+					      sizetype,
+					      arg_type->get_tree(gogo),
+					      arg_tree);
+	      }
+	    else
+	      gcc_unreachable();
+	  }
 
-#if 0
-	  // The old way, passing array[]interface{}.
-	  Type* element_type = varargs_type->array_type()->element_type();
-	  VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
-						      this->args_->size());
-	  size_t j = 0;
-	  for (; pe != this->args_->end(); ++pe, ++j)
-	    {
-	      constructor_elt* elt = VEC_quick_push(constructor_elt, values,
-						    NULL);
-	      elt->index = size_int(j);
-	      tree expr_tree = (*pe)->get_tree(context);
-	      elt->value = Expression::convert_for_assignment(context,
-							      element_type,
-							      (*pe)->type(),
-							      expr_tree,
-							      location);
-	    }
+	tree type_tree = Type::lookup_integer_type("int")->get_tree(gogo);
+	if (type_tree == TREE_TYPE(val_tree))
+	  return val_tree;
+	else
+	  return fold(convert_to_integer(type_tree, val_tree));
+      }
 
-	  mpz_t val;
-	  mpz_init_set_ui(val, j);
-	  Type* array_type =
-	    Type::make_array_type(element_type,
-				  Expression::make_integer(&val, NULL,
-							   location));
-	  mpz_clear(val);
+    case BUILTIN_PANIC:
+    case BUILTIN_PANICLN:
+    case BUILTIN_PRINT:
+    case BUILTIN_PRINTLN:
+      {
+	const bool is_panic = (this->code_ == BUILTIN_PANIC
+			       || this->code_ == BUILTIN_PANICLN);
+	const bool is_ln = (this->code_ == BUILTIN_PANICLN
+			    || this->code_ == BUILTIN_PRINTLN);
+	const Expression_list* call_args = this->args();
 
-	  tree varargs_val = build_constructor(array_type->get_tree(gogo),
-					       values);
+	int nargs;
+	tree* args;
+	std::string format;
+	if (call_args == NULL)
+	  {
+	    if (!is_panic && !is_ln)
+	      {
+		// A call to print with no arguments.  There is nothing
+		// to do.
+		return integer_zero_node;
+	      }
+	    nargs = 1;
+	    args = new tree[1];
+	  }
+	else
+	  {
+	    nargs = call_args->size() + 1;
 
-	  args[i] = Expression::convert_for_assignment(context, varargs_type,
-						       array_type,
-						       varargs_val,
-						       location);
-#else
-	  // The new way, passing a struct converted to an empty
-	  // interface.
-	  Struct_field_list* fields = new Struct_field_list();
-	  Expression_list::const_iterator pehold = pe;
-	  for (; pe != this->args_->end(); ++pe)
-	    fields->push_back(Struct_field(Typed_identifier("UNNAMED",
-							    (*pe)->type(),
-							    location)));
-	  Type* struct_type = Type::make_struct_type(fields, location);
-	  tree struct_tree = struct_type->get_tree(gogo);
-	  if (struct_tree == error_mark_node)
-	    return error_mark_node;
+	    // We allocate extra space because we use three arguments
+	    // to print arrays.
+	    args = new tree[nargs * 3];
 
-	  VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
-						      this->args_->size());
-	  tree field = TYPE_FIELDS(struct_tree);
-	  for (pe = pehold; pe != this->args_->end(); ++pe)
-	    {
-	      gcc_assert(field != NULL_TREE);
-	      constructor_elt* elt = VEC_quick_push(constructor_elt, values,
-						    NULL);
-	      elt->index = field;
-	      elt->value = (*pe)->get_tree(context);
-	      field = TREE_CHAIN(field);
-	    }
-	  gcc_assert(field == NULL_TREE);
+	    int i = 1;
+	    for (Expression_list::const_iterator p = call_args->begin();
+		 p != call_args->end();
+		 ++p, ++i)
+	      {
+		if (is_ln && i > 1)
+		  format += " ";
 
-	  tree varargs_val = build_constructor(struct_tree, values);
+		args[i] = (*p)->get_tree(context);
 
-	  args[i] = Expression::convert_for_assignment(context, varargs_type,
-						       struct_type,
-						       varargs_val,
-						       location);
-#endif
+		if (args[i] == error_mark_node)
+		  {
+		    args[i] = integer_zero_node;
+		    format += "%d";
+		  }
+		else if ((*p)->type()->is_string_type())
+		  {
+		    // We use a precision to print the right number of
+		    // characters.  FIXME: If the string has embedded
+		    // null bytes, it won't be printed correctly.
+		    tree string = args[i];
+		    tree len = String_type::length_tree(gogo, string);
+		    args[i] = convert_to_integer(integer_type_node, len);
+		    ++i;
+		    ++nargs;
+		    args[i] = String_type::bytes_tree(gogo, string);
+		    format += "%.*s";
+		  }
+		else if ((*p)->type()->integer_type() != NULL)
+		  {
+		    const Integer_type* itype = (*p)->type()->integer_type();
+		    int bits = TYPE_PRECISION(TREE_TYPE(args[i]));
+		    if (bits <= INT_TYPE_SIZE)
+		      {
+			args[i] = fold_convert((itype->is_unsigned()
+						? unsigned_type_node
+						: integer_type_node),
+					       args[i]);
+			format += itype->is_unsigned() ? "%u" : "%d";
+		      }
+		    else if (bits <= LONG_TYPE_SIZE)
+		      {
+			args[i] = fold_convert((itype->is_unsigned()
+						? long_unsigned_type_node
+						: long_integer_type_node),
+					       args[i]);
+			format += itype->is_unsigned() ? "%lu" : "%ld";
+		      }
+		    else if (bits <= LONG_LONG_TYPE_SIZE)
+		      {
+			args[i] = fold_convert((itype->is_unsigned()
+						? long_long_unsigned_type_node
+						: long_long_integer_type_node),
+					       args[i]);
+			format += itype->is_unsigned() ? "%llu" : "%lld";
+		      }
+		    else
+		      gcc_unreachable();
+		  }
+		else if ((*p)->type()->float_type() != NULL)
+		  {
+		    args[i] = fold_convert(double_type_node, args[i]);
+		    format += "%.24g";
+		  }
+		else if ((*p)->type()->is_boolean_type())
+		  {
+		    tree string_type = Gogo::const_char_pointer_type_tree();
+		    tree true_string = Gogo::string_constant_tree("true");
+		    true_string = build_fold_addr_expr(true_string);
+		    true_string = fold_convert(string_type, true_string);
+		    tree false_string = Gogo::string_constant_tree("false");
+		    false_string = build_fold_addr_expr(false_string);
+		    false_string = fold_convert(string_type, false_string);
+		    args[i] = fold_build3(COND_EXPR, string_type, args[i],
+					  true_string, false_string);
+		    format += "%s";
+		  }
+		else if ((*p)->type()->points_to() != NULL
+			 || (*p)->type()->interface_type() != NULL)
+		  {
+		    args[i] = fold_convert(ptr_type_node, args[i]);
+		    format += "%p";
+		  }
+		else if ((*p)->type()->array_type() != NULL)
+		  {
+		    Array_type* at = (*p)->type()->array_type();
+		    tree v = save_expr(args[i]);
+		    args[i] = at->length_tree(gogo, v);
+		    ++i;
+		    ++nargs;
+		    args[i] = at->capacity_tree(gogo, v);
+		    ++i;
+		    ++nargs;
+		    args[i] = at->value_pointer_tree(gogo, v);
+		    format += "[%zu/%zu]%p";
+		  }
+		else
+		  {
+		    args[i] = integer_zero_node;
+		    format += "%d";
+		  }
+	      }
+	    gcc_assert(i == nargs);
+	  }
 
-	  ++i;
-	  ++pp;
-	}
+	if (is_ln)
+	  format += "\n";
 
-      gcc_assert(pp == params->end());
-      gcc_assert(i == nargs);
-    }
+	tree string_val = Gogo::string_constant_tree(format);
+	args[0] = build_fold_addr_expr(string_val);
 
-  tree rettype = TREE_TYPE(TREE_TYPE(fntype->get_tree(gogo)));
-  if (rettype == error_mark_node)
-    return error_mark_node;
+	static tree panic_fndecl;
+	static tree print_fndecl;
+	static tree* pfndecl;
+	if (is_panic)
+	  pfndecl = &panic_fndecl;
+	else
+	  pfndecl = &print_fndecl;
+	if (*pfndecl == NULL_TREE)
+	  {
+	    tree fnid = get_identifier(is_panic ? "__go_panic" : "__go_print");
+	    tree argtypes = tree_cons(NULL_TREE,
+				      Gogo::const_char_pointer_type_tree(),
+				      NULL_TREE);
+	    tree fntype = build_function_type(void_type_node, argtypes);
+
+	    *pfndecl = build_decl(BUILTINS_LOCATION, FUNCTION_DECL, fnid,
+				  fntype);
+	    Gogo::mark_fndecl_as_builtin_library(*pfndecl);
+	    if (is_panic)
+	      {
+		// Mark the function as noreturn.
+		TREE_THIS_VOLATILE(*pfndecl) = 1;
+	      }
+	    go_preserve_from_gc(*pfndecl);
+	  }
+
+	tree fnptr = build_fold_addr_expr(*pfndecl);
+	tree call = build_call_array(void_type_node, fnptr, nargs, args);
+	delete[] args;
+
+	SET_EXPR_LOCATION(call, this->location());
+
+	return call;
+      }
+
+    case BUILTIN_CLOSE:
+    case BUILTIN_CLOSED:
+      {
+	const Expression_list* args = this->args();
+	gcc_assert(args != NULL && args->size() == 1);
+	Expression* arg = args->front();
+	tree arg_tree = arg->get_tree(context);
+	if (arg_tree == error_mark_node)
+	  return error_mark_node;
+	if (this->code_ == BUILTIN_CLOSE)
+	  {
+	    static tree close_fndecl;
+	    return Gogo::call_builtin(&close_fndecl,
+				      this->location(),
+				      "__go_builtin_close",
+				      1,
+				      void_type_node,
+				      TREE_TYPE(arg_tree),
+				      arg_tree);
+	  }
+	else
+	  {
+	    static tree closed_fndecl;
+	    return Gogo::call_builtin(&closed_fndecl,
+				      this->location(),
+				      "__go_builtin_closed",
+				      1,
+				      boolean_type_node,
+				      TREE_TYPE(arg_tree),
+				      arg_tree);
+	  }
+      }
+
+    case BUILTIN_SIZEOF:
+    case BUILTIN_OFFSETOF:
+    case BUILTIN_ALIGNOF:
+      {
+	mpz_t val;
+	mpz_init(val);
+	Type* dummy;
+	bool b = this->integer_constant_value(true, val, &dummy);
+	gcc_assert(b);
+	tree type = Type::lookup_integer_type("int")->get_tree(gogo);
+	tree ret = Expression::integer_constant_tree(val, type);
+	mpz_clear(val);
+	return ret;
+      }
+
+    default:
+      gcc_unreachable();
+    }
+}
 
-  tree fn;
-  if (has_closure)
-    fn = func->get_tree_without_closure(gogo);
-  else if (!is_method)
-    fn = this->fn_->get_tree(context);
-  else if (bound_method != NULL)
-    fn = this->bound_method_function(context, bound_method, &args[0]);
-  else if (interface_method != NULL)
-    fn = this->interface_method_function(context, interface_method, &args[0]);
-  else
-    gcc_unreachable();
+// We have to support exporting a builtin call expression, because
+// code can set a constant to the result of a builtin expression.
 
-  if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node)
-    return error_mark_node;
+void
+Builtin_call_expression::do_export(Export* exp) const
+{
+  mpz_t val;
+  mpz_init(val);
+  Type* dummy;
+  bool b = this->integer_constant_value(true, val, &dummy);
+  if (!b)
+    error_at(this->location(), "value is not constant");
+  char* s = mpz_get_str(NULL, 10, val);
+  exp->write_c_string(s);
+  free(s);
+  // A trailing space lets us reliably identify the end of the number.
+  exp->write_c_string(" ");
+  mpz_clear(val);
+}
 
-  tree ret = build_call_array(rettype, fn, nargs, args);
-  delete[] args;
+// Class Call_expression.
 
-  SET_EXPR_LOCATION(ret, location);
+// Traversal.
 
-  if (has_closure)
+int
+Call_expression::do_traverse(Traverse* traverse)
+{
+  if (Expression::traverse(&this->fn_, traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  if (this->args_ != NULL)
     {
-      tree closure_tree = func->closure()->get_tree(context);
-      if (closure_tree != error_mark_node)
-	CALL_EXPR_STATIC_CHAIN(ret) = closure_tree;
+      if (this->args_->traverse(traverse) == TRAVERSE_EXIT)
+	return TRAVERSE_EXIT;
     }
-
-  // If there is more than one result, we will refer to the call
-  // multiple times.
-  if (fntype->results() != NULL && fntype->results()->size() > 1)
-    ret = save_expr(ret);
-
-  if (this->refcount_entries_ != NULL)
-    ret = this->set_refcount_queue_entries(context, ret);
-
-  this->tree_ = ret;
-
-  return ret;
+  return TRAVERSE_CONTINUE;
 }
 
-// Set entries in the decrement queue as needed.
+// Lower a call statement.  This is where we handle an argument which
+// is a call to a function which returns multiple results.  We also
+// may discover that we thought was a call is actually a type cast.
 
-tree
-Call_expression::set_refcount_queue_entries(Translate_context* context,
-					    tree ret)
+Expression*
+Call_expression::do_lower(Gogo* gogo, int)
 {
-  Gogo* gogo = context->gogo();
-  Refcounts* refcounts = context->function()->func_value()->refcounts();
-  tree val = ret;
-  Function_type* fntype = this->get_function_type(false);
-  const Typed_identifier_list* results = fntype->results();
-  gcc_assert(TREE_CODE(TREE_TYPE(val)) == RECORD_TYPE);
-  std::vector<Refcount_entry>::const_iterator pre =
-    this->refcount_entries_->begin();
-  tree field = TYPE_FIELDS(TREE_TYPE(val));
-  for (Typed_identifier_list::const_iterator pr = results->begin();
-       pr != results->end();
-       ++pr, field = TREE_CHAIN(field))
+  if (this->fn_->is_type_expression()
+      && this->args_ != NULL
+      && this->args_->size() == 1)
+    return Expression::make_cast(this->fn_->type(), this->args_->front(),
+				 this->location());
+
+  Func_expression* fne = this->fn_->func_expression();
+  if (fne != NULL
+      && fne->named_object()->is_function_declaration()
+      && fne->named_object()->func_declaration_value()->type()->is_builtin())
+    return new Builtin_call_expression(gogo, this->fn_, this->args_,
+				       this->location());
+
+  if (this->args_ != NULL
+      && this->args_->size() == 1
+      && this->args_->front()->call_expression() != NULL
+      && this->fn_->type()->function_type() != NULL)
     {
-      gcc_assert(field != NULL_TREE);
-      if (pr->type()->has_refcounted_component())
+      Function_type* fntype = this->fn_->type()->function_type();
+      size_t rc = this->args_->front()->call_expression()->result_count();
+      if (rc > 1
+	  && fntype->parameters() != NULL
+	  && (fntype->parameters()->size() == rc
+	      || (fntype->is_varargs()
+		  && fntype->parameters()->size() - 1 <= rc)))
 	{
-	  gcc_assert(pre != this->refcount_entries_->end());
-	  Refcount_entry re = *pre;
-	  tree f = fold_build3(COMPONENT_REF, TREE_TYPE(field), val, field,
-			       NULL_TREE);
-	  pr->type()->set_refcount_queue_entry(gogo, refcounts, &re, f);
-	  ++pre;
+	  Call_expression* call = this->args_->front()->call_expression();
+	  Expression_list* args = new Expression_list;
+	  for (size_t i = 0; i < rc; ++i)
+	    args->push_back(Expression::make_call_result(call, i));
+	  // We can't return a new call expression here, because this
+	  // one may be referenced by Call_result expressions.  FIXME.
+	  delete this->args_;
+	  this->args_ = args;
 	}
     }
-  gcc_assert(pre == this->refcount_entries_->end());
-  gcc_assert(field == NULL_TREE);
-  return ret;
+
+  return this;
 }
 
-// Make a call expression.
+// Get the function type.  Returns NULL if we don't know the type.  If
+// this returns NULL, and if_ERROR is true, issues an error.
 
-Call_expression*
-Expression::make_call(Expression* fn, Expression_list* args,
-		      source_location location)
+Function_type*
+Call_expression::get_function_type(bool issue_error)
 {
-  return new Call_expression(fn, args, location);
+  Type* type = this->fn_->type()->deref();
+  Function_type* fntype = type->function_type();
+  if (fntype != NULL)
+    return fntype;
+  if (issue_error && !type->is_error_type())
+    this->report_error("expected function");
+  return NULL;
 }
 
-// A single result from a call which returns multiple results.
+// Return the number of values which this call will return.
 
-class Call_result_expression : public Expression
+size_t
+Call_expression::result_count()
 {
- public:
-  Call_result_expression(Call_expression* call, unsigned int index)
-    : Expression(EXPRESSION_CALL_RESULT, call->location()),
-      call_(call), index_(index), is_being_copied_(false)
-  { }
-
- protected:
-  int
-  do_traverse(Traverse*);
-
-  Type*
-  do_type();
-
-  void
-  do_determine_type(const Type_context*);
-
-  Expression*
-  do_copy()
-  {
-    return new Call_result_expression(this->call_->call_expression(),
-				      this->index_);
-  }
+  Function_type* fntype = this->get_function_type(false);
+  if (fntype == NULL)
+    return 0;
+  if (fntype->results() == NULL)
+    return 0;
+  return fntype->results()->size();
+}
 
-  Expression*
-  do_being_copied(Refcounts*, bool);
+// Get the type.
 
-  Expression*
-  do_note_decrements(Refcounts*);
+Type*
+Call_expression::do_type()
+{
+  if (this->type_ != NULL)
+    return this->type_;
 
-  tree
-  do_get_tree(Translate_context*);
+  Type* ret;
+  Function_type* fntype = this->get_function_type(false);
+  if (fntype == NULL)
+    return Type::make_error_type();
 
- private:
-  // The underlying call expression.
-  Expression* call_;
-  // Which result we want.
-  unsigned int index_;
-  // Whether the result is being copied.
-  bool is_being_copied_;
-};
+  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);
 
-// Traverse a call result.  We only traverse the call expression for
-// index 0, to avoid traversing it multiple times.
+  this->type_ = ret;
 
-int
-Call_result_expression::do_traverse(Traverse* traverse)
-{
-  if (this->index_ > 0)
-    return TRAVERSE_CONTINUE;
-  return Expression::traverse(&this->call_, traverse);
+  return this->type_;
 }
 
-// Get the type.
+// Determine types for a call expression.  We can use the function
+// parameter types to set the types of the arguments.
 
-Type*
-Call_result_expression::do_type()
+void
+Call_expression::do_determine_type(const Type_context*)
 {
-  if (this->call_->is_error_expression())
-    return Type::make_error_type();
-  Function_type* fntype =
-    this->call_->call_expression()->get_function_type(false);
-  if (fntype == NULL)
-    return Type::make_error_type();
-  const Typed_identifier_list* results = fntype->results();
-  Typed_identifier_list::const_iterator pr = results->begin();
-  for (unsigned int i = 0; i < this->index_; ++i)
+  this->fn_->determine_type_no_context();
+  Function_type* fntype = this->get_function_type(false);
+  const Typed_identifier_list* parameters = NULL;
+  if (fntype != NULL)
+    parameters = fntype->parameters();
+  if (this->args_ != NULL)
     {
-      gcc_assert(pr != results->end());
-      ++pr;
+      Typed_identifier_list::const_iterator pt;
+      if (parameters != NULL)
+	pt = parameters->begin();
+      for (Expression_list::const_iterator pa = this->args_->begin();
+	   pa != this->args_->end();
+	   ++pa)
+	{
+	  if (parameters != NULL
+	      && pt != parameters->end()
+	      && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+	    {
+	      Type_context subcontext(pt->type(), false);
+	      (*pa)->determine_type(&subcontext);
+	      ++pt;
+	    }
+	  else
+	    (*pa)->determine_type_no_context();
+	}
     }
-  gcc_assert(pr != results->end());
-  return pr->type();
 }
 
-// Determine the type.  We have nothing to do here, but the 0 result
-// needs to pass down to the caller.
+// Check types for parameter I.
 
 void
-Call_result_expression::do_determine_type(const Type_context*)
+Call_expression::check_argument_type(int i, const Type* parameter_type,
+				     const Type* argument_type,
+				     source_location argument_location)
 {
-  if (this->index_ == 0)
-    this->call_->determine_type_no_context();
+  std::string reason;
+  if (!Type::are_compatible_for_assign(parameter_type, argument_type, &reason))
+    {
+      if (reason.empty())
+	error_at(argument_location, "argument %d has incompatible type", i);
+      else
+	error_at(argument_location, "argument %d has incompatible type (%s)",
+		 i, reason.c_str());
+      this->set_is_error();
+    }
 }
 
-// The result will come back with a reference, so we don't need to do
-// anything to copy it.
+// Check types.
 
-Expression*
-Call_result_expression::do_being_copied(Refcounts*, bool)
+void
+Call_expression::do_check_types(Gogo*)
 {
-  this->is_being_copied_ = true;
-  return this;
-}
+  Function_type* fntype = this->get_function_type(true);
+  if (fntype == NULL)
+    return;
 
-// Decrement the reference count if necessary.
+  if (fntype->is_method())
+    {
+      // We don't support pointers to methods, so the function has to
+      // be a bound method expression.
+      if (this->fn_->bound_method_expression() == NULL)
+	this->report_error(_("method call without object"));
+    }
 
-Expression*
-Call_result_expression::do_note_decrements(Refcounts* refcounts)
-{
-  if (this->is_being_copied_ || !this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_adjust(refcounts,
-					  REFCOUNT_DECREMENT_COMPUTED,
-					  this, false);
+  const Typed_identifier_list* parameters = fntype->parameters();
+  if (this->args_ == NULL)
+    {
+      // A single varargs parameter can accept zero arguments.
+      if (parameters != NULL
+	  && (!fntype->is_varargs() || parameters->size() > 1))
+	this->report_error(_("not enough arguments"));
+    }
+  else if (parameters == NULL)
+    this->report_error(_("too many arguments"));
+  else
+    {
+      int i = 0;
+      Typed_identifier_list::const_iterator pt = parameters->begin();
+      for (Expression_list::const_iterator pa = this->args_->begin();
+	   pa != this->args_->end();
+	   ++pa, ++pt, ++i)
+	{
+	  if (fntype->is_varargs() && pt + 1 == parameters->end())
+	    {
+	      // A varargs parameter can accept any number of
+	      // arguments, but you can't pass nil.
+	      for (; pa != this->args_->end(); ++pa)
+		{
+		  if ((*pa)->type()->is_nil_type())
+		    {
+		      this->report_error(_("invalid use of %<nil%> for "
+					   "%<...%> argument"));
+		      return;
+		    }
+		}
+	      break;
+	    }
+	  if (pt == parameters->end())
+	    {
+	      this->report_error(_("too many arguments"));
+	      return;
+	    }
+	  this->check_argument_type(i + 1, pt->type(), (*pa)->type(),
+				    (*pa)->location());
+	}
+      // The varargs parameter can accept zero arguments.
+      if (pt != parameters->end()
+	  && (!fntype->is_varargs() || pt + 1 != parameters->end()))
+	this->report_error(_("not enough arguments"));
+    }
 }
 
-// Return the tree.
+// Get the function and the first argument to use when calling a bound
+// method.
 
 tree
-Call_result_expression::do_get_tree(Translate_context* context)
+Call_expression::bound_method_function(Translate_context* context,
+				       Bound_method_expression* bound_method,
+				       tree* first_arg_ptr)
 {
-  tree call_tree = this->call_->get_tree(context);
-  if (call_tree == error_mark_node)
+  Expression* first_argument = bound_method->first_argument();
+  tree first_arg = first_argument->get_tree(context);
+  if (first_arg == error_mark_node)
     return error_mark_node;
-  gcc_assert(TREE_CODE(TREE_TYPE(call_tree)) == RECORD_TYPE);
-  tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
-  for (unsigned int i = 0; i < this->index_; ++i)
+
+  // We always pass a pointer to the first argument when calling a
+  // method.
+  if (first_argument->type()->points_to() == NULL)
     {
-      gcc_assert(field != NULL_TREE);
-      field = TREE_CHAIN(field);
+      tree pointer_to_arg_type = build_pointer_type(TREE_TYPE(first_arg));
+      if (TREE_ADDRESSABLE(TREE_TYPE(first_arg))
+	  || DECL_P(first_arg)
+	  || TREE_CODE(first_arg) == INDIRECT_REF
+	  || TREE_CODE(first_arg) == COMPONENT_REF)
+	{
+	  first_arg = build_fold_addr_expr(first_arg);
+	  if (DECL_P(first_arg))
+	    TREE_ADDRESSABLE(first_arg) = 1;
+	}
+      else
+	{
+	  tree tmp = create_tmp_var(TREE_TYPE(first_arg),
+				    get_name(first_arg));
+	  DECL_IGNORED_P(tmp) = 0;
+	  DECL_INITIAL(tmp) = first_arg;
+	  first_arg = build2(COMPOUND_EXPR, pointer_to_arg_type,
+			     build1(DECL_EXPR, void_type_node, tmp),
+			     build_fold_addr_expr(tmp));
+	  TREE_ADDRESSABLE(tmp) = 1;
+	}
+      if (first_arg == error_mark_node)
+	return error_mark_node;
     }
-  gcc_assert(field != NULL_TREE);
-  return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+
+  Type* fatype = bound_method->first_argument_type();
+  if (fatype != NULL)
+    {
+      if (fatype->points_to() == NULL)
+	fatype = Type::make_pointer_type(fatype);
+      first_arg = fold_convert(fatype->get_tree(context->gogo()), first_arg);
+      if (first_arg == error_mark_node
+	  || TREE_TYPE(first_arg) == error_mark_node)
+	return error_mark_node;
+    }
+
+  *first_arg_ptr = first_arg;
+
+  return bound_method->method()->get_tree(context);
 }
 
-// Make a reference to a single result of a call which returns
-// multiple results.
+// Get the function and the first argument to use when calling an
+// interface method.
 
-Expression*
-Expression::make_call_result(Call_expression* call, unsigned int index)
+tree
+Call_expression::interface_method_function(
+    Translate_context* context,
+    Interface_field_reference_expression* interface_method,
+    tree* first_arg_ptr)
 {
-  return new Call_result_expression(call, index);
+  tree expr = interface_method->expr()->get_tree(context);
+  if (expr == error_mark_node)
+    return error_mark_node;
+  expr = save_expr(expr);
+  tree first_arg = interface_method->get_underlying_object_tree(context, expr);
+  if (first_arg == error_mark_node)
+    return error_mark_node;
+  *first_arg_ptr = first_arg;
+  return interface_method->get_function_tree(context, expr);
 }
 
-// Class Index_expression.
-
-// Traversal.
+// The call expression is being copied.  There is nothing to do here
+// except to disable any decrements--the function should return with
+// an appropriate reference count.
 
-int
-Index_expression::do_traverse(Traverse* traverse)
+Expression*
+Call_expression::do_being_copied(Refcounts*, bool)
 {
-  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))
-    return TRAVERSE_EXIT;
-  return TRAVERSE_CONTINUE;
+  this->is_being_copied_ = true;
+  return this;
 }
 
-// Lower an index expression.  This converts the generic index
-// expression into an array index, a string index, or a map index.
+// Where needed, decrement the reference counts of any values returned
+// by this call.
 
 Expression*
-Index_expression::do_lower(Gogo*, int)
+Call_expression::do_note_decrements(Refcounts* refcounts)
 {
-  source_location location = this->location();
-  Expression* left = this->left_;
-  Expression* start = this->start_;
-  Expression* end = this->end_;
-
-  Type* type = left->type();
-  if (type->is_error_type())
-    return Expression::make_error(location);
-  else if (type->array_type() != NULL
-	   || (type->deref()->array_type() != NULL
-	       && !type->deref()->array_type()->is_open_array_type()))
-    return Expression::make_array_index(left, start, end, location);
-  else if (type->is_string_type())
-    return Expression::make_string_index(left, start, end, location);
-  else if (type->map_type() != NULL)
+  if (this->is_being_copied_)
+    return this;
+  Function_type* fntype = this->get_function_type(false);
+  if (fntype == NULL)
+    return this;
+  const Typed_identifier_list* results = fntype->results();
+  if (results == NULL)
+    return this;
+  if (results->size() == 1)
     {
-      if (end != NULL)
+      if (!results->begin()->type()->has_refcounted_component())
+	return this;
+      return Expression::make_refcount_adjust(refcounts,
+					      REFCOUNT_DECREMENT_COMPUTED,
+					      this, false);
+    }
+  else if (!this->is_value_discarded_)
+    {
+      // If the value is not discarded, each result will be handled
+      // separately via Call_expression_result.
+      return this;
+    }
+  else
+    {
+      for (Typed_identifier_list::const_iterator p = results->begin();
+	   p != results->end();
+	   p++)
 	{
-	  error_at(location, "invalid slice of map");
-	  return Expression::make_error(location);
+	  if (p->type()->has_refcounted_component())
+	    {
+	      if (this->refcount_entries_ == NULL)
+		this->refcount_entries_ = new std::vector<Refcount_entry>;
+	      Refcount_entry re = refcounts->add(REFCOUNT_DECREMENT_COMPUTED,
+						 p->type());
+	      this->refcount_entries_->push_back(re);
+	    }
 	}
-      Map_index_expression* ret= Expression::make_map_index(left, start,
-							    location);
-      if (this->is_lvalue_)
-	ret->set_is_lvalue();
-      return ret;
-    }
-  else
-    {
-      error_at(location,
-	       "attempt to index object which is not array, string, or map");
-      return Expression::make_error(location);
+      return this;
     }
 }
 
-// Make an index expression.
-
-Expression*
-Expression::make_index(Expression* left, Expression* start, Expression* end,
-		       source_location location)
-{
-  return new Index_expression(left, start, end, location);
-}
-
-// An array index.  This is used for both indexing and slicing.
+// Build the call expression.
 
-class Array_index_expression : public Expression
+tree
+Call_expression::do_get_tree(Translate_context* context)
 {
- public:
-  Array_index_expression(Expression* array, Expression* start,
-			 Expression* end, source_location location)
-    : Expression(EXPRESSION_ARRAY_INDEX, location),
-      array_(array), start_(start), end_(end), type_(NULL)
-  { }
-
- protected:
-  int
-  do_traverse(Traverse*);
-
-  Type*
-  do_type();
+  if (this->tree_ != NULL_TREE)
+    return this->tree_;
 
-  void
-  do_determine_type(const Type_context*);
+  Function_type* fntype = this->get_function_type(false);
+  if (fntype == NULL)
+    return error_mark_node;
 
-  void
-  do_check_types(Gogo*);
+  if (this->fn_->is_error_expression())
+    return error_mark_node;
 
-  Expression*
-  do_copy()
-  {
-    return Expression::make_array_index(this->array_->copy(),
-					this->start_->copy(),
-					(this->end_ == NULL
-					 ? NULL
-					 : this->end_->copy()),
-					this->location());
-  }
+  Gogo* gogo = context->gogo();
+  source_location location = this->location();
 
-  bool
-  do_is_lvalue() const
-  { return this->end_ == NULL; }
+  Func_expression* func = this->fn_->func_expression();
+  Bound_method_expression* bound_method = this->fn_->bound_method_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_method = bound_method != NULL || interface_method != NULL;
+  gcc_assert(!fntype->is_method() || is_method);
 
-  bool
-  do_address_taken(source_location);
+  bool is_varargs = fntype->is_varargs();
 
-  Expression*
-  do_being_copied(Refcounts*, bool);
+  int nargs;
+  tree* args;
+  if (this->args_ == NULL || this->args_->empty())
+    {
+      nargs = is_method ? 1 : 0;
+      if (is_varargs)
+	++nargs;
+      args = nargs == 0 ? NULL : new tree[nargs];
+      if (is_varargs)
+	{
+	  Type* varargs_type = Function_type::varargs_type();
+	  // The old way, passing array[]interface{}.
+	  // args[nargs - 1] = varargs_type->get_init_tree(gogo, false);
+	  Struct_field_list* fields = new Struct_field_list();
+	  Type* struct_type = Type::make_struct_type(fields, location);
+	  tree val = struct_type->get_init_tree(gogo, false);
+	  args[nargs - 1] = Expression::convert_for_assignment(context,
+							       varargs_type,
+							       struct_type,
+							       val,
+							       location);
+	}
+    }
+  else
+    {
+      const Typed_identifier_list* params = fntype->parameters();
+      gcc_assert(params != NULL);
 
-  Expression*
-  do_note_decrements(Refcounts*);
+      if (is_varargs)
+	nargs = params->size();
+      else
+	nargs = this->args_->size();
+      int i = is_method ? 1 : 0;
+      nargs += i;
+      args = new tree[nargs];
 
-  Expression*
-  do_being_set(Refcounts*);
+      Typed_identifier_list::const_iterator pp = params->begin();
+      Expression_list::const_iterator pe;
+      for (pe = this->args_->begin();
+	   pe != this->args_->end();
+	   ++pe, ++pp, ++i)
+	{
+	  Type* pp_type = pp->type();
+	  if (is_varargs && pp + 1 == params->end())
+	    {
+	      // If the last argument is itself a varargs argument for
+	      // the caller, we pass it without further encapsulation.
+	      if (pe + 1 == this->args_->end()
+		  && (*pe)->var_expression() != NULL
+		  && (*pe)->var_expression()->named_object()->is_variable()
+		  && ((*pe)->var_expression()->named_object()->var_value()->
+		      is_varargs_parameter()))
+		{
+		  pp_type = Function_type::varargs_type();
+		  is_varargs = false;
+		}
+	      else
+		break;
+	    }
+	  tree arg_val = (*pe)->get_tree(context);
+	  args[i] = Expression::convert_for_assignment(context,
+						       pp_type,
+						       (*pe)->type(),
+						       arg_val,
+						       location);
+	  if (args[i] == error_mark_node)
+	    return error_mark_node;
+	}
 
-  tree
-  do_get_tree(Translate_context*);
+      if (is_varargs)
+	{
+	  Type* varargs_type = Function_type::varargs_type();
 
- private:
-  // The array we are getting a value from.
-  Expression* array_;
-  // The start or only index.
-  Expression* start_;
-  // The end index of a slice.  This may be NULL.
-  Expression* end_;
-  // The type of the expression.
-  Type* type_;
-};
+#if 0
+	  // The old way, passing array[]interface{}.
+	  Type* element_type = varargs_type->array_type()->element_type();
+	  VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
+						      this->args_->size());
+	  size_t j = 0;
+	  for (; pe != this->args_->end(); ++pe, ++j)
+	    {
+	      constructor_elt* elt = VEC_quick_push(constructor_elt, values,
+						    NULL);
+	      elt->index = size_int(j);
+	      tree expr_tree = (*pe)->get_tree(context);
+	      elt->value = Expression::convert_for_assignment(context,
+							      element_type,
+							      (*pe)->type(),
+							      expr_tree,
+							      location);
+	    }
 
-// Array index traversal.
+	  mpz_t val;
+	  mpz_init_set_ui(val, j);
+	  Type* array_type =
+	    Type::make_array_type(element_type,
+				  Expression::make_integer(&val, NULL,
+							   location));
+	  mpz_clear(val);
 
-int
-Array_index_expression::do_traverse(Traverse* traverse)
-{
-  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;
-    }
-  return TRAVERSE_CONTINUE;
-}
+	  tree varargs_val = build_constructor(array_type->get_tree(gogo),
+					       values);
 
-// Return the type of an array index.
+	  args[i] = Expression::convert_for_assignment(context, varargs_type,
+						       array_type,
+						       varargs_val,
+						       location);
+#else
+	  // The new way, passing a struct converted to an empty
+	  // interface.
+	  Struct_field_list* fields = new Struct_field_list();
+	  Expression_list::const_iterator pehold = pe;
+	  for (; pe != this->args_->end(); ++pe)
+	    fields->push_back(Struct_field(Typed_identifier("UNNAMED",
+							    (*pe)->type(),
+							    location)));
+	  Type* struct_type = Type::make_struct_type(fields, location);
+	  tree struct_tree = struct_type->get_tree(gogo);
+	  if (struct_tree == error_mark_node)
+	    return error_mark_node;
 
-Type*
-Array_index_expression::do_type()
-{
-  if (this->type_ == NULL)
-    {
-      Array_type* type = this->array_->type()->deref()->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_open_array_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);
+	  VEC(constructor_elt,gc)* values = VEC_alloc(constructor_elt, gc,
+						      this->args_->size());
+	  tree field = TYPE_FIELDS(struct_tree);
+	  for (pe = pehold; pe != this->args_->end(); ++pe)
+	    {
+	      gcc_assert(field != NULL_TREE);
+	      constructor_elt* elt = VEC_quick_push(constructor_elt, values,
+						    NULL);
+	      elt->index = field;
+	      elt->value = (*pe)->get_tree(context);
+	      field = TREE_CHAIN(field);
+	    }
+	  gcc_assert(field == NULL_TREE);
+
+	  tree varargs_val = build_constructor(struct_tree, values);
+
+	  args[i] = Expression::convert_for_assignment(context, varargs_type,
+						       struct_type,
+						       varargs_val,
+						       location);
+#endif
+
+	  ++i;
+	  ++pp;
 	}
+
+      gcc_assert(pp == params->end());
+      gcc_assert(i == nargs);
     }
-  return this->type_;
-}
 
-// Set the type of an array index.
+  tree rettype = TREE_TYPE(TREE_TYPE(fntype->get_tree(gogo)));
+  if (rettype == error_mark_node)
+    return error_mark_node;
 
-void
-Array_index_expression::do_determine_type(const Type_context*)
-{
-  this->array_->determine_type_no_context();
-  Type_context subcontext(NULL, true);
-  this->start_->determine_type(&subcontext);
-  if (this->end_ != NULL)
-    this->end_->determine_type(&subcontext);
-}
+  tree fn;
+  if (has_closure)
+    fn = func->get_tree_without_closure(gogo);
+  else if (!is_method)
+    fn = this->fn_->get_tree(context);
+  else if (bound_method != NULL)
+    fn = this->bound_method_function(context, bound_method, &args[0]);
+  else if (interface_method != NULL)
+    fn = this->interface_method_function(context, interface_method, &args[0]);
+  else
+    gcc_unreachable();
 
-// Check types of an array index.
+  if (fn == error_mark_node || TREE_TYPE(fn) == error_mark_node)
+    return error_mark_node;
 
-void
-Array_index_expression::do_check_types(Gogo*)
-{
-  if (this->start_->type()->integer_type() == NULL)
-    this->report_error(_("index must be integer"));
-  if (this->end_ != NULL
-      && this->end_->type()->integer_type() == NULL)
-    this->report_error(_("slice end must be integer"));
+  tree ret = build_call_array(rettype, fn, nargs, args);
+  delete[] args;
 
-  Array_type* array_type = this->array_->type()->deref()->array_type();
-  gcc_assert(array_type != NULL);
+  SET_EXPR_LOCATION(ret, location);
 
-  Type* dummy;
-  mpz_t lval;
-  mpz_init(lval);
-  bool lval_valid = (array_type->length() != NULL
-		     && array_type->length()->integer_constant_value(true,
-								     lval,
-								     &dummy));
-  mpz_t ival;
-  mpz_init(ival);
-  if (this->start_->integer_constant_value(true, ival, &dummy))
-    {
-      if (mpz_sgn(ival) < 0
-	  || (lval_valid
-	      && (this->end_ == NULL
-		  ? mpz_cmp(ival, lval) >= 0
-		  : mpz_cmp(ival, lval) > 0)))
-	{
-	  error_at(this->start_->location(), "array index out of bounds");
-	  this->set_is_error();
-	}
-    }
-  if (this->end_ != NULL)
+  if (has_closure)
     {
-      if (this->end_->integer_constant_value(true, ival, &dummy))
-	{
-	  if (mpz_sgn(ival) < 0 || (lval_valid && mpz_cmp(ival, lval) > 0))
-	    {
-	      error_at(this->end_->location(), "array index out of bounds");
-	      this->set_is_error();
-	    }
-	}
+      tree closure_tree = func->closure()->get_tree(context);
+      if (closure_tree != error_mark_node)
+	CALL_EXPR_STATIC_CHAIN(ret) = closure_tree;
     }
-  mpz_clear(ival);
-  mpz_clear(lval);
-}
 
-// We can take the address of an array index.  We don't support taking
-// the address of a slice--I'm not sure what the type of that would
-// be.  Taking the address of an array index implies taking the
-// address of the array.
+  // If there is more than one result, we will refer to the call
+  // multiple times.
+  if (fntype->results() != NULL && fntype->results()->size() > 1)
+    ret = save_expr(ret);
 
-bool
-Array_index_expression::do_address_taken(source_location location)
-{
-  if (!this->array_->address_taken(location))
-    return false;
-  if (this->end_ != NULL)
-    {
-      error_at(location, "may not take address of array slice");
-      return false;
-    }
-  return true;
-}
+  if (this->refcount_entries_ != NULL)
+    ret = this->set_refcount_queue_entries(context, ret);
 
-// Copying an element of an array may require incrementing a reference
-// count.  Copying a slice requires incrementing the reference count
-// of the underlying array.
+  this->tree_ = ret;
 
-Expression*
-Array_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
-{
-  if (this->end_ == NULL && !this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_adjust(refcounts,
-					  REFCOUNT_INCREMENT_COPIED,
-					  this, for_local);
+  return ret;
 }
 
-// Referring to an element of an array does not require changing any
-// reference counts.  If the array is changed before the value is
-// used, the program is ill-defined.  We do not introduce a reference
-// count for a slice, so there is nothing to decrement.  FIXME: Is
-// that safe?
+// Set entries in the decrement queue as needed.
 
-Expression*
-Array_index_expression::do_note_decrements(Refcounts*)
+tree
+Call_expression::set_refcount_queue_entries(Translate_context* context,
+					    tree ret)
 {
-  return this;
+  Gogo* gogo = context->gogo();
+  Refcounts* refcounts = context->function()->func_value()->refcounts();
+  tree val = ret;
+  Function_type* fntype = this->get_function_type(false);
+  const Typed_identifier_list* results = fntype->results();
+  gcc_assert(TREE_CODE(TREE_TYPE(val)) == RECORD_TYPE);
+  std::vector<Refcount_entry>::const_iterator pre =
+    this->refcount_entries_->begin();
+  tree field = TYPE_FIELDS(TREE_TYPE(val));
+  for (Typed_identifier_list::const_iterator pr = results->begin();
+       pr != results->end();
+       ++pr, field = TREE_CHAIN(field))
+    {
+      gcc_assert(field != NULL_TREE);
+      if (pr->type()->has_refcounted_component())
+	{
+	  gcc_assert(pre != this->refcount_entries_->end());
+	  Refcount_entry re = *pre;
+	  tree f = fold_build3(COMPONENT_REF, TREE_TYPE(field), val, field,
+			       NULL_TREE);
+	  pr->type()->set_refcount_queue_entry(gogo, refcounts, &re, f);
+	  ++pre;
+	}
+    }
+  gcc_assert(pre == this->refcount_entries_->end());
+  gcc_assert(field == NULL_TREE);
+  return ret;
 }
 
-// Assigning to an element of an array may require decrementing a
-// reference count of the old value.
+// Make a call expression.
 
-Expression*
-Array_index_expression::do_being_set(Refcounts* refcounts)
+Call_expression*
+Expression::make_call(Expression* fn, Expression_list* args,
+		      source_location location)
 {
-  gcc_assert(this->end_ == NULL);
-  if (!this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_decrement_lvalue(refcounts, this);
+  return new Call_expression(fn, args, location);
 }
 
-// Get a tree for an array index.
+// A single result from a call which returns multiple results.
 
-tree
-Array_index_expression::do_get_tree(Translate_context* context)
+class Call_result_expression : public Expression
 {
-  Gogo* gogo = context->gogo();
-  source_location loc = this->location();
+ public:
+  Call_result_expression(Call_expression* call, unsigned int index)
+    : Expression(EXPRESSION_CALL_RESULT, call->location()),
+      call_(call), index_(index), is_being_copied_(false)
+  { }
 
-  Type* t = this->array_->type();
-  Type* pt = t->points_to();
-  if (pt != NULL)
-    t = pt;
-  Array_type* array_type = t->array_type();
-  gcc_assert(array_type != NULL);
+ protected:
+  int
+  do_traverse(Traverse*);
 
-  tree type_tree = array_type->get_tree(gogo);
+  Type*
+  do_type();
 
-  tree array_tree = this->array_->get_tree(context);
-  if (array_tree == error_mark_node)
-    return error_mark_node;
+  void
+  do_determine_type(const Type_context*);
 
-  tree bad_index = boolean_false_node;
-  if (pt != NULL)
-    {
-      gcc_assert(!array_type->is_open_array_type());
-      if (!DECL_P(array_tree))
-	array_tree = save_expr(array_tree);
-      bad_index = build2(EQ_EXPR, boolean_type_node, array_tree,
-			 fold_convert(TREE_TYPE(array_tree),
-				      null_pointer_node));
-      array_tree = build_fold_indirect_ref(array_tree);
-    }
+  Expression*
+  do_copy()
+  {
+    return new Call_result_expression(this->call_->call_expression(),
+				      this->index_);
+  }
 
-  tree start_tree = this->start_->get_tree(context);
-  if (start_tree == error_mark_node)
-    return error_mark_node;
+  Expression*
+  do_being_copied(Refcounts*, bool);
 
-  if (array_type->length() == NULL && !DECL_P(array_tree))
-    array_tree = save_expr(array_tree);
-  if (!DECL_P(start_tree))
-    start_tree = save_expr(start_tree);
-  tree length_tree = array_type->length_tree(gogo, array_tree);
-  start_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), start_tree);
-  bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node, bad_index,
-			      fold_build2_loc(loc,
-					      (this->end_ == NULL
-					       ? GE_EXPR
-					       : GT_EXPR),
-					      boolean_type_node, start_tree,
-					      length_tree));
+  Expression*
+  do_note_decrements(Refcounts*);
+
+  tree
+  do_get_tree(Translate_context*);
+
+ private:
+  // The underlying call expression.
+  Expression* call_;
+  // Which result we want.
+  unsigned int index_;
+  // Whether the result is being copied.
+  bool is_being_copied_;
+};
 
-  static tree bad_index_fndecl;
-  tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				  loc,
-				  "__go_bad_index",
-				  0,
-				  void_type_node);
-  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+// Traverse a call result.  We only traverse the call expression for
+// index 0, to avoid traversing it multiple times.
 
-  if (this->end_ == NULL)
-    {
-      // Simple array indexing.  This has to return an l-value, so
-      // wrap the index check into START_TREE.
-      start_tree = build2(COMPOUND_EXPR, TREE_TYPE(start_tree),
-			  build3(COND_EXPR, void_type_node,
-				 bad_index, crash, NULL_TREE),
-			  start_tree);
+int
+Call_result_expression::do_traverse(Traverse* traverse)
+{
+  if (this->index_ > 0)
+    return TRAVERSE_CONTINUE;
+  return Expression::traverse(&this->call_, traverse);
+}
 
-      if (array_type->length() != NULL)
-	{
-	  // Fixed array.
-	  return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree,
-			start_tree, NULL_TREE, NULL_TREE);
-	}
-      else
-	{
-	  // Open array.
-	  tree values = array_type->value_pointer_tree(gogo, array_tree);
-	  tree element_type_tree = array_type->element_type()->get_tree(gogo);
-	  tree element_size = TYPE_SIZE_UNIT(element_type_tree);
-	  tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
-					start_tree,
-					fold_convert_loc(loc,
-							 TREE_TYPE(length_tree),
-							 element_size));
-	  tree ptr = fold_build2_loc(loc, POINTER_PLUS_EXPR,
-				     TREE_TYPE(values), values,
-				     fold_convert_loc(loc, sizetype, offset));
-	  return build_fold_indirect_ref(ptr);
-	}
-    }
+// Get the type.
 
-  // Array slice.
+Type*
+Call_result_expression::do_type()
+{
+  if (this->call_->is_error_expression())
+    return Type::make_error_type();
+  Function_type* fntype =
+    this->call_->call_expression()->get_function_type(false);
+  if (fntype == NULL)
+    return Type::make_error_type();
+  const Typed_identifier_list* results = fntype->results();
+  Typed_identifier_list::const_iterator pr = results->begin();
+  for (unsigned int i = 0; i < this->index_; ++i)
+    {
+      gcc_assert(pr != results->end());
+      ++pr;
+    }
+  gcc_assert(pr != results->end());
+  return pr->type();
+}
 
-  tree end_tree = this->end_->get_tree(context);
-  if (end_tree == error_mark_node)
-    return error_mark_node;
-  end_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), end_tree);
+// Determine the type.  We have nothing to do here, but the 0 result
+// needs to pass down to the caller.
 
-  if (!DECL_P(end_tree))
-    end_tree = save_expr(end_tree);
-  tree capacity_tree = array_type->capacity_tree(gogo, array_tree);
-  capacity_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), capacity_tree);
-  capacity_tree = save_expr(capacity_tree);
-  tree bad_end = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
-				 fold_build2_loc(loc, LT_EXPR,
-						 boolean_type_node,
-						 end_tree, start_tree),
-				 fold_build2_loc(loc, GT_EXPR,
-						 boolean_type_node,
-						 end_tree, capacity_tree));
-  bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
-			      bad_index, bad_end);
+void
+Call_result_expression::do_determine_type(const Type_context*)
+{
+  if (this->index_ == 0)
+    this->call_->determine_type_no_context();
+}
 
-  tree element_type_tree = array_type->element_type()->get_tree(gogo);
-  tree element_size = TYPE_SIZE_UNIT(element_type_tree);
-  element_size = fold_convert_loc(loc, TREE_TYPE(length_tree), element_size);
+// The result will come back with a reference, so we don't need to do
+// anything to copy it.
 
-  tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
-				start_tree, element_size);
+Expression*
+Call_result_expression::do_being_copied(Refcounts*, bool)
+{
+  this->is_being_copied_ = true;
+  return this;
+}
 
-  tree value_pointer = array_type->value_pointer_tree(gogo, array_tree);
+// Decrement the reference count if necessary.
 
-  value_pointer = fold_build2_loc(loc, POINTER_PLUS_EXPR,
-				  TREE_TYPE(value_pointer),
-				  value_pointer,
-				  fold_convert_loc(loc, sizetype, offset));
+Expression*
+Call_result_expression::do_note_decrements(Refcounts* refcounts)
+{
+  if (this->is_being_copied_ || !this->type()->has_refcounted_component())
+    return this;
+  return Expression::make_refcount_adjust(refcounts,
+					  REFCOUNT_DECREMENT_COMPUTED,
+					  this, false);
+}
 
-  tree result_length_tree = fold_build2_loc(loc, MINUS_EXPR,
-					    TREE_TYPE(length_tree),
-					    end_tree, start_tree);
+// Return the tree.
 
-  tree result_capacity_tree = fold_build2_loc(loc, MINUS_EXPR,
-					      TREE_TYPE(length_tree),
-					      capacity_tree, start_tree);
+tree
+Call_result_expression::do_get_tree(Translate_context* context)
+{
+  tree call_tree = this->call_->get_tree(context);
+  if (call_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert(TREE_CODE(TREE_TYPE(call_tree)) == RECORD_TYPE);
+  tree field = TYPE_FIELDS(TREE_TYPE(call_tree));
+  for (unsigned int i = 0; i < this->index_; ++i)
+    {
+      gcc_assert(field != NULL_TREE);
+      field = TREE_CHAIN(field);
+    }
+  gcc_assert(field != NULL_TREE);
+  return build3(COMPONENT_REF, TREE_TYPE(field), call_tree, field, NULL_TREE);
+}
 
-  tree struct_tree = this->type()->get_tree(gogo);
-  gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
+// Make a reference to a single result of a call which returns
+// multiple results.
 
-  VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
+Expression*
+Expression::make_call_result(Call_expression* call, unsigned int index)
+{
+  return new Call_result_expression(call, index);
+}
 
-  constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
-  tree field = TYPE_FIELDS(struct_tree);
-  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
-  elt->index = field;
-  elt->value = value_pointer;
+// Class Index_expression.
 
-  elt = VEC_quick_push(constructor_elt, init, NULL);
-  field = TREE_CHAIN(field);
-  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
-  elt->index = field;
-  elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_length_tree);
+// Traversal.
 
-  elt = VEC_quick_push(constructor_elt, init, NULL);
-  field = TREE_CHAIN(field);
-  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
-  elt->index = field;
-  elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_capacity_tree);
+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))
+    return TRAVERSE_EXIT;
+  return TRAVERSE_CONTINUE;
+}
 
-  tree constructor = build_constructor(struct_tree, init);
+// Lower an index expression.  This converts the generic index
+// expression into an array index, a string index, or a map index.
 
-  if (TREE_CONSTANT(value_pointer)
-      && TREE_CONSTANT(result_length_tree)
-      && TREE_CONSTANT(result_capacity_tree))
-    TREE_CONSTANT(constructor) = 1;
+Expression*
+Index_expression::do_lower(Gogo*, int)
+{
+  source_location location = this->location();
+  Expression* left = this->left_;
+  Expression* start = this->start_;
+  Expression* end = this->end_;
 
-  return fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(constructor),
-			 build3(COND_EXPR, void_type_node,
-				bad_index, crash, NULL_TREE),
-			 constructor);
+  Type* type = left->type();
+  if (type->is_error_type())
+    return Expression::make_error(location);
+  else if (type->array_type() != NULL
+	   || (type->deref()->array_type() != NULL
+	       && !type->deref()->array_type()->is_open_array_type()))
+    return Expression::make_array_index(left, start, end, location);
+  else if (type->is_string_type())
+    return Expression::make_string_index(left, start, end, location);
+  else if (type->map_type() != NULL)
+    {
+      if (end != NULL)
+	{
+	  error_at(location, "invalid slice of map");
+	  return Expression::make_error(location);
+	}
+      Map_index_expression* ret= Expression::make_map_index(left, start,
+							    location);
+      if (this->is_lvalue_)
+	ret->set_is_lvalue();
+      return ret;
+    }
+  else
+    {
+      error_at(location,
+	       "attempt to index object which is not array, string, or map");
+      return Expression::make_error(location);
+    }
 }
 
-// Make an array index expression.  END may be NULL.
+// Make an index expression.
 
 Expression*
-Expression::make_array_index(Expression* array, Expression* start,
-			     Expression* end, source_location location)
+Expression::make_index(Expression* left, Expression* start, Expression* end,
+		       source_location location)
 {
-  return new Array_index_expression(array, start, end, location);
+  return new Index_expression(left, start, end, location);
 }
 
-// A string index.  This is used for both indexing and slicing.
+// An array index.  This is used for both indexing and slicing.
 
-class String_index_expression : public Expression
+class Array_index_expression : public Expression
 {
  public:
-  String_index_expression(Expression* string, Expression* start,
-			  Expression* end, source_location location)
-    : Expression(EXPRESSION_STRING_INDEX, location),
-      string_(string), start_(start), end_(end), is_being_copied_(false)
+  Array_index_expression(Expression* array, Expression* start,
+			 Expression* end, source_location location)
+    : Expression(EXPRESSION_ARRAY_INDEX, location),
+      array_(array), start_(start), end_(end), type_(NULL)
   { }
 
  protected:
@@ -6509,17 +6695,20 @@ class String_index_expression : public E
   Expression*
   do_copy()
   {
-    return Expression::make_string_index(this->string_->copy(),
-					 this->start_->copy(),
-					 (this->end_ == NULL
-					  ? NULL
-					  : this->end_->copy()),
-					 this->location());
+    return Expression::make_array_index(this->array_->copy(),
+					this->start_->copy(),
+					(this->end_ == NULL
+					 ? NULL
+					 : this->end_->copy()),
+					this->location());
   }
 
   bool
-  do_address_taken(source_location)
-  { return true; }
+  do_is_lvalue() const
+  { return this->end_ == NULL; }
+
+  bool
+  do_address_taken(source_location, bool);
 
   Expression*
   do_being_copied(Refcounts*, bool);
@@ -6527,26 +6716,29 @@ class String_index_expression : public E
   Expression*
   do_note_decrements(Refcounts*);
 
+  Expression*
+  do_being_set(Refcounts*);
+
   tree
   do_get_tree(Translate_context*);
 
  private:
-  // The string we are getting a value from.
-  Expression* string_;
+  // The array we are getting a value from.
+  Expression* array_;
   // The start or only index.
   Expression* start_;
   // The end index of a slice.  This may be NULL.
   Expression* end_;
-  // Whether the slice is being copied.
-  bool is_being_copied_;
+  // The type of the expression.
+  Type* type_;
 };
 
-// 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;
@@ -6558,33 +6750,49 @@ String_index_expression::do_traverse(Tra
   return TRAVERSE_CONTINUE;
 }
 
-// Return the type of a string index.
+// Return the type of an array index.
 
 Type*
-String_index_expression::do_type()
+Array_index_expression::do_type()
 {
-  if (this->end_ == NULL)
-    return Type::lookup_integer_type("uint8");
-  else
-    return Type::make_string_type();
+  if (this->type_ == NULL)
+    {
+      Array_type* type = this->array_->type()->deref()->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_open_array_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_;
 }
 
-// Determine the type of a string index.
+// Set the type of an array index.
 
 void
-String_index_expression::do_determine_type(const Type_context*)
+Array_index_expression::do_determine_type(const Type_context*)
 {
-  this->string_->determine_type_no_context();
+  this->array_->determine_type_no_context();
   Type_context subcontext(NULL, true);
   this->start_->determine_type(&subcontext);
   if (this->end_ != NULL)
     this->end_->determine_type(&subcontext);
 }
 
-// Check types of a string index.
+// Check types of an array index.
 
 void
-String_index_expression::do_check_types(Gogo*)
+Array_index_expression::do_check_types(Gogo*)
 {
   if (this->start_->type()->integer_type() == NULL)
     this->report_error(_("index must be integer"));
@@ -6592,1551 +6800,1383 @@ String_index_expression::do_check_types(
       && this->end_->type()->integer_type() == NULL)
     this->report_error(_("slice end must be integer"));
 
-  std::string sval;
-  bool sval_valid = this->string_->string_constant_value(&sval);
+  Array_type* array_type = this->array_->type()->deref()->array_type();
+  gcc_assert(array_type != NULL);
 
-  mpz_t ival;
-  mpz_init(ival);
   Type* dummy;
-  if (this->start_->integer_constant_value(true, ival, &dummy))
-    {
-      if (mpz_sgn(ival) < 0
-	  || (sval_valid && mpz_cmp_ui(ival, sval.length()) >= 0))
-	{
-	  error_at(this->start_->location(), "string index out of bounds");
-	  this->set_is_error();
-	}
-    }
-  if (this->end_ != NULL)
-    {
-      if (this->end_->integer_constant_value(true, ival, &dummy))
-	{
-	  if (mpz_sgn(ival) < 0
-	      || (sval_valid && mpz_cmp_ui(ival, sval.length()) > 0))
-	    {
-	      error_at(this->end_->location(), "string index out of bounds");
-	      this->set_is_error();
-	    }
-	}
-    }
-  mpz_clear(ival);
-}
-
-// Copying a string index or a string slice does not require any
-// special action.
-
-Expression*
-String_index_expression::do_being_copied(Refcounts*, bool)
-{
-  this->is_being_copied_ = true;
-  return this;
-}
-
-// A string slice introduces a new reference which must be decremented
-// if the value is discarded.
-
-Expression*
-String_index_expression::do_note_decrements(Refcounts* refcounts)
-{
-  if (this->end_ == NULL || this->is_being_copied_)
-    return this;
-  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
-					  this, false);
-}
-
-// Get a tree for a string index.
-
-tree
-String_index_expression::do_get_tree(Translate_context* context)
-{
-  tree string_tree = this->string_->get_tree(context);
-  if (string_tree == error_mark_node)
-    return error_mark_node;
-
-  if (this->string_->type()->points_to() != NULL)
-    string_tree = build_fold_indirect_ref(string_tree);
-
-  tree start_tree = this->start_->get_tree(context);
-  if (start_tree == error_mark_node)
-    return error_mark_node;
-  start_tree = fold_convert(sizetype, start_tree);
-
-  tree string_type = TREE_TYPE(string_tree);
-
-  if (this->end_ == NULL)
-    {
-      if (!DECL_P(string_tree))
-	string_tree = save_expr(string_tree);
-      if (!DECL_P(start_tree))
-	start_tree = save_expr(start_tree);
-
-      tree length_tree = String_type::length_tree(context->gogo(),
-						  string_tree);
-      tree bad_index = fold_build2(GE_EXPR, boolean_type_node,
-				   start_tree, length_tree);
-
-      // FIXME: Duplicates Array_index::do_get_tree.
-      static tree bad_index_fndecl;
-      tree crash = Gogo::call_builtin(&bad_index_fndecl,
-				      this->location(),
-				      "__go_bad_index",
-				      0,
-				      void_type_node);
-      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
-
-      tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree);
-      tree ptr = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(bytes_tree),
-			     bytes_tree, start_tree);
-      tree index = build_fold_indirect_ref(ptr);
-
-      return fold_build2(COMPOUND_EXPR, TREE_TYPE(index),
-			 build3(COND_EXPR, void_type_node,
-				bad_index, crash, NULL_TREE),
-			 index);
-    }
-  else
-    {
-      tree end_tree = this->end_->get_tree(context);
-      if (end_tree == error_mark_node)
-	return error_mark_node;
-      end_tree = fold_convert(sizetype, end_tree);
-      static tree strslice_fndecl;
-      return Gogo::call_builtin(&strslice_fndecl,
-				this->location(),
-				"__go_string_slice",
-				3,
-				string_type,
-				string_type,
-				string_tree,
-				sizetype,
-				start_tree,
-				sizetype,
-				end_tree);
-    }
-}
-
-// Make a string index expression.  END may be NULL.
-
-Expression*
-Expression::make_string_index(Expression* string, Expression* start,
-			      Expression* end, source_location location)
-{
-  return new String_index_expression(string, start, end, 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()->deref()->map_type();
-  gcc_assert(mt != NULL);
-  return mt;
-}
-
-// Map index traversal.
-
-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);
-}
-
-// Return the type of a map index.
-
-Type*
-Map_index_expression::do_type()
-{
-  Type* type = this->get_map_type()->val_type();
-  // If this map index is in a tuple assignment, we actually return a
-  // pointer to the value type.  Tuple_map_assignment_statement is
-  // responsible for handling this correctly.  We need to get the type
-  // right in case this gets assigned to a temporary variable.
-  if (this->is_in_tuple_assignment_)
-    type = Type::make_pointer_type(type);
-  return type;
-}
-
-// Fix the type of a map index.
-
-void
-Map_index_expression::do_determine_type(const Type_context*)
-{
-  this->map_->determine_type_no_context();
-  Type_context subcontext(this->get_map_type()->key_type(), true);
-  this->index_->determine_type(&subcontext);
-}
-
-// Check types of a map index.
-
-void
-Map_index_expression::do_check_types(Gogo*)
-{
-  std::string reason;
-  if (!Type::are_compatible_for_assign(this->get_map_type()->key_type(),
-				       this->index_->type(),
-				       &reason))
+  mpz_t lval;
+  mpz_init(lval);
+  bool lval_valid = (array_type->length() != NULL
+		     && array_type->length()->integer_constant_value(true,
+								     lval,
+								     &dummy));
+  mpz_t ival;
+  mpz_init(ival);
+  if (this->start_->integer_constant_value(true, ival, &dummy))
     {
-      if (reason.empty())
-	this->report_error(_("incompatible type for map index"));
-      else
+      if (mpz_sgn(ival) < 0
+	  || (lval_valid
+	      && (this->end_ == NULL
+		  ? mpz_cmp(ival, lval) >= 0
+		  : mpz_cmp(ival, lval) > 0)))
 	{
-	  error_at(this->location(), "incompatible type for map index (%s)",
-		   reason.c_str());
+	  error_at(this->start_->location(), "array index out of bounds");
 	  this->set_is_error();
 	}
     }
+  if (this->end_ != NULL)
+    {
+      if (this->end_->integer_constant_value(true, ival, &dummy))
+	{
+	  if (mpz_sgn(ival) < 0 || (lval_valid && mpz_cmp(ival, lval) > 0))
+	    {
+	      error_at(this->end_->location(), "array index out of bounds");
+	      this->set_is_error();
+	    }
+	}
+    }
+  mpz_clear(ival);
+  mpz_clear(lval);
 }
 
-// We can take the address of a map index expression if it is an
-// lvalue.
+// We can take the address of an array index.  We don't support taking
+// the address of a slice--I'm not sure what the type of that would
+// be.  Taking the address of an array index implies taking the
+// address of the array.
 
 bool
-Map_index_expression::do_address_taken(source_location location)
+Array_index_expression::do_address_taken(source_location location,
+					 bool escapes)
 {
-  if (!this->is_lvalue_)
+  if (!this->array_->address_taken(location, escapes))
+    return false;
+  if (this->end_ != NULL)
     {
-      this->report_address_taken_error(location);
+      error_at(location, "may not take address of array slice");
       return false;
     }
   return true;
 }
 
-// If we are copying the map index to a variable, we need to increment
-// the reference count.
+// Copying an element of an array may require incrementing a reference
+// count.  Copying a slice requires incrementing the reference count
+// of the underlying array.
 
 Expression*
-Map_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+Array_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
 {
-  if (!this->type()->has_refcounted_component())
+  if (this->end_ == NULL && !this->type()->has_refcounted_component())
     return this;
   return Expression::make_refcount_adjust(refcounts,
 					  REFCOUNT_INCREMENT_COPIED,
 					  this, for_local);
 }
 
-// We don't do anything with the reference count of a map index if we
-// aren't copying it, so there is nothing to do here.  FIXME: Is this
-// safe?
+// Referring to an element of an array does not require changing any
+// reference counts.  If the array is changed before the value is
+// used, the program is ill-defined.  We do not introduce a reference
+// count for a slice, so there is nothing to decrement.  FIXME: Is
+// that safe?
 
 Expression*
-Map_index_expression::do_note_decrements(Refcounts*)
+Array_index_expression::do_note_decrements(Refcounts*)
 {
   return this;
 }
 
-// If we are changing the map index, we need to decrement the
-// reference count for the old value, and we may need to increment the
-// reference count for the new index.
+// Assigning to an element of an array may require decrementing a
+// reference count of the old value.
 
 Expression*
-Map_index_expression::do_being_set(Refcounts* refcounts)
+Array_index_expression::do_being_set(Refcounts* refcounts)
 {
-  if (this->get_map_type()->key_type()->has_refcounted_component())
-    this->index_ = this->index_->being_copied(refcounts, false);
+  gcc_assert(this->end_ == NULL);
   if (!this->type()->has_refcounted_component())
     return this;
   return Expression::make_refcount_decrement_lvalue(refcounts, this);
 }
 
-// Get a tree for a map index.
+// Get a tree for an array index.
 
 tree
-Map_index_expression::do_get_tree(Translate_context* context)
+Array_index_expression::do_get_tree(Translate_context* context)
 {
-  Map_type* type = this->get_map_type();
+  Gogo* gogo = context->gogo();
+  source_location loc = this->location();
 
-  tree valptr = this->get_value_pointer(context, this->is_lvalue_);
-  if (valptr == error_mark_node)
-    return error_mark_node;
-  valptr = save_expr(valptr);
+  Type* t = this->array_->type();
+  Type* pt = t->points_to();
+  if (pt != NULL)
+    t = pt;
+  Array_type* array_type = t->array_type();
+  gcc_assert(array_type != NULL);
 
-  tree val_type_tree = TREE_TYPE(TREE_TYPE(valptr));
+  tree type_tree = array_type->get_tree(gogo);
 
-  if (this->is_lvalue_)
-    return build_fold_indirect_ref(valptr);
-  else if (this->is_in_tuple_assignment_)
+  tree array_tree = this->array_->get_tree(context);
+  if (array_tree == error_mark_node)
+    return error_mark_node;
+
+  tree bad_index = boolean_false_node;
+  if (pt != NULL)
     {
-      // Tuple_map_assignment_statement is responsible for using this
-      // appropriately.
-      return valptr;
+      gcc_assert(!array_type->is_open_array_type());
+      if (!DECL_P(array_tree))
+	array_tree = save_expr(array_tree);
+      bad_index = build2(EQ_EXPR, boolean_type_node, array_tree,
+			 fold_convert(TREE_TYPE(array_tree),
+				      null_pointer_node));
+      array_tree = build_fold_indirect_ref(array_tree);
     }
-  else
+
+  tree start_tree = this->start_->get_tree(context);
+  if (start_tree == error_mark_node)
+    return error_mark_node;
+
+  if (array_type->length() == NULL && !DECL_P(array_tree))
+    array_tree = save_expr(array_tree);
+  if (!DECL_P(start_tree))
+    start_tree = save_expr(start_tree);
+  tree length_tree = array_type->length_tree(gogo, array_tree);
+  start_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), start_tree);
+  bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node, bad_index,
+			      fold_build2_loc(loc,
+					      (this->end_ == NULL
+					       ? GE_EXPR
+					       : GT_EXPR),
+					      boolean_type_node, start_tree,
+					      length_tree));
+
+  static tree bad_index_fndecl;
+  tree crash = Gogo::call_builtin(&bad_index_fndecl,
+				  loc,
+				  "__go_bad_index",
+				  0,
+				  void_type_node);
+  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+
+  if (this->end_ == NULL)
     {
-      return fold_build3(COND_EXPR, val_type_tree,
-			 fold_build2(EQ_EXPR, boolean_type_node, valptr,
-				     fold_convert(TREE_TYPE(valptr),
-						  null_pointer_node)),
-			 type->val_type()->get_init_tree(context->gogo(),
-							 false),
-			 build_fold_indirect_ref(valptr));
-    }
-}
+      // Simple array indexing.  This has to return an l-value, so
+      // wrap the index check into START_TREE.
+      start_tree = build2(COMPOUND_EXPR, TREE_TYPE(start_tree),
+			  build3(COND_EXPR, void_type_node,
+				 bad_index, crash, NULL_TREE),
+			  start_tree);
 
-// Get a tree for the map index.  This returns a tree which evaluates
-// to a pointer to a value.  The pointer will be NULL if the key is
-// not in the map.
+      if (array_type->length() != NULL)
+	{
+	  // Fixed array.
+	  return build4(ARRAY_REF, TREE_TYPE(type_tree), array_tree,
+			start_tree, NULL_TREE, NULL_TREE);
+	}
+      else
+	{
+	  // Open array.
+	  tree values = array_type->value_pointer_tree(gogo, array_tree);
+	  tree element_type_tree = array_type->element_type()->get_tree(gogo);
+	  tree element_size = TYPE_SIZE_UNIT(element_type_tree);
+	  tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
+					start_tree,
+					fold_convert_loc(loc,
+							 TREE_TYPE(length_tree),
+							 element_size));
+	  tree ptr = fold_build2_loc(loc, POINTER_PLUS_EXPR,
+				     TREE_TYPE(values), values,
+				     fold_convert_loc(loc, sizetype, offset));
+	  return build_fold_indirect_ref(ptr);
+	}
+    }
 
-tree
-Map_index_expression::get_value_pointer(Translate_context* context,
-					bool insert)
-{
-  Map_type* type = this->get_map_type();
+  // Array slice.
 
-  tree map_tree = this->map_->get_tree(context);
-  tree index_tree = this->index_->get_tree(context);
-  if (map_tree == error_mark_node || index_tree == error_mark_node)
+  tree end_tree = this->end_->get_tree(context);
+  if (end_tree == error_mark_node)
     return error_mark_node;
+  end_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), end_tree);
 
-  if (this->map_->type()->points_to() != NULL)
-    map_tree = build_fold_indirect_ref(map_tree);
+  if (!DECL_P(end_tree))
+    end_tree = save_expr(end_tree);
+  tree capacity_tree = array_type->capacity_tree(gogo, array_tree);
+  capacity_tree = fold_convert_loc(loc, TREE_TYPE(length_tree), capacity_tree);
+  capacity_tree = save_expr(capacity_tree);
+  tree bad_end = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
+				 fold_build2_loc(loc, LT_EXPR,
+						 boolean_type_node,
+						 end_tree, start_tree),
+				 fold_build2_loc(loc, GT_EXPR,
+						 boolean_type_node,
+						 end_tree, capacity_tree));
+  bad_index = fold_build2_loc(loc, TRUTH_OR_EXPR, boolean_type_node,
+			      bad_index, bad_end);
 
-  // We need to pass in a pointer to the key, so stuff it into a
-  // variable.
-  tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
-  DECL_IGNORED_P(tmp) = 0;
-  DECL_INITIAL(tmp) = index_tree;
-  tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
-  tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
-  TREE_ADDRESSABLE(tmp) = 1;
+  tree element_type_tree = array_type->element_type()->get_tree(gogo);
+  tree element_size = TYPE_SIZE_UNIT(element_type_tree);
+  element_size = fold_convert_loc(loc, TREE_TYPE(length_tree), element_size);
 
-  static tree map_index_fndecl;
-  tree call = Gogo::call_builtin(&map_index_fndecl,
-				 this->location(),
-				 "__go_map_index",
-				 3,
-				 const_ptr_type_node,
-				 TREE_TYPE(map_tree),
-				 map_tree,
-				 const_ptr_type_node,
-				 tmpref,
-				 boolean_type_node,
-				 (insert
-				  ? boolean_true_node
-				  : boolean_false_node));
+  tree offset = fold_build2_loc(loc, MULT_EXPR, TREE_TYPE(length_tree),
+				start_tree, element_size);
 
-  tree val_type_tree = type->val_type()->get_tree(context->gogo());
-  if (val_type_tree == error_mark_node)
-    return error_mark_node;
-  tree ptr_val_type_tree = build_pointer_type(val_type_tree);
+  tree value_pointer = array_type->value_pointer_tree(gogo, array_tree);
 
-  return build2(COMPOUND_EXPR, ptr_val_type_tree,
-		make_tmp,
-		fold_convert(ptr_val_type_tree, call));
-}
+  value_pointer = fold_build2_loc(loc, POINTER_PLUS_EXPR,
+				  TREE_TYPE(value_pointer),
+				  value_pointer,
+				  fold_convert_loc(loc, sizetype, offset));
+
+  tree result_length_tree = fold_build2_loc(loc, MINUS_EXPR,
+					    TREE_TYPE(length_tree),
+					    end_tree, start_tree);
+
+  tree result_capacity_tree = fold_build2_loc(loc, MINUS_EXPR,
+					      TREE_TYPE(length_tree),
+					      capacity_tree, start_tree);
+
+  tree struct_tree = this->type()->get_tree(gogo);
+  gcc_assert(TREE_CODE(struct_tree) == RECORD_TYPE);
 
-// Return a tree to delete this key from the map.
+  VEC(constructor_elt,gc)* init = VEC_alloc(constructor_elt, gc, 3);
 
-tree
-Map_index_expression::delete_key(Translate_context* context)
-{
-  tree map_tree = this->map_->get_tree(context);
-  tree index_tree = this->index_->get_tree(context);
-  if (map_tree == error_mark_node || index_tree == error_mark_node)
-    return error_mark_node;
+  constructor_elt* elt = VEC_quick_push(constructor_elt, init, NULL);
+  tree field = TYPE_FIELDS(struct_tree);
+  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__values") == 0);
+  elt->index = field;
+  elt->value = value_pointer;
 
-  if (this->map_->type()->points_to() != NULL)
-    map_tree = build_fold_indirect_ref(map_tree);
+  elt = VEC_quick_push(constructor_elt, init, NULL);
+  field = TREE_CHAIN(field);
+  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__count") == 0);
+  elt->index = field;
+  elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_length_tree);
 
-  // We need to pass in a pointer to the key, so stuff it in a
-  // variable.
-  tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
-  DECL_IGNORED_P(tmp) = 0;
-  DECL_INITIAL(tmp) = index_tree;
-  tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
-  tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
-  TREE_ADDRESSABLE(tmp) = 1;
+  elt = VEC_quick_push(constructor_elt, init, NULL);
+  field = TREE_CHAIN(field);
+  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__capacity") == 0);
+  elt->index = field;
+  elt->value = fold_convert_loc(loc, TREE_TYPE(field), result_capacity_tree);
 
-  static tree map_delete_fndecl;
-  tree call = Gogo::call_builtin(&map_delete_fndecl,
-				 this->location(),
-				 "__go_map_delete",
-				 2,
-				 void_type_node,
-				 TREE_TYPE(map_tree),
-				 map_tree,
-				 const_ptr_type_node,
-				 tmpref);
+  tree constructor = build_constructor(struct_tree, init);
 
-  return build2(COMPOUND_EXPR, void_type_node, make_tmp, call);
+  if (TREE_CONSTANT(value_pointer)
+      && TREE_CONSTANT(result_length_tree)
+      && TREE_CONSTANT(result_capacity_tree))
+    TREE_CONSTANT(constructor) = 1;
+
+  return fold_build2_loc(loc, COMPOUND_EXPR, TREE_TYPE(constructor),
+			 build3(COND_EXPR, void_type_node,
+				bad_index, crash, NULL_TREE),
+			 constructor);
 }
 
-// Make a map index expression.
+// Make an array index expression.  END may be NULL.
 
-Map_index_expression*
-Expression::make_map_index(Expression* map, Expression* index,
-			   source_location location)
+Expression*
+Expression::make_array_index(Expression* array, Expression* start,
+			     Expression* end, source_location location)
 {
-  return new Map_index_expression(map, index, location);
+  return new Array_index_expression(array, start, end, location);
 }
 
-// Class Field_reference_expression.
-
-// Return the type of a field reference.
+// A string index.  This is used for both indexing and slicing.
 
-Type*
-Field_reference_expression::do_type()
+class String_index_expression : public Expression
 {
-  Type* expr_type = this->expr_->type();
-  Type* points_to = expr_type->points_to();
-  if (points_to != NULL)
-    expr_type = points_to;
-  Struct_type* struct_type = expr_type->struct_type();
-  gcc_assert(struct_type != NULL);
-  return struct_type->field(this->field_index_)->type();
-}
+ public:
+  String_index_expression(Expression* string, Expression* start,
+			  Expression* end, source_location location)
+    : Expression(EXPRESSION_STRING_INDEX, location),
+      string_(string), start_(start), end_(end), is_being_copied_(false)
+  { }
 
-// Check the types for a field reference.
+ protected:
+  int
+  do_traverse(Traverse*);
 
-void
-Field_reference_expression::do_check_types(Gogo*)
-{
-  Type* expr_type = this->expr_->type();
-  Type* points_to = expr_type->points_to();
-  if (points_to != NULL)
-    expr_type = points_to;
-  Struct_type* struct_type = expr_type->struct_type();
-  gcc_assert(struct_type != NULL);
-  gcc_assert(struct_type->field(this->field_index_) != NULL);
-}
+  Type*
+  do_type();
 
-// We can take the address of a field, and it implies taking the
-// address of the whole structure.
+  void
+  do_determine_type(const Type_context*);
 
-bool
-Field_reference_expression::do_address_taken(source_location location)
-{
-  if (!this->expr_->address_taken(location))
-    return false;
-  return true;
-}
+  void
+  do_check_types(Gogo*);
 
-// If we are copying the field to a variable, we need to increment the
-// reference count.
+  Expression*
+  do_copy()
+  {
+    return Expression::make_string_index(this->string_->copy(),
+					 this->start_->copy(),
+					 (this->end_ == NULL
+					  ? NULL
+					  : this->end_->copy()),
+					 this->location());
+  }
 
-Expression*
-Field_reference_expression::do_being_copied(Refcounts* refcounts,
-					    bool for_local)
+  bool
+  do_address_taken(source_location, bool)
+  { return true; }
+
+  Expression*
+  do_being_copied(Refcounts*, bool);
+
+  Expression*
+  do_note_decrements(Refcounts*);
+
+  tree
+  do_get_tree(Translate_context*);
+
+ private:
+  // The string we are getting a value from.
+  Expression* string_;
+  // The start or only index.
+  Expression* start_;
+  // The end index of a slice.  This may be NULL.
+  Expression* end_;
+  // Whether the slice is being copied.
+  bool is_being_copied_;
+};
+
+// String index traversal.
+
+int
+String_index_expression::do_traverse(Traverse* traverse)
 {
-  if (!this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_adjust(refcounts,
-					  REFCOUNT_INCREMENT_COPIED,
-					  this, for_local);
+  if (Expression::traverse(&this->string_, 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;
+    }
+  return TRAVERSE_CONTINUE;
 }
 
-// We do not need to bump the reference count; any change to the
-// structure before the value of the field is used would mean an
-// invalid program.
+// Return the type of a string index.
 
-Expression*
-Field_reference_expression::do_note_decrements(Refcounts*)
+Type*
+String_index_expression::do_type()
 {
-  return this;
+  if (this->end_ == NULL)
+    return Type::lookup_integer_type("uint8");
+  else
+    return Type::make_string_type();
 }
 
-// If we are changing the field, we need to decrement the reference
-// count for the old value.
+// Determine the type of a string index.
 
-Expression*
-Field_reference_expression::do_being_set(Refcounts* refcounts)
+void
+String_index_expression::do_determine_type(const Type_context*)
 {
-  if (!this->type()->has_refcounted_component())
-    return this;
-  return Expression::make_refcount_decrement_lvalue(refcounts, this);
+  this->string_->determine_type_no_context();
+  Type_context subcontext(NULL, true);
+  this->start_->determine_type(&subcontext);
+  if (this->end_ != NULL)
+    this->end_->determine_type(&subcontext);
 }
 
-// Get a tree for a field reference.
+// Check types of a string index.
 
-tree
-Field_reference_expression::do_get_tree(Translate_context* context)
+void
+String_index_expression::do_check_types(Gogo*)
 {
-  tree struct_tree = this->expr_->get_tree(context);
-  if (struct_tree == error_mark_node
-      || TREE_TYPE(struct_tree) == error_mark_node)
-    return error_mark_node;
+  if (this->start_->type()->integer_type() == NULL)
+    this->report_error(_("index must be integer"));
+  if (this->end_ != NULL
+      && this->end_->type()->integer_type() == NULL)
+    this->report_error(_("slice end must be integer"));
 
-  if (POINTER_TYPE_P(TREE_TYPE(struct_tree)))
+  std::string sval;
+  bool sval_valid = this->string_->string_constant_value(&sval);
+
+  mpz_t ival;
+  mpz_init(ival);
+  Type* dummy;
+  if (this->start_->integer_constant_value(true, ival, &dummy))
     {
-      // If we are dereferencing the pointer to a large struct, we
-      // need to check for nil.  We don't bother to check for small
-      // structs because we expect the system to crash on a nil
-      // pointer dereference.
-      HOST_WIDE_INT s = int_size_in_bytes(TREE_TYPE(TREE_TYPE(struct_tree)));
-      if (s == -1 || s >= 4096)
+      if (mpz_sgn(ival) < 0
+	  || (sval_valid && mpz_cmp_ui(ival, sval.length()) >= 0))
 	{
-	  if (!DECL_P(struct_tree))
-	    struct_tree = save_expr(struct_tree);
-	  tree compare = build2(EQ_EXPR, boolean_type_node, struct_tree,
-				fold_convert(TREE_TYPE(struct_tree),
-					     null_pointer_node));
-	  // FIXME: This should be a different error message.
-	  static tree bad_index_fndecl;
-	  tree crash = Gogo::call_builtin(&bad_index_fndecl,
-					  this->location(),
-					  "__go_bad_index",
-					  0,
-					  void_type_node);
-	  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
-	  struct_tree = build2(COMPOUND_EXPR, TREE_TYPE(struct_tree),
-			       build3(COND_EXPR, void_type_node, compare,
-				      crash, NULL_TREE),
-			       struct_tree);
-	}
-      struct_tree = build_fold_indirect_ref(struct_tree);
-    }
-
-  gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE);
-  tree field = TYPE_FIELDS(TREE_TYPE(struct_tree));
-  gcc_assert(field != NULL_TREE);
-  for (unsigned int i = this->field_index_; i > 0; --i)
+	  error_at(this->start_->location(), "string index out of bounds");
+	  this->set_is_error();
+	}
+    }
+  if (this->end_ != NULL)
     {
-      field = TREE_CHAIN(field);
-      gcc_assert(field != NULL_TREE);
+      if (this->end_->integer_constant_value(true, ival, &dummy))
+	{
+	  if (mpz_sgn(ival) < 0
+	      || (sval_valid && mpz_cmp_ui(ival, sval.length()) > 0))
+	    {
+	      error_at(this->end_->location(), "string index out of bounds");
+	      this->set_is_error();
+	    }
+	}
     }
-  return build3(COMPONENT_REF, TREE_TYPE(field), struct_tree, field,
-		NULL_TREE);
+  mpz_clear(ival);
 }
 
-// Make a reference to a qualified identifier in an expression.
+// Copying a string index or a string slice does not require any
+// special action.
 
-Field_reference_expression*
-Expression::make_field_reference(Expression* expr, unsigned int field_index,
-				 source_location location)
+Expression*
+String_index_expression::do_being_copied(Refcounts*, bool)
 {
-  return new Field_reference_expression(expr, field_index, location);
+  this->is_being_copied_ = true;
+  return this;
 }
 
-// Class Interface_field_reference_expression.
+// A string slice introduces a new reference which must be decremented
+// if the value is discarded.
 
-// Return a tree for the pointer to the function to call.
+Expression*
+String_index_expression::do_note_decrements(Refcounts* refcounts)
+{
+  if (this->end_ == NULL || this->is_being_copied_)
+    return this;
+  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+					  this, false);
+}
+
+// Get a tree for a string index.
 
 tree
-Interface_field_reference_expression::get_function_tree(Translate_context*,
-							tree expr)
+String_index_expression::do_get_tree(Translate_context* context)
 {
-  if (this->expr_->type()->points_to() != NULL)
-    expr = build_fold_indirect_ref(expr);
+  tree string_tree = this->string_->get_tree(context);
+  if (string_tree == error_mark_node)
+    return error_mark_node;
 
-  tree expr_type = TREE_TYPE(expr);
-  gcc_assert(POINTER_TYPE_P(expr_type));
-  expr_type = TREE_TYPE(expr_type);
-  expr = build_fold_indirect_ref(expr);
-  gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+  if (this->string_->type()->points_to() != NULL)
+    string_tree = build_fold_indirect_ref(string_tree);
 
-  tree field = TREE_CHAIN(TYPE_FIELDS(expr_type));
-  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0);
+  tree start_tree = this->start_->get_tree(context);
+  if (start_tree == error_mark_node)
+    return error_mark_node;
+  start_tree = fold_convert(sizetype, start_tree);
 
-  tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
-  gcc_assert(POINTER_TYPE_P(TREE_TYPE(table)));
+  tree string_type = TREE_TYPE(string_tree);
 
-  table = build_fold_indirect_ref(table);
-  gcc_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE);
+  if (this->end_ == NULL)
+    {
+      if (!DECL_P(string_tree))
+	string_tree = save_expr(string_tree);
+      if (!DECL_P(start_tree))
+	start_tree = save_expr(start_tree);
 
-  std::string name = Gogo::unpack_hidden_name(this->name_);
-  for (field = TYPE_FIELDS(TREE_TYPE(table));
-       field != NULL_TREE;
-       field = TREE_CHAIN(field))
+      tree length_tree = String_type::length_tree(context->gogo(),
+						  string_tree);
+      tree bad_index = fold_build2(GE_EXPR, boolean_type_node,
+				   start_tree, length_tree);
+
+      // FIXME: Duplicates Array_index::do_get_tree.
+      static tree bad_index_fndecl;
+      tree crash = Gogo::call_builtin(&bad_index_fndecl,
+				      this->location(),
+				      "__go_bad_index",
+				      0,
+				      void_type_node);
+      TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+
+      tree bytes_tree = String_type::bytes_tree(context->gogo(), string_tree);
+      tree ptr = fold_build2(POINTER_PLUS_EXPR, TREE_TYPE(bytes_tree),
+			     bytes_tree, start_tree);
+      tree index = build_fold_indirect_ref(ptr);
+
+      return fold_build2(COMPOUND_EXPR, TREE_TYPE(index),
+			 build3(COND_EXPR, void_type_node,
+				bad_index, crash, NULL_TREE),
+			 index);
+    }
+  else
     {
-      if (name == IDENTIFIER_POINTER(DECL_NAME(field)))
-	break;
+      tree end_tree = this->end_->get_tree(context);
+      if (end_tree == error_mark_node)
+	return error_mark_node;
+      end_tree = fold_convert(sizetype, end_tree);
+      static tree strslice_fndecl;
+      return Gogo::call_builtin(&strslice_fndecl,
+				this->location(),
+				"__go_string_slice",
+				3,
+				string_type,
+				string_type,
+				string_tree,
+				sizetype,
+				start_tree,
+				sizetype,
+				end_tree);
     }
-  gcc_assert(field != NULL_TREE);
-
-  return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE);
 }
 
-// Return a tree for the first argument to pass to the interface
-// function.
+// Make a string index expression.  END may be NULL.
 
-tree
-Interface_field_reference_expression::get_underlying_object_tree(
-    Translate_context*,
-    tree expr)
+Expression*
+Expression::make_string_index(Expression* string, Expression* start,
+			      Expression* end, source_location location)
 {
-  if (this->expr_->type()->points_to() != NULL)
-    expr = build_fold_indirect_ref(expr);
+  return new String_index_expression(string, start, end, location);
+}
 
-  tree expr_type = TREE_TYPE(expr);
-  gcc_assert(POINTER_TYPE_P(expr_type));
-  expr_type = TREE_TYPE(expr_type);
-  expr = build_fold_indirect_ref(expr);
-  gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+// Class Map_index.
 
-  tree field = TREE_CHAIN(TREE_CHAIN(TYPE_FIELDS(expr_type)));
-  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
+// Get the type of the map.
 
-  return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+Map_type*
+Map_index_expression::get_map_type() const
+{
+  Map_type* mt = this->map_->type()->deref()->map_type();
+  gcc_assert(mt != NULL);
+  return mt;
 }
 
-// Traversal.
+// Map index traversal.
 
 int
-Interface_field_reference_expression::do_traverse(Traverse* traverse)
+Map_index_expression::do_traverse(Traverse* traverse)
 {
-  return Expression::traverse(&this->expr_, traverse);
+  if (Expression::traverse(&this->map_, traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  return Expression::traverse(&this->index_, traverse);
 }
 
-// Return the type of an interface field reference.
+// Return the type of a map index.
 
 Type*
-Interface_field_reference_expression::do_type()
+Map_index_expression::do_type()
 {
-  Type* expr_type = this->expr_->type();
-
-  Type* points_to = expr_type->points_to();
-  if (points_to != NULL)
-    expr_type = points_to;
-
-  Interface_type* interface_type = expr_type->interface_type();
-  if (interface_type == NULL)
-    return Type::make_error_type();
-
-  const Typed_identifier* method = interface_type->find_method(this->name_);
-  if (method == NULL)
-    return Type::make_error_type();
-
-  return Type::make_pointer_type(method->type());
+  Type* type = this->get_map_type()->val_type();
+  // If this map index is in a tuple assignment, we actually return a
+  // pointer to the value type.  Tuple_map_assignment_statement is
+  // responsible for handling this correctly.  We need to get the type
+  // right in case this gets assigned to a temporary variable.
+  if (this->is_in_tuple_assignment_)
+    type = Type::make_pointer_type(type);
+  return type;
 }
 
-// Determine types.
+// Fix the type of a map index.
 
 void
-Interface_field_reference_expression::do_determine_type(const Type_context*)
+Map_index_expression::do_determine_type(const Type_context*)
 {
-  this->expr_->determine_type_no_context();
+  this->map_->determine_type_no_context();
+  Type_context subcontext(this->get_map_type()->key_type(), true);
+  this->index_->determine_type(&subcontext);
 }
 
-// Check the types for an interface field reference.
+// Check types of a map index.
 
 void
-Interface_field_reference_expression::do_check_types(Gogo*)
+Map_index_expression::do_check_types(Gogo*)
 {
-  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)
-    this->report_error(_("expected interface or pointer to interface"));
-  else
+  std::string reason;
+  if (!Type::are_compatible_for_assign(this->get_map_type()->key_type(),
+				       this->index_->type(),
+				       &reason))
     {
-      const Typed_identifier* method =
-	interface_type->find_method(this->name_);
-      if (method == NULL)
+      if (reason.empty())
+	this->report_error(_("incompatible type for map index"));
+      else
 	{
-	  error_at(this->location(), "method %qs not in interface",
-		   this->name_.c_str());
+	  error_at(this->location(), "incompatible type for map index (%s)",
+		   reason.c_str());
 	  this->set_is_error();
 	}
     }
 }
 
-// Get a tree for a reference to a field in an interface.  There is no
-// standard tree type representation for this: it's a function
-// attached to its first argument, like a Bound_method_expression.
-// The only places it may currently be used are in a Call_expression
-// or a Go_statement, which will take it apart directly.  So this has
-// nothing to do at present.
+// We can take the address of a map index expression if it is an
+// lvalue.
+
+bool
+Map_index_expression::do_address_taken(source_location location, bool)
+{
+  if (!this->is_lvalue_)
+    {
+      this->report_address_taken_error(location);
+      return false;
+    }
+  return true;
+}
+
+// If we are copying the map index to a variable, we need to increment
+// the reference count.
+
+Expression*
+Map_index_expression::do_being_copied(Refcounts* refcounts, bool for_local)
+{
+  if (!this->type()->has_refcounted_component())
+    return this;
+  return Expression::make_refcount_adjust(refcounts,
+					  REFCOUNT_INCREMENT_COPIED,
+					  this, for_local);
+}
 
-tree
-Interface_field_reference_expression::do_get_tree(Translate_context*)
+// We don't do anything with the reference count of a map index if we
+// aren't copying it, so there is nothing to do here.  FIXME: Is this
+// safe?
+
+Expression*
+Map_index_expression::do_note_decrements(Refcounts*)
 {
-  gcc_unreachable();
+  return this;
 }
 
-// Make a reference to a field in an interface.
+// If we are changing the map index, we need to decrement the
+// reference count for the old value, and we may need to increment the
+// reference count for the new index.
 
 Expression*
-Expression::make_interface_field_reference(Expression* expr,
-					   const std::string& field,
-					   source_location location)
+Map_index_expression::do_being_set(Refcounts* refcounts)
 {
-  return new Interface_field_reference_expression(expr, field, location);
+  if (this->get_map_type()->key_type()->has_refcounted_component())
+    this->index_ = this->index_->being_copied(refcounts, false);
+  if (!this->type()->has_refcounted_component())
+    return this;
+  return Expression::make_refcount_decrement_lvalue(refcounts, this);
 }
 
-// A general selector.  This is a Parser_expression for LEFT.NAME.  It
-// is lowered after we know the type of the left hand side.
+// Get a tree for a map index.
 
-class Selector_expression : public Parser_expression
+tree
+Map_index_expression::do_get_tree(Translate_context* context)
 {
- public:
-  Selector_expression(Expression* left, const std::string& name,
-		      source_location location)
-    : Parser_expression(EXPRESSION_SELECTOR, location),
-      left_(left), name_(name)
-  { }
-
- protected:
-  int
-  do_traverse(Traverse* traverse)
-  { return Expression::traverse(&this->left_, traverse); }
+  Map_type* type = this->get_map_type();
 
-  Expression*
-  do_lower(Gogo*, int);
+  tree valptr = this->get_value_pointer(context, this->is_lvalue_);
+  if (valptr == error_mark_node)
+    return error_mark_node;
+  valptr = save_expr(valptr);
 
-  Expression*
-  do_copy()
-  {
-    return new Selector_expression(this->left_->copy(), this->name_,
-				   this->location());
-  }
+  tree val_type_tree = TREE_TYPE(TREE_TYPE(valptr));
 
- private:
-  // The expression on the left hand side.
-  Expression* left_;
-  // The name on the right hand side.
-  std::string name_;
-};
+  if (this->is_lvalue_)
+    return build_fold_indirect_ref(valptr);
+  else if (this->is_in_tuple_assignment_)
+    {
+      // Tuple_map_assignment_statement is responsible for using this
+      // appropriately.
+      return valptr;
+    }
+  else
+    {
+      return fold_build3(COND_EXPR, val_type_tree,
+			 fold_build2(EQ_EXPR, boolean_type_node, valptr,
+				     fold_convert(TREE_TYPE(valptr),
+						  null_pointer_node)),
+			 type->val_type()->get_init_tree(context->gogo(),
+							 false),
+			 build_fold_indirect_ref(valptr));
+    }
+}
 
-// Lower a selector expression once we know the real type of the left
-// hand side.
+// Get a tree for the map index.  This returns a tree which evaluates
+// to a pointer to a value.  The pointer will be NULL if the key is
+// not in the map.
 
-Expression*
-Selector_expression::do_lower(Gogo*, int)
+tree
+Map_index_expression::get_value_pointer(Translate_context* context,
+					bool insert)
 {
-  source_location location = this->location();
-  Expression* left = this->left_;
-  const std::string& name(this->name_);
+  Map_type* type = this->get_map_type();
 
-  Type* type = left->type();
-  Type* ptype = type->deref();
-  Struct_type* struct_type = ptype->struct_type();
-  Interface_type* interface_type = ptype->interface_type();
-  Named_type* named_type = type->named_type();
-  if (named_type == NULL && type != ptype)
-    named_type = ptype->named_type();
+  tree map_tree = this->map_->get_tree(context);
+  tree index_tree = this->index_->get_tree(context);
+  if (map_tree == error_mark_node || index_tree == error_mark_node)
+    return error_mark_node;
 
-  if (type->is_error_type())
-    return Expression::make_error(location);
-  else if (struct_type != NULL && named_type == NULL)
-    {
-      Expression* ret = struct_type->field_reference(left, name, location);
-      if (ret != NULL)
-	return ret;
-    }
-  else if (interface_type != NULL && interface_type->find_method(name) != NULL)
-    return Expression::make_interface_field_reference(left, name, location);
+  if (this->map_->type()->points_to() != NULL)
+    map_tree = build_fold_indirect_ref(map_tree);
 
-  if (named_type != NULL)
-    {
-      bool found_pointer_method;
-      Expression* ret = named_type->bind_field_or_method(left, name, location,
-							 &found_pointer_method);
-      if (ret != NULL)
-	return ret;
-      if (found_pointer_method)
-	{
-	  error_at(location, "method requires a pointer");
-	  return Expression::make_error(location);
-	}
-    }
+  // We need to pass in a pointer to the key, so stuff it into a
+  // variable.
+  tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
+  DECL_IGNORED_P(tmp) = 0;
+  DECL_INITIAL(tmp) = index_tree;
+  tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
+  tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
+  TREE_ADDRESSABLE(tmp) = 1;
 
-  if (struct_type == NULL && interface_type == NULL && named_type == NULL)
-    error_at(location,
-	     "request for %qs in something which has no fields or methods",
-	     Gogo::unpack_hidden_name(name).c_str());
-  else
-    error_at(location, "reference to undefined field or method %qs",
-	     Gogo::unpack_hidden_name(name).c_str());
-  return Expression::make_error(location);
-}
+  static tree map_index_fndecl;
+  tree call = Gogo::call_builtin(&map_index_fndecl,
+				 this->location(),
+				 "__go_map_index",
+				 3,
+				 const_ptr_type_node,
+				 TREE_TYPE(map_tree),
+				 map_tree,
+				 const_ptr_type_node,
+				 tmpref,
+				 boolean_type_node,
+				 (insert
+				  ? boolean_true_node
+				  : boolean_false_node));
 
-// Make a selector expression.
+  tree val_type_tree = type->val_type()->get_tree(context->gogo());
+  if (val_type_tree == error_mark_node)
+    return error_mark_node;
+  tree ptr_val_type_tree = build_pointer_type(val_type_tree);
 
-Expression*
-Expression::make_selector(Expression* left, const std::string& name,
-			  source_location location)
-{
-  return new Selector_expression(left, name, location);
+  return build2(COMPOUND_EXPR, ptr_val_type_tree,
+		make_tmp,
+		fold_convert(ptr_val_type_tree, call));
 }
 
-// Implement the builtin function new.
+// Return a tree to delete this key from the map.
 
-class Allocation_expression : public Expression
+tree
+Map_index_expression::delete_key(Translate_context* context)
 {
- public:
-  Allocation_expression(Type* type, source_location location)
-    : Expression(EXPRESSION_ALLOCATION, location),
-      type_(type), is_being_copied_(false)
-  { }
+  tree map_tree = this->map_->get_tree(context);
+  tree index_tree = this->index_->get_tree(context);
+  if (map_tree == error_mark_node || index_tree == error_mark_node)
+    return error_mark_node;
 
- protected:
-  int
-  do_traverse(Traverse* traverse)
-  { return Type::traverse(this->type_, traverse); }
+  if (this->map_->type()->points_to() != NULL)
+    map_tree = build_fold_indirect_ref(map_tree);
 
-  Type*
-  do_type()
-  { return Type::make_pointer_type(this->type_); }
+  // We need to pass in a pointer to the key, so stuff it in a
+  // variable.
+  tree tmp = create_tmp_var(TREE_TYPE(index_tree), get_name(index_tree));
+  DECL_IGNORED_P(tmp) = 0;
+  DECL_INITIAL(tmp) = index_tree;
+  tree make_tmp = build1(DECL_EXPR, void_type_node, tmp);
+  tree tmpref = fold_convert(const_ptr_type_node, build_fold_addr_expr(tmp));
+  TREE_ADDRESSABLE(tmp) = 1;
 
-  void
-  do_determine_type(const Type_context*)
-  { }
+  static tree map_delete_fndecl;
+  tree call = Gogo::call_builtin(&map_delete_fndecl,
+				 this->location(),
+				 "__go_map_delete",
+				 2,
+				 void_type_node,
+				 TREE_TYPE(map_tree),
+				 map_tree,
+				 const_ptr_type_node,
+				 tmpref);
 
-  void
-  do_check_types(Gogo*);
+  return build2(COMPOUND_EXPR, void_type_node, make_tmp, call);
+}
 
-  Expression*
-  do_copy()
-  { return new Allocation_expression(this->type_, this->location()); }
+// Make a map index expression.
 
-  Expression*
-  do_being_copied(Refcounts*, bool);
+Map_index_expression*
+Expression::make_map_index(Expression* map, Expression* index,
+			   source_location location)
+{
+  return new Map_index_expression(map, index, location);
+}
 
-  Expression*
-  do_note_decrements(Refcounts*);
+// Class Field_reference_expression.
+
+// Return the type of a field reference.
+
+Type*
+Field_reference_expression::do_type()
+{
+  Type* expr_type = this->expr_->type();
+  Type* points_to = expr_type->points_to();
+  if (points_to != NULL)
+    expr_type = points_to;
+  Struct_type* struct_type = expr_type->struct_type();
+  gcc_assert(struct_type != NULL);
+  return struct_type->field(this->field_index_)->type();
+}
+
+// Check the types for a field reference.
+
+void
+Field_reference_expression::do_check_types(Gogo*)
+{
+  Type* expr_type = this->expr_->type();
+  Type* points_to = expr_type->points_to();
+  if (points_to != NULL)
+    expr_type = points_to;
+  Struct_type* struct_type = expr_type->struct_type();
+  gcc_assert(struct_type != NULL);
+  gcc_assert(struct_type->field(this->field_index_) != NULL);
+}
 
-  tree
-  do_get_tree(Translate_context*);
+// We can take the address of a field, and it implies taking the
+// address of the whole structure.
 
- private:
-  // The type we are allocating.
-  Type* type_;
-  // Whether this value is being copied.
-  bool is_being_copied_;
-};
+bool
+Field_reference_expression::do_address_taken(source_location location,
+					     bool escapes)
+{
+  if (!this->expr_->address_taken(location, escapes))
+    return false;
+  return true;
+}
 
-// Check the type of an allocation expression.
+// If we are copying the field to a variable, we need to increment the
+// reference count.
 
-void
-Allocation_expression::do_check_types(Gogo*)
+Expression*
+Field_reference_expression::do_being_copied(Refcounts* refcounts,
+					    bool for_local)
 {
-  if (this->type_->function_type() != NULL)
-    this->report_error(_("invalid new of function type"));
+  if (!this->type()->has_refcounted_component())
+    return this;
+  return Expression::make_refcount_adjust(refcounts,
+					  REFCOUNT_INCREMENT_COPIED,
+					  this, for_local);
 }
 
-// An allocation expression arrives with a reference count, so nothing
-// special is needed to copy it.
+// We do not need to bump the reference count; any change to the
+// structure before the value of the field is used would mean an
+// invalid program.
 
 Expression*
-Allocation_expression::do_being_copied(Refcounts*, bool)
+Field_reference_expression::do_note_decrements(Refcounts*)
 {
-  this->is_being_copied_ = true;
   return this;
 }
 
-// An allocation expression arrives with a reference count, so if we
-// don't copy the value we must free it.
+// If we are changing the field, we need to decrement the reference
+// count for the old value.
 
 Expression*
-Allocation_expression::do_note_decrements(Refcounts* refcounts)
+Field_reference_expression::do_being_set(Refcounts* refcounts)
 {
-  if (this->is_being_copied_)
+  if (!this->type()->has_refcounted_component())
     return this;
-  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
-					  this, false);
+  return Expression::make_refcount_decrement_lvalue(refcounts, this);
 }
 
-// Return a tree for an allocation expression.
+// Get a tree for a field reference.
 
 tree
-Allocation_expression::do_get_tree(Translate_context* context)
+Field_reference_expression::do_get_tree(Translate_context* context)
 {
-  tree type_tree = this->type_->get_tree(context->gogo());
-  tree size_tree = TYPE_SIZE_UNIT(type_tree);
-  tree space = context->gogo()->allocate_memory(size_tree, this->location());
-  return fold_convert(build_pointer_type (type_tree), space);
+  tree struct_tree = this->expr_->get_tree(context);
+  if (struct_tree == error_mark_node
+      || TREE_TYPE(struct_tree) == error_mark_node)
+    return error_mark_node;
+
+  if (POINTER_TYPE_P(TREE_TYPE(struct_tree)))
+    {
+      // If we are dereferencing the pointer to a large struct, we
+      // need to check for nil.  We don't bother to check for small
+      // structs because we expect the system to crash on a nil
+      // pointer dereference.
+      HOST_WIDE_INT s = int_size_in_bytes(TREE_TYPE(TREE_TYPE(struct_tree)));
+      if (s == -1 || s >= 4096)
+	{
+	  if (!DECL_P(struct_tree))
+	    struct_tree = save_expr(struct_tree);
+	  tree compare = build2(EQ_EXPR, boolean_type_node, struct_tree,
+				fold_convert(TREE_TYPE(struct_tree),
+					     null_pointer_node));
+	  // FIXME: This should be a different error message.
+	  static tree bad_index_fndecl;
+	  tree crash = Gogo::call_builtin(&bad_index_fndecl,
+					  this->location(),
+					  "__go_bad_index",
+					  0,
+					  void_type_node);
+	  TREE_THIS_VOLATILE(bad_index_fndecl) = 1;
+	  struct_tree = build2(COMPOUND_EXPR, TREE_TYPE(struct_tree),
+			       build3(COND_EXPR, void_type_node, compare,
+				      crash, NULL_TREE),
+			       struct_tree);
+	}
+      struct_tree = build_fold_indirect_ref(struct_tree);
+    }
+
+  gcc_assert(TREE_CODE(TREE_TYPE(struct_tree)) == RECORD_TYPE);
+  tree field = TYPE_FIELDS(TREE_TYPE(struct_tree));
+  gcc_assert(field != NULL_TREE);
+  for (unsigned int i = this->field_index_; i > 0; --i)
+    {
+      field = TREE_CHAIN(field);
+      gcc_assert(field != NULL_TREE);
+    }
+  return build3(COMPONENT_REF, TREE_TYPE(field), struct_tree, field,
+		NULL_TREE);
 }
 
-// Make an allocation expression.
+// Make a reference to a qualified identifier in an expression.
 
-Expression*
-Expression::make_allocation(Type* type, source_location location)
+Field_reference_expression*
+Expression::make_field_reference(Expression* expr, unsigned int field_index,
+				 source_location location)
 {
-  return new Allocation_expression(type, location);
+  return new Field_reference_expression(expr, field_index, location);
 }
 
-// Implement the builtin function make.
+// Class Interface_field_reference_expression.
 
-class Make_expression : public Expression
+// Return a tree for the pointer to the function to call.
+
+tree
+Interface_field_reference_expression::get_function_tree(Translate_context*,
+							tree expr)
 {
- public:
-  Make_expression(Type* type, Expression_list* args, source_location location)
-    : Expression(EXPRESSION_MAKE, location),
-      type_(type), args_(args), is_being_copied_(false)
-  { }
+  if (this->expr_->type()->points_to() != NULL)
+    expr = build_fold_indirect_ref(expr);
 
- protected:
-  int
-  do_traverse(Traverse* traverse);
+  tree expr_type = TREE_TYPE(expr);
+  gcc_assert(POINTER_TYPE_P(expr_type));
+  expr_type = TREE_TYPE(expr_type);
+  expr = build_fold_indirect_ref(expr);
+  gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
 
-  Type*
-  do_type()
-  { return this->type_; }
+  tree field = TREE_CHAIN(TYPE_FIELDS(expr_type));
+  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__methods") == 0);
 
-  void
-  do_determine_type(const Type_context*);
+  tree table = build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+  gcc_assert(POINTER_TYPE_P(TREE_TYPE(table)));
 
-  void
-  do_check_types(Gogo*);
+  table = build_fold_indirect_ref(table);
+  gcc_assert(TREE_CODE(TREE_TYPE(table)) == RECORD_TYPE);
 
-  Expression*
-  do_copy()
-  {
-    return new Make_expression(this->type_, this->args_->copy(),
-			       this->location());
-  }
+  std::string name = Gogo::unpack_hidden_name(this->name_);
+  for (field = TYPE_FIELDS(TREE_TYPE(table));
+       field != NULL_TREE;
+       field = TREE_CHAIN(field))
+    {
+      if (name == IDENTIFIER_POINTER(DECL_NAME(field)))
+	break;
+    }
+  gcc_assert(field != NULL_TREE);
 
-  Expression*
-  do_being_copied(Refcounts*, bool);
+  return build3(COMPONENT_REF, TREE_TYPE(field), table, field, NULL_TREE);
+}
 
-  Expression*
-  do_note_decrements(Refcounts*);
+// Return a tree for the first argument to pass to the interface
+// function.
 
-  tree
-  do_get_tree(Translate_context*);
+tree
+Interface_field_reference_expression::get_underlying_object_tree(
+    Translate_context*,
+    tree expr)
+{
+  if (this->expr_->type()->points_to() != NULL)
+    expr = build_fold_indirect_ref(expr);
 
- private:
-  // The type we are making.
-  Type* type_;
-  // The arguments to pass to the make routine.
-  Expression_list* args_;
-  // Whether the expression is being copied.
-  bool is_being_copied_;
-};
+  tree expr_type = TREE_TYPE(expr);
+  gcc_assert(POINTER_TYPE_P(expr_type));
+  expr_type = TREE_TYPE(expr_type);
+  expr = build_fold_indirect_ref(expr);
+  gcc_assert(TREE_CODE(expr_type) == RECORD_TYPE);
+
+  tree field = TREE_CHAIN(TREE_CHAIN(TYPE_FIELDS(expr_type)));
+  gcc_assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(field)), "__object") == 0);
+
+  return build3(COMPONENT_REF, TREE_TYPE(field), expr, field, NULL_TREE);
+}
 
 // Traversal.
 
 int
-Make_expression::do_traverse(Traverse* traverse)
+Interface_field_reference_expression::do_traverse(Traverse* traverse)
 {
-  if (this->args_ != NULL
-      && this->args_->traverse(traverse) == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  return TRAVERSE_CONTINUE;
+  return Expression::traverse(&this->expr_, traverse);
 }
 
-// Set types of arguments.
+// Return the type of an interface field reference.
 
-void
-Make_expression::do_determine_type(const Type_context*)
+Type*
+Interface_field_reference_expression::do_type()
 {
-  if (this->args_ != NULL)
-    {
-      for (Expression_list::const_iterator pe = this->args_->begin();
-	   pe != this->args_->end();
-	   ++pe)
-	(*pe)->determine_type_no_context();
-    }
-}
+  Type* expr_type = this->expr_->type();
 
-// Check types for a make expression.
+  Type* points_to = expr_type->points_to();
+  if (points_to != NULL)
+    expr_type = points_to;
 
-void
-Make_expression::do_check_types(Gogo*)
-{
-  if (this->type_->channel_type() == NULL
-      && this->type_->map_type() == NULL
-      && (this->type_->array_type() == NULL
-	  || this->type_->array_type()->length() != NULL))
-    this->report_error(_("invalid type for make function"));
-  else if (!this->type_->check_make_expression(this->args_, this->location()))
-    this->set_is_error();
+  Interface_type* interface_type = expr_type->interface_type();
+  if (interface_type == NULL)
+    return Type::make_error_type();
+
+  const Typed_identifier* method = interface_type->find_method(this->name_);
+  if (method == NULL)
+    return Type::make_error_type();
+
+  return Type::make_pointer_type(method->type());
 }
 
-// An newly created object arrives with a reference count, so nothing
-// special is needed to copy it.
+// Determine types.
 
-Expression*
-Make_expression::do_being_copied(Refcounts*, bool)
+void
+Interface_field_reference_expression::do_determine_type(const Type_context*)
 {
-  this->is_being_copied_ = true;
-  return this;
+  this->expr_->determine_type_no_context();
 }
 
-// A newly created object arrives with a reference count, so if we
-// don't copy the value we must free it.
+// Check the types for an interface field reference.
 
-Expression*
-Make_expression::do_note_decrements(Refcounts* refcounts)
+void
+Interface_field_reference_expression::do_check_types(Gogo*)
 {
-  if (this->is_being_copied_)
-    return this;
-  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
-					  this, false);
+  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)
+    this->report_error(_("expected interface or pointer to interface"));
+  else
+    {
+      const Typed_identifier* method =
+	interface_type->find_method(this->name_);
+      if (method == NULL)
+	{
+	  error_at(this->location(), "method %qs not in interface",
+		   this->name_.c_str());
+	  this->set_is_error();
+	}
+    }
 }
 
-// Return a tree for a make expression.
+// Get a tree for a reference to a field in an interface.  There is no
+// standard tree type representation for this: it's a function
+// attached to its first argument, like a Bound_method_expression.
+// The only places it may currently be used are in a Call_expression
+// or a Go_statement, which will take it apart directly.  So this has
+// nothing to do at present.
 
 tree
-Make_expression::do_get_tree(Translate_context* context)
+Interface_field_reference_expression::do_get_tree(Translate_context*)
 {
-  return this->type_->make_expression_tree(context, this->args_,
-					   this->location());
+  gcc_unreachable();
 }
 
-// Make a make expression.
+// Make a reference to a field in an interface.
 
 Expression*
-Expression::make_make(Type* type, Expression_list* args,
-		      source_location location)
+Expression::make_interface_field_reference(Expression* expr,
+					   const std::string& field,
+					   source_location location)
 {
-  return new Make_expression(type, args, location);
+  return new Interface_field_reference_expression(expr, field, location);
 }
 
-// A type conversion expression.
+// A general selector.  This is a Parser_expression for LEFT.NAME.  It
+// is lowered after we know the type of the left hand side.
 
-class Type_conversion_expression : public Expression
+class Selector_expression : public Parser_expression
 {
  public:
-  Type_conversion_expression(Type* type, Expression* expr,
-			     source_location location)
-    : Expression(EXPRESSION_CONVERSION, location),
-      type_(type), expr_(expr), is_being_copied_(false)
+  Selector_expression(Expression* left, const std::string& name,
+		      source_location location)
+    : Parser_expression(EXPRESSION_SELECTOR, location),
+      left_(left), name_(name)
   { }
 
-  // Return the type to which we are converting.
-  Type*
-  type() const
-  { return this->type_; }
-
-  // Return the expression which we are converting.
-  Expression*
-  expr() const
-  { return this->expr_; }
-
-  static Expression*
-  do_import(Import*);
-
  protected:
   int
-  do_traverse(Traverse* traverse);
+  do_traverse(Traverse* traverse)
+  { return Expression::traverse(&this->left_, traverse); }
 
   Expression*
   do_lower(Gogo*, int);
 
-  bool
-  do_is_constant() const
-  { return this->expr_->is_constant(); }
-
-  bool
-  do_integer_constant_value(bool, mpz_t, Type**) const;
-
-  bool
-  do_float_constant_value(mpfr_t, Type**) const;
-
-  bool
-  do_string_constant_value(std::string*) const;
-
-  Type*
-  do_type()
-  { return this->type_; }
-
-  void
-  do_determine_type(const Type_context*)
-  {
-    Type_context subcontext(this->type_, true);
-    this->expr_->determine_type(&subcontext);
-  }
-
-  void
-  do_check_types(Gogo*);
-
   Expression*
   do_copy()
   {
-    return new Type_conversion_expression(this->type_, this->expr_->copy(),
-					  this->location());
+    return new Selector_expression(this->left_->copy(), this->name_,
+				   this->location());
   }
 
-  Expression*
-  do_being_copied(Refcounts*, bool);
-
-  Expression*
-  do_note_decrements(Refcounts*);
-
-  tree
-  do_get_tree(Translate_context* context);
-
-  void
-  do_export(Export*) const;
-
  private:
-  // The type to convert to.
-  Type* type_;
-  // The expression to convert.
-  Expression* expr_;
-  // Whether this expression is being copied.
-  bool is_being_copied_;
+  // The expression on the left hand side.
+  Expression* left_;
+  // The name on the right hand side.
+  std::string name_;
 };
 
-// Traversal.
-
-int
-Type_conversion_expression::do_traverse(Traverse* traverse)
-{
-  if (Expression::traverse(&this->expr_, traverse) == TRAVERSE_EXIT
-      || Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
-    return TRAVERSE_EXIT;
-  return TRAVERSE_CONTINUE;
-}
-
-// Convert to a constant at lowering time.
+// Lower a selector expression once we know the real type of the left
+// hand side.
 
 Expression*
-Type_conversion_expression::do_lower(Gogo*, int)
+Selector_expression::do_lower(Gogo*, int)
 {
-  Type* type = this->type_;
-  Expression* val = this->expr_;
   source_location location = this->location();
+  Expression* left = this->left_;
+  const std::string& name(this->name_);
 
-  if (type->integer_type() != NULL)
+  Type* type = left->type();
+  Type* ptype = type->deref();
+  Struct_type* struct_type = ptype->struct_type();
+  Interface_type* interface_type = ptype->interface_type();
+  Named_type* named_type = type->named_type();
+  if (named_type == NULL && type != ptype)
+    named_type = ptype->named_type();
+
+  if (type->is_error_type())
+    return Expression::make_error(location);
+  else if (struct_type != NULL && named_type == NULL)
     {
-      mpz_t ival;
-      mpz_init(ival);
-      Type* dummy;
-      if (val->integer_constant_value(false, ival, &dummy))
-	{
-	  if (!Integer_expression::check_constant(ival, type, location))
-	    mpz_set_ui(ival, 0);
-	  return Expression::make_integer(&ival, type, location);
-	}
+      Expression* ret = struct_type->field_reference(left, name, location);
+      if (ret != NULL)
+	return ret;
+    }
+  else if (interface_type != NULL && interface_type->find_method(name) != NULL)
+    return Expression::make_interface_field_reference(left, name, location);
 
-      mpfr_t fval;
-      mpfr_init(fval);
-      if (val->float_constant_value(fval, &dummy))
+  if (named_type != NULL)
+    {
+      bool found_pointer_method;
+      Expression* ret = named_type->bind_field_or_method(left, name, location,
+							 &found_pointer_method);
+      if (ret != NULL)
+	return ret;
+      if (found_pointer_method)
 	{
-	  if (!mpfr_integer_p(fval))
-	    {
-	      error_at(location,
-		       "floating point constant truncated to integer");
-	      return Expression::make_error(location);
-	    }
-	  mpfr_get_z(ival, fval, GMP_RNDN);
-	  if (!Integer_expression::check_constant(ival, type, location))
-	    mpz_set_ui(ival, 0);
-	  return Expression::make_integer(&ival, type, location);
+	  error_at(location, "method requires a pointer");
+	  return Expression::make_error(location);
 	}
-      mpfr_clear(fval);
-      mpz_clear(ival);
     }
 
-  if (type->float_type() != NULL)
-    {
-      mpfr_t fval;
-      mpfr_init(fval);
-      Type* dummy;
-      if (val->float_constant_value(fval, &dummy))
-	{
-	  if (!Float_expression::check_constant(fval, type, location))
-	    mpfr_set_ui(fval, 0, GMP_RNDN);
-	  Float_expression::constrain_float(fval, type);
-	  return Expression::make_float(&fval, type, location);
-	}
-      mpfr_clear(fval);
-    }
+  if (struct_type == NULL && interface_type == NULL && named_type == NULL)
+    error_at(location,
+	     "request for %qs in something which has no fields or methods",
+	     Gogo::unpack_hidden_name(name).c_str());
+  else
+    error_at(location, "reference to undefined field or method %qs",
+	     Gogo::unpack_hidden_name(name).c_str());
+  return Expression::make_error(location);
+}
+
+// Make a selector expression.
+
+Expression*
+Expression::make_selector(Expression* left, const std::string& name,
+			  source_location location)
+{
+  return new Selector_expression(left, name, location);
+}
+
+// Implement the builtin function new.
+
+class Allocation_expression : public Expression
+{
+ public:
+  Allocation_expression(Type* type, source_location location)
+    : Expression(EXPRESSION_ALLOCATION, location),
+      type_(type), is_being_copied_(false)
+  { }
+
+ protected:
+  int
+  do_traverse(Traverse* traverse)
+  { return Type::traverse(this->type_, traverse); }
+
+  Type*
+  do_type()
+  { return Type::make_pointer_type(this->type_); }
+
+  void
+  do_determine_type(const Type_context*)
+  { }
+
+  void
+  do_check_types(Gogo*);
 
-  return this;
-}
+  Expression*
+  do_copy()
+  { return new Allocation_expression(this->type_, this->location()); }
 
-// Return the constant integer value if there is one.
+  Expression*
+  do_being_copied(Refcounts*, bool);
 
-bool
-Type_conversion_expression::do_integer_constant_value(bool iota_is_constant,
-						      mpz_t val,
-						      Type** ptype) const
-{
-  if (this->type_->integer_type() == NULL)
-    return false;
+  Expression*
+  do_note_decrements(Refcounts*);
 
-  mpz_t ival;
-  mpz_init(ival);
-  Type* dummy;
-  if (this->expr_->integer_constant_value(iota_is_constant, ival, &dummy))
-    {
-      if (!Integer_expression::check_constant(ival, this->type_,
-					      this->location()))
-	{
-	  mpz_clear(ival);
-	  return false;
-	}
-      mpz_set(val, ival);
-      mpz_clear(ival);
-      *ptype = this->type_;
-      return true;
-    }
-  mpz_clear(ival);
+  tree
+  do_get_tree(Translate_context*);
 
-  mpfr_t fval;
-  mpfr_init(fval);
-  if (this->expr_->float_constant_value(fval, &dummy))
-    {
-      mpfr_get_z(val, fval, GMP_RNDN);
-      mpfr_clear(fval);
-      if (!Integer_expression::check_constant(val, this->type_,
-					      this->location()))
-	return false;
-      *ptype = this->type_;
-      return true;
-    }
-  mpfr_clear(fval);
+ private:
+  // The type we are allocating.
+  Type* type_;
+  // Whether this value is being copied.
+  bool is_being_copied_;
+};
 
-  return false;
+// Check the type of an allocation expression.
+
+void
+Allocation_expression::do_check_types(Gogo*)
+{
+  if (this->type_->function_type() != NULL)
+    this->report_error(_("invalid new of function type"));
 }
 
-// Return the constant floating point value if there is one.
+// An allocation expression arrives with a reference count, so nothing
+// special is needed to copy it.
 
-bool
-Type_conversion_expression::do_float_constant_value(mpfr_t val,
-						    Type** ptype) const
+Expression*
+Allocation_expression::do_being_copied(Refcounts*, bool)
 {
-  if (this->type_->float_type() == NULL)
-    return false;
-
-  mpfr_t fval;
-  mpfr_init(fval);
-  Type* dummy;
-  if (this->expr_->float_constant_value(fval, &dummy))
-    {
-      if (!Float_expression::check_constant(fval, this->type_,
-					    this->location()))
-	{
-	  mpfr_clear(fval);
-	  return false;
-	}
-      mpfr_set(val, fval, GMP_RNDN);
-      mpfr_clear(fval);
-      Float_expression::constrain_float(val, this->type_);
-      *ptype = this->type_;
-      return true;
-    }
-  mpfr_clear(fval);
+  this->is_being_copied_ = true;
+  return this;
+}
 
-  mpz_t ival;
-  mpz_init(ival);
-  if (this->expr_->integer_constant_value(false, ival, &dummy))
-    {
-      mpfr_set_z(val, ival, GMP_RNDN);
-      mpz_clear(ival);
-      if (!Float_expression::check_constant(val, this->type_,
-					    this->location()))
-	return false;
-      Float_expression::constrain_float(val, this->type_);
-      *ptype = this->type_;
-      return true;
-    }
-  mpz_clear(ival);
+// An allocation expression arrives with a reference count, so if we
+// don't copy the value we must free it.
 
-  return false;
+Expression*
+Allocation_expression::do_note_decrements(Refcounts* refcounts)
+{
+  if (this->is_being_copied_)
+    return this;
+  return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
+					  this, false);
 }
 
-// Return the constant string value if there is one.
+// Return a tree for an allocation expression.
 
-bool
-Type_conversion_expression::do_string_constant_value(std::string* val) const
+tree
+Allocation_expression::do_get_tree(Translate_context* context)
 {
-  if (this->type_->is_string_type()
-      && this->expr_->type()->integer_type() != NULL)
-    {
-      mpz_t ival;
-      mpz_init(ival);
-      Type* dummy;
-      if (this->expr_->integer_constant_value(false, ival, &dummy))
-	{
-	  unsigned long ulval = mpz_get_ui(ival);
-	  if (mpz_cmp_ui(ival, ulval) == 0)
-	    {
-	      Lex::append_char(ulval, true, val, this->location());
-	      mpz_clear(ival);
-	      return true;
-	    }
-	}
-      mpz_clear(ival);
-    }
+  tree type_tree = this->type_->get_tree(context->gogo());
+  tree size_tree = TYPE_SIZE_UNIT(type_tree);
+  tree space = context->gogo()->allocate_memory(size_tree, this->location());
+  return fold_convert(build_pointer_type (type_tree), space);
+}
 
-  // FIXME: Could handle conversion from const []int here.
+// Make an allocation expression.
 
-  return false;
+Expression*
+Expression::make_allocation(Type* type, source_location location)
+{
+  return new Allocation_expression(type, location);
 }
 
-// Check that types are convertible.
+// Implement the builtin function make.
 
-void
-Type_conversion_expression::do_check_types(Gogo*)
+class Make_expression : public Expression
 {
-  Type* type = this->type_;
-  Type* expr_type = this->expr_->type();
-  std::string reason;
+ public:
+  Make_expression(Type* type, Expression_list* args, source_location location)
+    : Expression(EXPRESSION_MAKE, location),
+      type_(type), args_(args), is_being_copied_(false)
+  { }
 
-  if (Type::are_compatible_for_conversion(type, expr_type, &reason))
-    return;
+ protected:
+  int
+  do_traverse(Traverse* traverse);
 
-  bool ok = false;
-  if ((type->integer_type() != NULL || type->float_type() != NULL)
-      && (expr_type->integer_type() != NULL
-	  || expr_type->float_type() != NULL))
-    ok = true;
-  else if (type->is_string_type())
-    {
-      if (expr_type->integer_type() != NULL)
-	ok = true;
-      else
-	{
-	  Type* t = expr_type->deref();
-	  if (t->array_type() != NULL)
-	    {
-	      Type* e = t->array_type()->element_type()->forwarded();
-	      if (e->integer_type() != NULL
-		  && (e == Type::lookup_integer_type("uint8")
-		      || e == Type::lookup_integer_type("int")))
-		ok = true;
-	    }
-	}
-    }
-  else if ((type->is_unsafe_pointer_type()
-	    && (expr_type->points_to() != NULL
-		|| (expr_type->integer_type() != NULL
-		    && expr_type->integer_type()->bits() == POINTER_SIZE
-		    && expr_type->integer_type()->is_unsigned())))
-	   || (expr_type->is_unsafe_pointer_type()
-	       && (type->points_to() != NULL
-		   || (type->integer_type() != NULL
-		       && type->integer_type()->bits() == POINTER_SIZE
-		       && type->integer_type()->is_unsigned()))))
-    {
-      // Conversions between unsafe pointers and other pointers or
-      // integers of appropriate size are permitted.
-      ok = true;
-    }
+  Type*
+  do_type()
+  { return this->type_; }
+
+  void
+  do_determine_type(const Type_context*);
+
+  void
+  do_check_types(Gogo*);
+
+  Expression*
+  do_copy()
+  {
+    return new Make_expression(this->type_, this->args_->copy(),
+			       this->location());
+  }
+
+  Expression*
+  do_being_copied(Refcounts*, bool);
+
+  Expression*
+  do_note_decrements(Refcounts*);
+
+  tree
+  do_get_tree(Translate_context*);
+
+ private:
+  // The type we are making.
+  Type* type_;
+  // The arguments to pass to the make routine.
+  Expression_list* args_;
+  // Whether the expression is being copied.
+  bool is_being_copied_;
+};
 
-  // FIXME: Conversions between interfaces and pointers are supported.
+// Traversal.
 
-  if (!ok)
+int
+Make_expression::do_traverse(Traverse* traverse)
+{
+  if (this->args_ != NULL
+      && this->args_->traverse(traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  if (Type::traverse(this->type_, traverse) == TRAVERSE_EXIT)
+    return TRAVERSE_EXIT;
+  return TRAVERSE_CONTINUE;
+}
+
+// Set types of arguments.
+
+void
+Make_expression::do_determine_type(const Type_context*)
+{
+  if (this->args_ != NULL)
     {
-      if (reason.empty())
-	this->report_error(_("invalid type conversion"));
-      else
-	{
-	  error_at(this->location(), "invalid type conversion (%s)",
-		   reason.c_str());
-	  this->set_is_error();
-	}
+      for (Expression_list::const_iterator pe = this->args_->begin();
+	   pe != this->args_->end();
+	   ++pe)
+	(*pe)->determine_type_no_context();
     }
 }
 
-// The type conversion is being copied elsewhere.  If we do not call a
-// function which creates a new reference, then we need to pass this
-// on to the subsidiary expression.
+// Check types for a make expression.
+
+void
+Make_expression::do_check_types(Gogo*)
+{
+  if (this->type_->channel_type() == NULL
+      && this->type_->map_type() == NULL
+      && (this->type_->array_type() == NULL
+	  || this->type_->array_type()->length() != NULL))
+    this->report_error(_("invalid type for make function"));
+  else if (!this->type_->check_make_expression(this->args_, this->location()))
+    this->set_is_error();
+}
+
+// An newly created object arrives with a reference count, so nothing
+// special is needed to copy it.
 
 Expression*
-Type_conversion_expression::do_being_copied(Refcounts* refcounts,
-					    bool for_local)
+Make_expression::do_being_copied(Refcounts*, bool)
 {
   this->is_being_copied_ = true;
-  Type* type = this->type_;
-  Type* expr_type = this->expr_->type();
-  bool copy_base;
-  if (type == expr_type)
-    copy_base = true;
-  else if (type->interface_type() != NULL
-	   || expr_type->interface_type() != NULL)
-    copy_base = false;
-  else if (type->is_string_type()
-	   && (expr_type->integer_type() != NULL
-	       || expr_type->deref()->array_type() != NULL))
-    copy_base = false;
-  else
-    copy_base = true;
-  if (copy_base && expr_type->has_refcounted_component())
-    this->expr_ = this->expr_->being_copied(refcounts, for_local);
   return this;
 }
 
-// A type conversion may introduce a reference count.
+// A newly created object arrives with a reference count, so if we
+// don't copy the value we must free it.
 
 Expression*
-Type_conversion_expression::do_note_decrements(Refcounts* refcounts)
+Make_expression::do_note_decrements(Refcounts* refcounts)
 {
   if (this->is_being_copied_)
     return this;
-  Type* type = this->type_;
-  Type* expr_type = this->expr_->type();
-  bool need_decrement;
-  if (type == expr_type)
-    need_decrement = false;
-  else if (type->interface_type() != NULL)
-    need_decrement = true;
-  else if (expr_type->interface_type() != NULL)
-    need_decrement = type->has_refcounted_component();
-  else if (type->is_string_type()
-	   && (expr_type->integer_type() != NULL
-	       || expr_type->deref()->array_type() != NULL))
-    need_decrement = true;
-  else
-    need_decrement = false;
-  if (!need_decrement)
-    return this;
   return Expression::make_refcount_adjust(refcounts, REFCOUNT_DECREMENT_NEW,
 					  this, false);
 }
 
-// Get a tree for a type conversion.
+// Return a tree for a make expression.
 
 tree
-Type_conversion_expression::do_get_tree(Translate_context* context)
-{
-  Gogo* gogo = context->gogo();
-  tree type_tree = this->type_->get_tree(gogo);
-  tree expr_tree = this->expr_->get_tree(context);
-
-  if (type_tree == error_mark_node
-      || expr_tree == error_mark_node
-      || TREE_TYPE(expr_tree) == error_mark_node)
-    return error_mark_node;
-
-  if (TYPE_MAIN_VARIANT(type_tree) == TYPE_MAIN_VARIANT(TREE_TYPE(expr_tree)))
-    return fold_convert(type_tree, expr_tree);
-
-  Type* type = this->type_;
-  Type* expr_type = this->expr_->type();
-  tree ret;
-  if (type->interface_type() != NULL || expr_type->interface_type() != NULL)
-    ret = Expression::convert_for_assignment(context, type, expr_type,
-					     expr_tree, this->location());
-  else if (type->integer_type() != NULL)
-    {
-      if (expr_type->integer_type() != NULL
-	  || expr_type->float_type() != NULL
-	  || expr_type->is_unsafe_pointer_type())
-	ret = fold(convert_to_integer(type_tree, expr_tree));
-      else
-	gcc_unreachable();
-    }
-  else if (type->float_type() != NULL)
-    {
-      if (expr_type->integer_type() != NULL
-	  || expr_type->float_type() != NULL)
-	ret = fold(convert_to_real(type_tree, expr_tree));
-      else
-	gcc_unreachable();
-    }
-  else if (type->is_string_type()
-	   && expr_type->integer_type() != NULL)
-    {
-      expr_tree = fold_convert(integer_type_node, expr_tree);
-      if (host_integerp(expr_tree, 0))
-	{
-	  HOST_WIDE_INT intval = tree_low_cst(expr_tree, 0);
-	  std::string s;
-	  Lex::append_char(intval, true, &s, this->location());
-	  Expression* se = Expression::make_string(s, this->location());
-	  return se->get_tree(context);
-	}
-
-      static tree int_to_string_fndecl;
-      ret = Gogo::call_builtin(&int_to_string_fndecl,
-			       this->location(),
-			       "__go_int_to_string",
-			       1,
-			       type_tree,
-			       integer_type_node,
-			       fold_convert(integer_type_node, expr_tree));
-    }
-  else if (type->is_string_type()
-	   && (expr_type->array_type() != NULL
-	       || (expr_type->points_to() != NULL
-		   && expr_type->points_to()->array_type() != NULL)))
-    {
-      Type* t = expr_type;
-      if (t->points_to() != NULL)
-	{
-	  t = t->points_to();
-	  expr_tree = build_fold_indirect_ref(expr_tree);
-	}
-      if (!DECL_P(expr_tree))
-	expr_tree = save_expr(expr_tree);
-      Array_type* a = t->array_type();
-      Type* e = a->element_type()->forwarded();
-      gcc_assert(e->integer_type() != NULL);
-      tree valptr = fold_convert(const_ptr_type_node,
-				 a->value_pointer_tree(gogo, expr_tree));
-      tree len = a->length_tree(gogo, expr_tree);
-      if (e->integer_type()->is_unsigned()
-	  && e->integer_type()->bits() == 8)
-	{
-	  static tree byte_array_to_string_fndecl;
-	  ret = Gogo::call_builtin(&byte_array_to_string_fndecl,
-				   this->location(),
-				   "__go_byte_array_to_string",
-				   2,
-				   type_tree,
-				   const_ptr_type_node,
-				   valptr,
-				   size_type_node,
-				   len);
-	}
-      else
-	{
-	  gcc_assert(e == Type::lookup_integer_type("int"));
-	  static tree int_array_to_string_fndecl;
-	  ret = Gogo::call_builtin(&int_array_to_string_fndecl,
-				   this->location(),
-				   "__go_int_array_to_string",
-				   2,
-				   type_tree,
-				   const_ptr_type_node,
-				   valptr,
-				   size_type_node,
-				   len);
-	}
-    }
-  else if ((type->is_unsafe_pointer_type()
-	    && expr_type->points_to() != NULL)
-	   || (expr_type->is_unsafe_pointer_type()
-	       && type->points_to() != NULL))
-    ret = fold_convert(type_tree, expr_tree);
-  else if (type->is_unsafe_pointer_type()
-	   && expr_type->integer_type() != NULL)
-    ret = convert_to_pointer(type_tree, expr_tree);
-  else
-    ret = Expression::convert_for_assignment(context, type, expr_type,
-					     expr_tree, this->location());
-
-  return ret;
-}
-
-// Output a type conversion in a constant expression.
-
-void
-Type_conversion_expression::do_export(Export* exp) const
-{
-  exp->write_c_string("convert(");
-  exp->write_type(this->type_);
-  exp->write_c_string(", ");
-  this->expr_->export_expression(exp);
-  exp->write_c_string(")");
-}
-
-// Import a type conversion or a struct construction.
-
-Expression*
-Type_conversion_expression::do_import(Import* imp)
+Make_expression::do_get_tree(Translate_context* context)
 {
-  imp->require_c_string("convert(");
-  Type* type = imp->read_type();
-  imp->require_c_string(", ");
-  Expression* val = Expression::import_expression(imp);
-  imp->require_c_string(")");
-  return Expression::make_cast(type, val, imp->location());
+  return this->type_->make_expression_tree(context, this->args_,
+					   this->location());
 }
 
-// Make a type cast expression.
+// Make a make expression.
 
 Expression*
-Expression::make_cast(Type* type, Expression* val, source_location location)
+Expression::make_make(Type* type, Expression_list* args,
+		      source_location location)
 {
-  if (type->is_error_type() || val->is_error_expression())
-    return Expression::make_error(location);
-  return new Type_conversion_expression(type, val, location);
+  return new Make_expression(type, args, location);
 }
 
 // Construct a struct.
@@ -8176,7 +8216,7 @@ class Struct_construction_expression : p
   }
 
   bool
-  do_address_taken(source_location)
+  do_address_taken(source_location, bool)
   {
     gcc_assert(this->is_constant_struct());
     return true;
@@ -8479,7 +8519,7 @@ protected:
   do_check_types(Gogo*);
 
   bool
-  do_address_taken(source_location)
+  do_address_taken(source_location, bool)
   {
     gcc_assert(this->is_constant_array());
     return true;
@@ -8922,7 +8962,7 @@ class Map_construction_expression : publ
 
   // Should be turned into Heap_composite_expression.
   bool
-  do_address_taken()
+  do_address_taken(source_location, bool)
   { gcc_unreachable(); }
 
   Expression*

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]