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] Add complex numbers


Complex numbers were recently added to the Go language.  I committed
this patch to add support for them to gccgo.

Ian

diff -r 56cbaf257cf7 go/export.cc
--- a/go/export.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/export.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -344,10 +344,13 @@
   this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64);
   this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32);
   this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64);
+  this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64);
+  this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128);
   this->register_builtin_type(gogo, "int", BUILTIN_INT);
   this->register_builtin_type(gogo, "uint", BUILTIN_UINT);
   this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR);
   this->register_builtin_type(gogo, "float", BUILTIN_FLOAT);
+  this->register_builtin_type(gogo, "complex", BUILTIN_COMPLEX);
   this->register_builtin_type(gogo, "bool", BUILTIN_BOOL);
   this->register_builtin_type(gogo, "string", BUILTIN_STRING);
 }
diff -r 56cbaf257cf7 go/export.h
--- a/go/export.h	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/export.h	Wed Mar 10 14:43:36 2010 -0800
@@ -38,8 +38,11 @@
   BUILTIN_FLOAT = -14,
   BUILTIN_BOOL = -15,
   BUILTIN_STRING = -16,
+  BUILTIN_COMPLEX64 = -17,
+  BUILTIN_COMPLEX128 = -18,
+  BUILTIN_COMPLEX = -19,
 
-  SMALLEST_BUILTIN_CODE = -16
+  SMALLEST_BUILTIN_CODE = -19
 };
 
 // This class manages exporting Go declarations.  It handles the main
diff -r 56cbaf257cf7 go/expressions.cc
--- a/go/expressions.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/expressions.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -74,6 +74,24 @@
   return ret;
 }
 
+// If this expression has a constant complex value, return it.
+
+bool
+Expression::complex_constant_value(mpfr_t real, mpfr_t imag,
+				   Type** ptype) const
+{
+  *ptype = NULL;
+  if (this->do_complex_constant_value(real, imag, ptype))
+    return true;
+  Type *t;
+  if (this->float_constant_value(real, &t))
+    {
+      mpfr_set_ui(imag, 0, GMP_RNDN);
+      return true;
+    }
+  return false;
+}
+
 // Traverse the expressions.
 
 int
@@ -607,7 +625,8 @@
     }
   else if (POINTER_TYPE_P(lhs_type_tree)
 	   || INTEGRAL_TYPE_P(lhs_type_tree)
-	   || SCALAR_FLOAT_TYPE_P(lhs_type_tree))
+	   || SCALAR_FLOAT_TYPE_P(lhs_type_tree)
+	   || COMPLEX_FLOAT_TYPE_P(lhs_type_tree))
     return fold_convert_loc(location, lhs_type_tree, rhs_tree);
   else if (TREE_CODE(lhs_type_tree) == RECORD_TYPE
 	   && TREE_CODE(TREE_TYPE(rhs_tree)) == RECORD_TYPE)
@@ -658,6 +677,16 @@
       mpfr_clear(fval);
       return ret;
     }
+  else if (TREE_CODE(type) == COMPLEX_TYPE)
+    {
+      mpfr_t fval;
+      mpfr_init_set_z(fval, val, GMP_RNDN);
+      tree real = Expression::float_constant_tree(fval, TREE_TYPE(type));
+      mpfr_clear(fval);
+      tree imag = build_real_from_int_cst(TREE_TYPE(type),
+					  integer_zero_node);
+      return build_complex(type, real, imag);
+    }
   else
     gcc_unreachable();
 }
@@ -686,6 +715,40 @@
       real_convert(&r2, TYPE_MODE(type), &r1);
       return build_real(type, r2);
     }
+  else if (TREE_CODE(type) == COMPLEX_TYPE)
+    {
+      REAL_VALUE_TYPE r1;
+      real_from_mpfr(&r1, val, TREE_TYPE(type), GMP_RNDN);
+      REAL_VALUE_TYPE r2;
+      real_convert(&r2, TYPE_MODE(TREE_TYPE(type)), &r1);
+      tree imag = build_real_from_int_cst(TREE_TYPE(type),
+					  integer_zero_node);
+      return build_complex(type, build_real(TREE_TYPE(type), r2), imag);
+    }
+  else
+    gcc_unreachable();
+}
+
+// Return a tree for REAL/IMAG in TYPE.
+
+tree
+Expression::complex_constant_tree(mpfr_t real, mpfr_t imag, tree type)
+{
+  if (TREE_CODE(type) == COMPLEX_TYPE)
+    {
+      REAL_VALUE_TYPE r1;
+      real_from_mpfr(&r1, real, TREE_TYPE(type), GMP_RNDN);
+      REAL_VALUE_TYPE r2;
+      real_convert(&r2, TYPE_MODE(TREE_TYPE(type)), &r1);
+
+      REAL_VALUE_TYPE r3;
+      real_from_mpfr(&r3, imag, TREE_TYPE(type), GMP_RNDN);
+      REAL_VALUE_TYPE r4;
+      real_convert(&r4, TYPE_MODE(TREE_TYPE(type)), &r3);
+
+      return build_complex(type, build_real(TREE_TYPE(type), r2),
+			   build_real(TREE_TYPE(type), r4));
+    }
   else
     gcc_unreachable();
 }
@@ -718,6 +781,14 @@
     return true;
   }
 
+  bool
+  do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const
+  {
+    mpfr_set_ui(real, 0, GMP_RNDN);
+    mpfr_set_ui(imag, 0, GMP_RNDN);
+    return true;
+  }
+
   void
   do_discarding_value()
   { }
@@ -1481,6 +1552,10 @@
   static bool
   check_constant(mpz_t val, Type*, source_location);
 
+  // Write VAL to export data.
+  static void
+  export_integer(Export* exp, const mpz_t val);
+
  protected:
   bool
   do_is_constant() const
@@ -1549,7 +1624,8 @@
     ;
   else if (context->type != NULL
 	   && (context->type->integer_type() != NULL
-	       || context->type->float_type() != NULL))
+	       || context->type->float_type() != NULL
+	       || context->type->complex_type() != NULL))
     this->type_ = context->type;
   else if (!context->may_be_abstract)
     this->type_ = Type::lookup_integer_type("int");
@@ -1621,6 +1697,11 @@
       // We are converting to an abstract floating point type.
       type = Type::lookup_float_type("float64")->get_tree(gogo);
     }
+  else if (this->type_ != NULL && this->type_->complex_type() != NULL)
+    {
+      // We are converting to an abstract complex type.
+      type = Type::lookup_complex_type("complex128")->get_tree(gogo);
+    }
   else
     {
       // If we still have an abstract type here, then this is being
@@ -1638,35 +1719,92 @@
   return Expression::integer_constant_tree(this->val_, type);
 }
 
-// Export an integer in a constant expression.
-
-void
-Integer_expression::do_export(Export* exp) const
-{
-  char* s = mpz_get_str(NULL, 10, this->val_);
+// Write VAL to export data.
+
+void
+Integer_expression::export_integer(Export* exp, const mpz_t val)
+{
+  char* s = mpz_get_str(NULL, 10, val);
   exp->write_c_string(s);
   free(s);
+}
+
+// Export an integer in a constant expression.
+
+void
+Integer_expression::do_export(Export* exp) const
+{
+  Integer_expression::export_integer(exp, this->val_);
   // A trailing space lets us reliably identify the end of the number.
   exp->write_c_string(" ");
 }
 
-// Import an integer or floating point value.  This handles both
-// because they both start with digits.
+// Import an integer, floating point, or complex value.  This handles
+// all these types because they all start with digits.
 
 Expression*
 Integer_expression::do_import(Import* imp)
 {
   std::string num = imp->read_identifier();
   imp->require_c_string(" ");
-  if (num.find('.') == std::string::npos && num.find('E') == std::string::npos)
-    {
-      mpz_t val;
-      if (mpz_init_set_str(val, num.c_str(), 10) != 0)
+  if (!num.empty() && num[num.length() - 1] == 'i')
+    {
+      mpfr_t real;
+      size_t plus_pos = num.find('+', 1);
+      size_t minus_pos = num.find('-', 1);
+      size_t pos;
+      if (plus_pos == std::string::npos)
+	pos = minus_pos;
+      else if (minus_pos == std::string::npos)
+	pos = plus_pos;
+      else
 	{
 	  error_at(imp->location(), "bad number in import data: %qs",
 		   num.c_str());
 	  return Expression::make_error(imp->location());
 	}
+      if (pos == std::string::npos)
+	mpfr_set_ui(real, 0, GMP_RNDN);
+      else
+	{
+	  std::string real_str = num.substr(0, pos);
+	  if (mpfr_init_set_str(real, real_str.c_str(), 10, GMP_RNDN) != 0)
+	    {
+	      error_at(imp->location(), "bad number in import data: %qs",
+		       real_str.c_str());
+	      return Expression::make_error(imp->location());
+	    }
+	}
+
+      std::string imag_str;
+      if (pos == std::string::npos)
+	imag_str = num;
+      else
+	imag_str = num.substr(pos);
+      imag_str = imag_str.substr(0, imag_str.size() - 1);
+      mpfr_t imag;
+      if (mpfr_init_set_str(imag, imag_str.c_str(), 10, GMP_RNDN) != 0)
+	{
+	  error_at(imp->location(), "bad number in import data: %qs",
+		   imag_str.c_str());
+	  return Expression::make_error(imp->location());
+	}
+      Expression* ret = Expression::make_complex(&real, &imag, NULL,
+						 imp->location());
+      mpfr_clear(real);
+      mpfr_clear(imag);
+      return ret;
+    }
+  else if (num.find('.') == std::string::npos
+	   && num.find('E') == std::string::npos)
+    {
+      mpz_t val;
+      if (mpz_init_set_str(val, num.c_str(), 10) != 0)
+	{
+	  error_at(imp->location(), "bad number in import data: %qs",
+		   num.c_str());
+	  return Expression::make_error(imp->location());
+	}
       Expression* ret = Expression::make_integer(&val, NULL, imp->location());
       mpz_clear(val);
       return ret;
@@ -1715,6 +1853,10 @@
   static bool
   check_constant(mpfr_t val, Type*, source_location);
 
+  // Write VAL to export data.
+  static void
+  export_float(Export* exp, const mpfr_t val);
+
  protected:
   bool
   do_is_constant() const
@@ -1798,7 +1940,8 @@
     ;
   else if (context->type != NULL
 	   && (context->type->integer_type() != NULL
-	       || context->type->float_type() != NULL))
+	       || context->type->float_type() != NULL
+	       || context->type->complex_type() != NULL))
     this->type_ = context->type;
   else if (!context->may_be_abstract)
     this->type_ = Type::lookup_float_type("float");
@@ -1814,7 +1957,7 @@
 {
   if (type == NULL)
     return true;
-  Float_type* ftype= type->float_type();
+  Float_type* ftype = type->float_type();
   if (ftype == NULL || ftype->is_abstract())
     return true;
 
@@ -1897,18 +2040,29 @@
   return Expression::float_constant_tree(this->val_, type);
 }
 
-// Export a floating point number in a constant expression.
-
-void
-Float_expression::do_export(Export* exp) const
-{
+// Write a floating point number to export data.
+
+void
+Float_expression::export_float(Export *exp, const mpfr_t val)
+{
+  mp_exp_t exponent;
+  char* s = mpfr_get_str(NULL, &exponent, 10, 0, val, GMP_RNDN);
+  if (*s == '-')
+    exp->write_c_string("-");
   exp->write_c_string("0.");
-  mp_exp_t exponent;
-  char* s = mpfr_get_str(NULL, &exponent, 10, 0, this->val_, GMP_RNDN);
-  exp->write_c_string(s);
+  exp->write_c_string(*s == '-' ? s + 1 : s);
+  mpfr_free_str(s);
   char buf[30];
   snprintf(buf, sizeof buf, "E%ld", exponent);
   exp->write_c_string(buf);
+}
+
+// Export a floating point number in a constant expression.
+
+void
+Float_expression::do_export(Export* exp) const
+{
+  Float_expression::export_float(exp, this->val_);
   // A trailing space lets us reliably identify the end of the number.
   exp->write_c_string(" ");
 }
@@ -1921,6 +2075,247 @@
   return new Float_expression(val, type, location);
 }
 
+// Complex numbers.
+
+class Complex_expression : public Expression
+{
+ public:
+  Complex_expression(const mpfr_t* real, const mpfr_t* imag, Type* type,
+		     source_location location)
+    : Expression(EXPRESSION_COMPLEX, location),
+      type_(type)
+  {
+    mpfr_init_set(this->real_, *real, GMP_RNDN);
+    mpfr_init_set(this->imag_, *imag, GMP_RNDN);
+  }
+
+  // Constrain REAL/IMAG to fit into TYPE.
+  static void
+  constrain_complex(mpfr_t real, mpfr_t imag, Type* type);
+
+  // Return whether REAL/IMAG fits in the type.
+  static bool
+  check_constant(mpfr_t real, mpfr_t imag, Type*, source_location);
+
+  // Write REAL/IMAG to export data.
+  static void
+  export_complex(Export* exp, const mpfr_t real, const mpfr_t val);
+
+ protected:
+  bool
+  do_is_constant() const
+  { return true; }
+
+  bool
+  do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
+
+  Type*
+  do_type();
+
+  void
+  do_determine_type(const Type_context*);
+
+  void
+  do_check_types(Gogo*);
+
+  Expression*
+  do_copy()
+  {
+    return Expression::make_complex(&this->real_, &this->imag_, this->type_,
+				    this->location());
+  }
+
+  tree
+  do_get_tree(Translate_context*);
+
+  void
+  do_export(Export*) const;
+
+ private:
+  // The real part.
+  mpfr_t real_;
+  // The imaginary part;
+  mpfr_t imag_;
+  // The type if known.
+  Type* type_;
+};
+
+// Constrain REAL/IMAG to fit into TYPE.
+
+void
+Complex_expression::constrain_complex(mpfr_t real, mpfr_t imag, Type* type)
+{
+  Complex_type* ctype = type->complex_type();
+  if (ctype != NULL && !ctype->is_abstract())
+    {
+      tree type_tree = ctype->type_tree();
+
+      REAL_VALUE_TYPE rvt;
+      real_from_mpfr(&rvt, real, TREE_TYPE(type_tree), GMP_RNDN);
+      real_convert(&rvt, TYPE_MODE(TREE_TYPE(type_tree)), &rvt);
+      mpfr_from_real(real, &rvt, GMP_RNDN);
+
+      real_from_mpfr(&rvt, imag, TREE_TYPE(type_tree), GMP_RNDN);
+      real_convert(&rvt, TYPE_MODE(TREE_TYPE(type_tree)), &rvt);
+      mpfr_from_real(imag, &rvt, GMP_RNDN);
+    }
+}
+
+// Return a complex constant value.
+
+bool
+Complex_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
+					      Type** ptype) const
+{
+  if (this->type_ != NULL)
+    *ptype = this->type_;
+  mpfr_set(real, this->real_, GMP_RNDN);
+  mpfr_set(imag, this->imag_, GMP_RNDN);
+  return true;
+}
+
+// Return the current type.  If we haven't set the type yet, we return
+// an abstract complex type.
+
+Type*
+Complex_expression::do_type()
+{
+  if (this->type_ == NULL)
+    this->type_ = Type::make_abstract_complex_type();
+  return this->type_;
+}
+
+// Set the type of the complex value.  Here we may switch from an
+// abstract type to a real type.
+
+void
+Complex_expression::do_determine_type(const Type_context* context)
+{
+  if (this->type_ != NULL && !this->type_->is_abstract())
+    ;
+  else if (context->type != NULL
+	   && context->type->complex_type() != NULL)
+    this->type_ = context->type;
+  else if (!context->may_be_abstract)
+    this->type_ = Type::lookup_complex_type("complex");
+}
+
+// Return true if the complex value REAL/IMAG fits in the range of the
+// type TYPE.  Otherwise give an error and return false.  TYPE may be
+// NULL.
+
+bool
+Complex_expression::check_constant(mpfr_t real, mpfr_t imag, Type* type,
+				   source_location location)
+{
+  if (type == NULL)
+    return true;
+  Complex_type* ctype = type->complex_type();
+  if (ctype == NULL || ctype->is_abstract())
+    return true;
+
+  mp_exp_t max_exp;
+  switch (ctype->bits())
+    {
+    case 64:
+      max_exp = 128;
+      break;
+    case 128:
+      max_exp = 1024;
+      break;
+    default:
+      gcc_unreachable();
+    }
+
+  // A NaN or Infinity always fits in the range of the type.
+  if (!mpfr_nan_p(real) && !mpfr_inf_p(real) && !mpfr_zero_p(real))
+    {
+      if (mpfr_get_exp(real) > max_exp)
+	{
+	  error_at(location, "complex real part constant overflow");
+	  return false;
+	}
+    }
+
+  if (!mpfr_nan_p(imag) && !mpfr_inf_p(imag) && !mpfr_zero_p(imag))
+    {
+      if (mpfr_get_exp(imag) > max_exp)
+	{
+	  error_at(location, "complex imaginary part constant overflow");
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+// Check the type of a complex value.
+
+void
+Complex_expression::do_check_types(Gogo*)
+{
+  if (this->type_ == NULL)
+    return;
+
+  if (!Complex_expression::check_constant(this->real_, this->imag_,
+					  this->type_, this->location()))
+    this->set_is_error();
+}
+
+// Get a tree for a complex constant.
+
+tree
+Complex_expression::do_get_tree(Translate_context* context)
+{
+  Gogo* gogo = context->gogo();
+  tree type;
+  if (this->type_ != NULL && !this->type_->is_abstract())
+    type = this->type_->get_tree(gogo);
+  else
+    {
+      // If we still have an abstract type here, this this is being
+      // used in a constant expression which didn't get reduced.  We
+      // just use complex128 and hope for the best.
+      type = Type::lookup_complex_type("complex128")->get_tree(gogo);
+    }
+  return Expression::complex_constant_tree(this->real_, this->imag_, type);
+}
+
+// Write REAL/IMAG to export data.
+
+void
+Complex_expression::export_complex(Export* exp, const mpfr_t real,
+				   const mpfr_t imag)
+{
+  if (!mpfr_zero_p(real))
+    {
+      Float_expression::export_float(exp, real);
+      if (mpfr_sgn(imag) > 0)
+	exp->write_c_string("+");
+    }
+  Float_expression::export_float(exp, imag);
+  exp->write_c_string("i");
+}
+
+// Export a complex number in a constant expression.
+
+void
+Complex_expression::do_export(Export* exp) const
+{
+  Complex_expression::export_complex(exp, this->real_, this->imag_);
+  // A trailing space lets us reliably identify the end of the number.
+  exp->write_c_string(" ");
+}
+
+// Make a complex expression.
+
+Expression*
+Expression::make_complex(const mpfr_t* real, const mpfr_t* imag, Type* type,
+			 source_location location)
+{
+  return new Complex_expression(real, imag, type, location);
+}
+
 // A reference to a const in an expression.
 
 class Const_expression : public Expression
@@ -1950,6 +2345,9 @@
   do_float_constant_value(mpfr_t val, Type**) const;
 
   bool
+  do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
+
+  bool
   do_string_constant_value(std::string* val) const
   { return this->constant_->const_value()->expr()->string_constant_value(val); }
 
@@ -2080,6 +2478,35 @@
   return r;
 }
 
+// Return a complex constant value.
+
+bool
+Const_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
+					    Type **ptype) const
+{
+  Type* ctype;
+  if (this->type_ != NULL)
+    ctype = this->type_;
+  else
+    ctype = this->constant_->const_value()->type();
+  if (ctype != NULL && ctype->complex_type() == NULL)
+    return false;
+
+  Type *t;
+  bool r = this->constant_->const_value()->expr()->complex_constant_value(real,
+									  imag,
+									  &t);
+  if (r && ctype != NULL)
+    {
+      if (!Complex_expression::check_constant(real, imag, ctype,
+					      this->location()))
+	return false;
+      Complex_expression::constrain_complex(real, imag, ctype);
+    }
+  *ptype = ctype != NULL ? ctype : t;
+  return r;
+}
+
 // Return the type of the const reference.
 
 Type*
@@ -2106,7 +2533,8 @@
     ;
   else if (context->type != NULL
 	   && (context->type->integer_type() != NULL
-	       || context->type->float_type() != NULL))
+	       || context->type->float_type() != NULL
+	       || context->type->complex_type() != NULL))
     this->type_ = context->type;
   else if (!context->may_be_abstract)
     {
@@ -2223,6 +2651,17 @@
 	  mpfr_clear(fval);
 	  return ret;
 	}
+
+      mpfr_t imag;
+      mpfr_init(imag);
+      if (expr->complex_constant_value(fval, imag, &t))
+	{
+	  tree ret = Expression::complex_constant_tree(fval, imag, type_tree);
+	  mpfr_clear(fval);
+	  mpfr_clear(imag);
+	  return ret;
+	}
+      mpfr_clear(imag);
       mpfr_clear(fval);
     }
 
@@ -2239,6 +2678,8 @@
     ret = fold(convert_to_integer(type_tree, const_tree));
   else if (TREE_CODE(type_tree) == REAL_TYPE)
     ret = fold(convert_to_real(type_tree, const_tree));
+  else if (TREE_CODE(type_tree) == COMPLEX_TYPE)
+    ret = fold(convert_to_complex(type_tree, const_tree));
   else
     gcc_unreachable();
   return ret;
@@ -2383,6 +2824,9 @@
   do_float_constant_value(mpfr_t, Type**) const;
 
   bool
+  do_complex_constant_value(mpfr_t, mpfr_t, Type**) const;
+
+  bool
   do_string_constant_value(std::string*) const;
 
   Type*
@@ -2456,7 +2900,9 @@
 	{
 	  if (!Integer_expression::check_constant(ival, type, location))
 	    mpz_set_ui(ival, 0);
-	  return Expression::make_integer(&ival, type, location);
+	  Expression* ret = Expression::make_integer(&ival, type, location);
+	  mpz_clear(ival);
+	  return ret;
 	}
 
       mpfr_t fval;
@@ -2472,7 +2918,10 @@
 	  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);
+	  Expression* ret = Expression::make_integer(&ival, type, location);
+	  mpfr_clear(fval);
+	  mpz_clear(ival);
+	  return ret;
 	}
       mpfr_clear(fval);
       mpz_clear(ival);
@@ -2488,11 +2937,38 @@
 	  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);
+	  Expression *ret = Expression::make_float(&fval, type, location);
+	  mpfr_clear(fval);
+	  return ret;
 	}
       mpfr_clear(fval);
     }
 
+  if (type->complex_type() != NULL)
+    {
+      mpfr_t real;
+      mpfr_t imag;
+      mpfr_init(real);
+      mpfr_init(imag);
+      Type* dummy;
+      if (val->complex_constant_value(real, imag, &dummy))
+	{
+	  if (!Complex_expression::check_constant(real, imag, type, location))
+	    {
+	      mpfr_set_ui(real, 0, GMP_RNDN);
+	      mpfr_set_ui(imag, 0, GMP_RNDN);
+	    }
+	  Complex_expression::constrain_complex(real, imag, type);
+	  Expression* ret = Expression::make_complex(&real, &imag, type,
+						     location);
+	  mpfr_clear(real);
+	  mpfr_clear(imag);
+	  return ret;
+	}
+      mpfr_clear(real);
+      mpfr_clear(imag);
+    }
+
   return this;
 }
 
@@ -2569,22 +3045,45 @@
     }
   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_);
+  return false;
+}
+
+// Return the constant complex value if there is one.
+
+bool
+Type_conversion_expression::do_complex_constant_value(mpfr_t real,
+						      mpfr_t imag,
+						      Type **ptype) const
+{
+  if (this->type_->complex_type() == NULL)
+    return false;
+
+  mpfr_t rval;
+  mpfr_t ival;
+  mpfr_init(rval);
+  mpfr_init(ival);
+  Type* dummy;
+  if (this->expr_->complex_constant_value(rval, ival, &dummy))
+    {
+      if (!Complex_expression::check_constant(rval, ival, this->type_,
+					      this->location()))
+	{
+	  mpfr_clear(rval);
+	  mpfr_clear(ival);
+	  return false;
+	}
+      mpfr_set(real, rval, GMP_RNDN);
+      mpfr_set(imag, ival, GMP_RNDN);
+      mpfr_clear(rval);
+      mpfr_clear(ival);
+      Complex_expression::constrain_complex(real, imag, this->type_);
       *ptype = this->type_;
       return true;
     }
-  mpz_clear(ival);
-
-  return false;
+  mpfr_clear(rval);
+  mpfr_clear(ival);
+
+  return false;  
 }
 
 // Return the constant string value if there is one.
@@ -2633,6 +3132,9 @@
       && (expr_type->integer_type() != NULL
 	  || expr_type->float_type() != NULL))
     ok = true;
+  else if (type->complex_type() != NULL
+	   && expr_type->complex_type() != NULL)
+    ok = true;
   else if (type->is_string_type())
     {
       if (expr_type->integer_type() != NULL)
@@ -2791,6 +3293,13 @@
       else
 	gcc_unreachable();
     }
+  else if (type->complex_type() != NULL)
+    {
+      if (expr_type->complex_type() != NULL)
+	ret = fold(convert_to_complex(type_tree, expr_tree));
+      else
+	gcc_unreachable();
+    }
   else if (type->is_string_type()
 	   && expr_type->integer_type() != NULL)
     {
@@ -2979,6 +3488,12 @@
   static bool
   eval_float(Operator op, mpfr_t uval, mpfr_t val);
 
+  // Apply unary opcode OP to UREAL/UIMAG, setting REAL/IMAG.  Return
+  // true if this could be done, false if not.
+  static bool
+  eval_complex(Operator op, mpfr_t ureal, mpfr_t uimag, mpfr_t real,
+	       mpfr_t imag);
+
   static Expression*
   do_import(Import*);
 
@@ -2999,6 +3514,9 @@
   bool
   do_float_constant_value(mpfr_t, Type**) const;
 
+  bool
+  do_complex_constant_value(mpfr_t, mpfr_t, Type**) const;
+
   Type*
   do_type();
 
@@ -3123,6 +3641,26 @@
 		ret = Expression::make_float(&val, ftype, loc);
 	      mpfr_clear(val);
 	    }
+	  if (ret != NULL)
+	    {
+	      mpfr_clear(fval);
+	      return ret;
+	    }
+
+	  mpfr_t ival;
+	  mpfr_init(ival);
+	  if (expr->complex_constant_value(fval, ival, &ftype))
+	    {
+	      mpfr_t real;
+	      mpfr_t imag;
+	      mpfr_init(real);
+	      mpfr_init(imag);
+	      if (Unary_expression::eval_complex(op, fval, ival, real, imag))
+		ret = Expression::make_complex(&real, &imag, ftype, loc);
+	      mpfr_clear(real);
+	      mpfr_clear(imag);
+	    }
+	  mpfr_clear(ival);
 	  mpfr_clear(fval);
 	  if (ret != NULL)
 	    return ret;
@@ -3254,6 +3792,33 @@
     }
 }
 
+// Apply unary opcode OP to RVAL/IVAL, setting REAL/IMAG.  Return true
+// if this could be done, false if not.
+
+bool
+Unary_expression::eval_complex(Operator op, mpfr_t rval, mpfr_t ival,
+			       mpfr_t real, mpfr_t imag)
+{
+  switch (op)
+    {
+    case OPERATOR_PLUS:
+      mpfr_set(real, rval, GMP_RNDN);
+      mpfr_set(imag, ival, GMP_RNDN);
+      return true;
+    case OPERATOR_MINUS:
+      mpfr_neg(real, rval, GMP_RNDN);
+      mpfr_neg(imag, ival, GMP_RNDN);
+      return true;
+    case OPERATOR_NOT:
+    case OPERATOR_XOR:
+    case OPERATOR_AND:
+    case OPERATOR_MULT:
+      return false;
+    default:
+      gcc_unreachable();
+    }
+}
+
 // Return the integral constant value of a unary expression, if it has one.
 
 bool
@@ -3289,6 +3854,27 @@
   return ret;
 }
 
+// Return the complex constant value of a unary expression, if it has
+// one.
+
+bool
+Unary_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
+					    Type** ptype) const
+{
+  mpfr_t rval;
+  mpfr_t ival;
+  mpfr_init(rval);
+  mpfr_init(ival);
+  bool ret;
+  if (!this->expr_->complex_constant_value(rval, ival, ptype))
+    ret = false;
+  else
+    ret = Unary_expression::eval_complex(this->op_, rval, ival, real, imag);
+  mpfr_clear(rval);
+  mpfr_clear(ival);
+  return ret;
+}
+
 // Return the type of a unary expression.
 
 Type*
@@ -3373,6 +3959,7 @@
 	Type* type = this->expr_->type();
 	if (type->integer_type() == NULL
 	    && type->float_type() == NULL
+	    && type->complex_type() == NULL
 	    && !type->is_error_type())
 	  this->report_error(_("expected numeric type"));
       }
@@ -3486,14 +4073,14 @@
 	tree type = TREE_TYPE(expr);
 	tree compute_type = excess_precision_type(type);
 	if (compute_type != NULL_TREE)
-	  expr = convert_to_real(compute_type, expr);
+	  expr = ::convert(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);
+	  ret = ::convert(type, ret);
 	return ret;
       }
 
@@ -3683,6 +4270,47 @@
     }
 }
 
+// Compare complex constants according to OP.  Complex numbers may
+// only be compared for equality.
+
+bool
+Binary_expression::compare_complex(Operator op, Type* type,
+				   mpfr_t left_real, mpfr_t left_imag,
+				   mpfr_t right_real, mpfr_t right_imag)
+{
+  bool is_equal;
+  if (type == NULL)
+    is_equal = (mpfr_cmp(left_real, right_real) == 0
+		&& mpfr_cmp(left_imag, right_imag) == 0);
+  else
+    {
+      mpfr_t lr;
+      mpfr_t li;
+      mpfr_init_set(lr, left_real, GMP_RNDN);
+      mpfr_init_set(li, left_imag, GMP_RNDN);
+      mpfr_t rr;
+      mpfr_t ri;
+      mpfr_init_set(rr, right_real, GMP_RNDN);
+      mpfr_init_set(ri, right_imag, GMP_RNDN);
+      Complex_expression::constrain_complex(lr, li, type);
+      Complex_expression::constrain_complex(rr, ri, type);
+      is_equal = mpfr_cmp(lr, rr) == 0 && mpfr_cmp(li, ri) == 0;
+      mpfr_clear(lr);
+      mpfr_clear(li);
+      mpfr_clear(rr);
+      mpfr_clear(ri);
+    }
+  switch (op)
+    {
+    case OPERATOR_EQEQ:
+      return is_equal;
+    case OPERATOR_NOTEQ:
+      return !is_equal;
+    default:
+      gcc_unreachable();
+    }
+}
+
 // 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
@@ -3904,6 +4532,370 @@
   return true;
 }
 
+// Apply binary opcode OP to LEFT_REAL/LEFT_IMAG and
+// RIGHT_REAL/RIGHT_IMAG, setting REAL/IMAG.  Return true if this
+// could be done, false if not.
+
+bool
+Binary_expression::eval_complex(Operator op, Type* left_type,
+				mpfr_t left_real, mpfr_t left_imag,
+				Type *right_type,
+				mpfr_t right_real, mpfr_t right_imag,
+				mpfr_t real, mpfr_t imag,
+				source_location location)
+{
+  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 and must be handled differently.
+      return false;
+    case OPERATOR_PLUS:
+      mpfr_add(real, left_real, right_real, GMP_RNDN);
+      mpfr_add(imag, left_imag, right_imag, GMP_RNDN);
+      break;
+    case OPERATOR_MINUS:
+      mpfr_sub(real, left_real, right_real, GMP_RNDN);
+      mpfr_sub(imag, left_imag, right_imag, GMP_RNDN);
+      break;
+    case OPERATOR_OR:
+    case OPERATOR_XOR:
+    case OPERATOR_AND:
+    case OPERATOR_BITCLEAR:
+      return false;
+    case OPERATOR_MULT:
+      {
+	// You might think that multiplying two complex numbers would
+	// be simple, and you would be right, until you start to think
+	// about getting the right answer for infinity.  If one
+	// operand here is infinity and the other is anything other
+	// than zero or NaN, then we are going to wind up subtracting
+	// two infinity values.  That will give us a NaN, but the
+	// correct answer is infinity.
+
+	mpfr_t lrrr;
+	mpfr_init(lrrr);
+	mpfr_mul(lrrr, left_real, right_real, GMP_RNDN);
+
+	mpfr_t lrri;
+	mpfr_init(lrri);
+	mpfr_mul(lrri, left_real, right_imag, GMP_RNDN);
+
+	mpfr_t lirr;
+	mpfr_init(lirr);
+	mpfr_mul(lirr, left_imag, right_real, GMP_RNDN);
+
+	mpfr_t liri;
+	mpfr_init(liri);
+	mpfr_mul(liri, left_imag, right_imag, GMP_RNDN);
+
+	mpfr_sub(real, lrrr, liri, GMP_RNDN);
+	mpfr_add(imag, lrri, lirr, GMP_RNDN);
+
+	// If we get NaN on both sides, check whether it should really
+	// be infinity.  The rule is that if either side of the
+	// complex number is infinity, then the whole value is
+	// infinity, even if the other side is NaN.  So the only case
+	// we have to fix is the one in which both sides are NaN.
+	if (mpfr_nan_p(real) && mpfr_nan_p(imag)
+	    && (!mpfr_nan_p(left_real) || !mpfr_nan_p(left_imag))
+	    && (!mpfr_nan_p(right_real) || !mpfr_nan_p(right_imag)))
+	  {
+	    bool is_infinity = false;
+
+	    mpfr_t lr;
+	    mpfr_t li;
+	    mpfr_init_set(lr, left_real, GMP_RNDN);
+	    mpfr_init_set(li, left_imag, GMP_RNDN);
+
+	    mpfr_t rr;
+	    mpfr_t ri;
+	    mpfr_init_set(rr, right_real, GMP_RNDN);
+	    mpfr_init_set(ri, right_imag, GMP_RNDN);
+
+	    // If the left side is infinity, then the result is
+	    // infinity.
+	    if (mpfr_inf_p(lr) || mpfr_inf_p(li))
+	      {
+		mpfr_set_ui(lr, mpfr_inf_p(lr) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(lr, lr, left_real, GMP_RNDN);
+		mpfr_set_ui(li, mpfr_inf_p(li) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(li, li, left_imag, GMP_RNDN);
+		if (mpfr_nan_p(rr))
+		  {
+		    mpfr_set_ui(rr, 0, GMP_RNDN);
+		    mpfr_copysign(rr, rr, right_real, GMP_RNDN);
+		  }
+		if (mpfr_nan_p(ri))
+		  {
+		    mpfr_set_ui(ri, 0, GMP_RNDN);
+		    mpfr_copysign(ri, ri, right_imag, GMP_RNDN);
+		  }
+		is_infinity = true;
+	      }
+
+	    // If the right side is infinity, then the result is
+	    // infinity.
+	    if (mpfr_inf_p(rr) || mpfr_inf_p(ri))
+	      {
+		mpfr_set_ui(rr, mpfr_inf_p(rr) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(rr, rr, right_real, GMP_RNDN);
+		mpfr_set_ui(ri, mpfr_inf_p(ri) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(ri, ri, right_imag, GMP_RNDN);
+		if (mpfr_nan_p(lr))
+		  {
+		    mpfr_set_ui(lr, 0, GMP_RNDN);
+		    mpfr_copysign(lr, lr, left_real, GMP_RNDN);
+		  }
+		if (mpfr_nan_p(li))
+		  {
+		    mpfr_set_ui(li, 0, GMP_RNDN);
+		    mpfr_copysign(li, li, left_imag, GMP_RNDN);
+		  }
+		is_infinity = true;
+	      }
+
+	    // If we got an overflow in the intermediate computations,
+	    // then the result is infinity.
+	    if (!is_infinity
+		&& (mpfr_inf_p(lrrr) || mpfr_inf_p(lrri)
+		    || mpfr_inf_p(lirr) || mpfr_inf_p(liri)))
+	      {
+		if (mpfr_nan_p(lr))
+		  {
+		    mpfr_set_ui(lr, 0, GMP_RNDN);
+		    mpfr_copysign(lr, lr, left_real, GMP_RNDN);
+		  }
+		if (mpfr_nan_p(li))
+		  {
+		    mpfr_set_ui(li, 0, GMP_RNDN);
+		    mpfr_copysign(li, li, left_imag, GMP_RNDN);
+		  }
+		if (mpfr_nan_p(rr))
+		  {
+		    mpfr_set_ui(rr, 0, GMP_RNDN);
+		    mpfr_copysign(rr, rr, right_real, GMP_RNDN);
+		  }
+		if (mpfr_nan_p(ri))
+		  {
+		    mpfr_set_ui(ri, 0, GMP_RNDN);
+		    mpfr_copysign(ri, ri, right_imag, GMP_RNDN);
+		  }
+		is_infinity = true;
+	      }
+
+	    if (is_infinity)
+	      {
+		mpfr_mul(lrrr, lr, rr, GMP_RNDN);
+		mpfr_mul(lrri, lr, ri, GMP_RNDN);
+		mpfr_mul(lirr, li, rr, GMP_RNDN);
+		mpfr_mul(liri, li, ri, GMP_RNDN);
+		mpfr_sub(real, lrrr, liri, GMP_RNDN);
+		mpfr_add(imag, lrri, lirr, GMP_RNDN);
+		mpfr_set_inf(real, mpfr_sgn(real));
+		mpfr_set_inf(imag, mpfr_sgn(imag));
+	      }
+
+	    mpfr_clear(lr);
+	    mpfr_clear(li);
+	    mpfr_clear(rr);
+	    mpfr_clear(ri);
+	  }
+
+	mpfr_clear(lrrr);
+	mpfr_clear(lrri);
+	mpfr_clear(lirr);
+	mpfr_clear(liri);				  
+      }
+      break;
+    case OPERATOR_DIV:
+      {
+	// For complex division we want to avoid having an
+	// intermediate overflow turn the whole result in a NaN.  We
+	// scale the values to try to avoid this.
+
+	if (mpfr_zero_p(right_real) && mpfr_zero_p(right_imag))
+	  error_at(location, "division by zero");
+
+	mpfr_t rra;
+	mpfr_t ria;
+	mpfr_init(rra);
+	mpfr_init(ria);
+	mpfr_abs(rra, right_real, GMP_RNDN);
+	mpfr_abs(ria, right_imag, GMP_RNDN);
+	mpfr_t t;
+	mpfr_init(t);
+	mpfr_max(t, rra, ria, GMP_RNDN);
+
+	mpfr_t rr;
+	mpfr_t ri;
+	mpfr_init_set(rr, right_real, GMP_RNDN);
+	mpfr_init_set(ri, right_imag, GMP_RNDN);
+	long ilogbw = 0;
+	if (!mpfr_inf_p(t) && !mpfr_nan_p(t) && !mpfr_zero_p(t))
+	  {
+	    ilogbw = mpfr_get_exp(t);
+	    mpfr_mul_2si(rr, rr, - ilogbw, GMP_RNDN);
+	    mpfr_mul_2si(ri, ri, - ilogbw, GMP_RNDN);
+	  }
+
+	mpfr_t denom;
+	mpfr_init(denom);
+	mpfr_mul(denom, rr, rr, GMP_RNDN);
+	mpfr_mul(t, ri, ri, GMP_RNDN);
+	mpfr_add(denom, denom, t, GMP_RNDN);
+
+	mpfr_mul(real, left_real, rr, GMP_RNDN);
+	mpfr_mul(t, left_imag, ri, GMP_RNDN);
+	mpfr_add(real, real, t, GMP_RNDN);
+	mpfr_div(real, real, denom, GMP_RNDN);
+	mpfr_mul_2si(real, real, - ilogbw, GMP_RNDN);
+
+	mpfr_mul(imag, left_imag, rr, GMP_RNDN);
+	mpfr_mul(t, left_real, ri, GMP_RNDN);
+	mpfr_sub(imag, imag, t, GMP_RNDN);
+	mpfr_div(imag, imag, denom, GMP_RNDN);
+	mpfr_mul_2si(imag, imag, - ilogbw, GMP_RNDN);
+
+	// If we wind up with NaN on both sides, check whether we
+	// should really have infinity.  The rule is that if either
+	// side of the complex number is infinity, then the whole
+	// value is infinity, even if the other side is NaN.  So the
+	// only case we have to fix is the one in which both sides are
+	// NaN.
+	if (mpfr_nan_p(real) && mpfr_nan_p(imag)
+	    && (!mpfr_nan_p(left_real) || !mpfr_nan_p(left_imag))
+	    && (!mpfr_nan_p(right_real) || !mpfr_nan_p(right_imag)))
+	  {
+	    if (mpfr_zero_p(denom))
+	      {
+		mpfr_set_inf(real, mpfr_sgn(rr));
+		mpfr_mul(real, real, left_real, GMP_RNDN);
+		mpfr_set_inf(imag, mpfr_sgn(rr));
+		mpfr_mul(imag, imag, left_imag, GMP_RNDN);
+	      }
+	    else if ((mpfr_inf_p(left_real) || mpfr_inf_p(left_imag))
+		     && mpfr_number_p(rr) && mpfr_number_p(ri))
+	      {
+		mpfr_set_ui(t, mpfr_inf_p(left_real) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(t, t, left_real, GMP_RNDN);
+
+		mpfr_t t2;
+		mpfr_init_set_ui(t2, mpfr_inf_p(left_imag) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(t2, t2, left_imag, GMP_RNDN);
+
+		mpfr_t t3;
+		mpfr_init(t3);
+		mpfr_mul(t3, t, rr, GMP_RNDN);
+
+		mpfr_t t4;
+		mpfr_init(t4);
+		mpfr_mul(t4, t2, ri, GMP_RNDN);
+
+		mpfr_add(t3, t3, t4, GMP_RNDN);
+		mpfr_set_inf(real, mpfr_sgn(t3));
+
+		mpfr_mul(t3, t2, rr, GMP_RNDN);
+		mpfr_mul(t4, t, ri, GMP_RNDN);
+		mpfr_sub(t3, t3, t4, GMP_RNDN);
+		mpfr_set_inf(imag, mpfr_sgn(t3));
+
+		mpfr_clear(t2);
+		mpfr_clear(t3);
+		mpfr_clear(t4);
+	      }
+	    else if ((mpfr_inf_p(right_real) || mpfr_inf_p(right_imag))
+		     && mpfr_number_p(left_real) && mpfr_number_p(left_imag))
+	      {
+		mpfr_set_ui(t, mpfr_inf_p(rr) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(t, t, rr, GMP_RNDN);
+
+		mpfr_t t2;
+		mpfr_init_set_ui(t2, mpfr_inf_p(ri) ? 1 : 0, GMP_RNDN);
+		mpfr_copysign(t2, t2, ri, GMP_RNDN);
+
+		mpfr_t t3;
+		mpfr_init(t3);
+		mpfr_mul(t3, left_real, t, GMP_RNDN);
+
+		mpfr_t t4;
+		mpfr_init(t4);
+		mpfr_mul(t4, left_imag, t2, GMP_RNDN);
+
+		mpfr_add(t3, t3, t4, GMP_RNDN);
+		mpfr_set_ui(real, 0, GMP_RNDN);
+		mpfr_mul(real, real, t3, GMP_RNDN);
+
+		mpfr_mul(t3, left_imag, t, GMP_RNDN);
+		mpfr_mul(t4, left_real, t2, GMP_RNDN);
+		mpfr_sub(t3, t3, t4, GMP_RNDN);
+		mpfr_set_ui(imag, 0, GMP_RNDN);
+		mpfr_mul(imag, imag, t3, GMP_RNDN);
+
+		mpfr_clear(t2);
+		mpfr_clear(t3);
+		mpfr_clear(t4);
+	      }
+	  }
+
+	mpfr_clear(denom);
+	mpfr_clear(rr);
+	mpfr_clear(ri);
+	mpfr_clear(t);
+	mpfr_clear(rra);
+	mpfr_clear(ria);
+      }
+      break;
+    case OPERATOR_MOD:
+      return false;
+    case OPERATOR_LSHIFT:
+    case OPERATOR_RSHIFT:
+      return false;
+    default:
+      gcc_unreachable();
+    }
+
+  Type* type = left_type;
+  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 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 != NULL && !type->is_abstract())
+    {
+      if ((type != left_type
+	   && !Complex_expression::check_constant(left_real, left_imag,
+						  type, location))
+	  || (type != right_type
+	      && !Complex_expression::check_constant(right_real, right_imag,
+						     type, location))
+	  || !Complex_expression::check_constant(real, imag, type,
+						 location))
+	{
+	  mpfr_set_ui(real, 0, GMP_RNDN);
+	  mpfr_set_ui(imag, 0, GMP_RNDN);
+	}
+    }
+
+  return true;
+}
+
 // Lower a binary expression.  We have to evaluate constant
 // expressions now, in order to implement Go's unlimited precision
 // constants.
@@ -3983,6 +4975,10 @@
 		  type = left_type;
 		else if (right_type->float_type() != NULL)
 		  type = right_type;
+		else if (left_type->complex_type() != NULL)
+		  type = left_type;
+		else if (right_type->complex_type() != NULL)
+		  type = right_type;
 		else
 		  type = left_type;
 		ret = Expression::make_integer(&val, type, location);
@@ -4049,7 +5045,7 @@
 		else if (right_type == NULL)
 		  type = left_type;
 		else if (!left_type->is_abstract()
-		    && left_type->named_type() != NULL)
+			 && left_type->named_type() != NULL)
 		  type = left_type;
 		else if (!right_type->is_abstract()
 			 && right_type->named_type() != NULL)
@@ -4081,6 +5077,103 @@
     mpfr_clear(left_val);
   }
 
+  // Complex constant expressions.
+  {
+    mpfr_t left_real;
+    mpfr_t left_imag;
+    mpfr_init(left_real);
+    mpfr_init(left_imag);
+    Type* left_type;
+
+    mpfr_t right_real;
+    mpfr_t right_imag;
+    mpfr_init(right_real);
+    mpfr_init(right_imag);
+    Type* right_type;
+
+    if (left->complex_constant_value(left_real, left_imag, &left_type)
+	&& right->complex_constant_value(right_real, right_imag, &right_type))
+      {
+	Expression* ret = NULL;
+	if (left_type != right_type
+	    && left_type != NULL
+	    && right_type != NULL
+	    && left_type->base() != right_type->base())
+	  {
+	    // May be a type error--let it be diagnosed later.
+	  }
+	else if (is_comparison)
+	  {
+	    bool b = Binary_expression::compare_complex(op,
+							(left_type != NULL
+							 ? left_type
+							 : right_type),
+							left_real,
+							left_imag,
+							right_real,
+							right_imag);
+	    ret = Expression::make_boolean(b, location);
+	  }
+	else
+	  {
+	    mpfr_t real;
+	    mpfr_t imag;
+	    mpfr_init(real);
+	    mpfr_init(imag);
+
+	    if (Binary_expression::eval_complex(op, left_type,
+						left_real, left_imag,
+						right_type,
+						right_real, right_imag,
+						real, imag,
+						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->complex_type() != NULL)
+		  type = left_type;
+		else if (right_type->complex_type() != NULL)
+		  type = right_type;
+		else
+		  type = left_type;
+		ret = Expression::make_complex(&real, &imag, type,
+					       location);
+	      }
+	    mpfr_clear(real);
+	    mpfr_clear(imag);
+	  }
+
+	if (ret != NULL)
+	  {
+	    mpfr_clear(left_real);
+	    mpfr_clear(left_imag);
+	    mpfr_clear(right_real);
+	    mpfr_clear(right_imag);
+	    return ret;
+	  }
+      }
+
+    mpfr_clear(left_real);
+    mpfr_clear(left_imag);
+    mpfr_clear(right_real);
+    mpfr_clear(right_imag);
+  }
+
   // String constant expressions.
   if (op == OPERATOR_PLUS
       && left->type()->is_string_type()
@@ -4140,9 +5233,7 @@
   mpz_clear(left_val);
 
   if (ret)
-    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
-	      ? right_type
-	      : left_type);
+    *ptype = left_type;
 
   return ret;
 }
@@ -4186,9 +5277,64 @@
   mpfr_clear(right_val);
 
   if (ret)
-    *ptype = (this->op_ == OPERATOR_OROR || this->op_ == OPERATOR_OROR
-	      ? right_type
-	      : left_type);
+    *ptype = left_type;
+
+  return ret;
+}
+
+// Return the complex constant value, if it has one.
+
+bool
+Binary_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
+					     Type** ptype) const
+{
+  mpfr_t left_real;
+  mpfr_t left_imag;
+  mpfr_init(left_real);
+  mpfr_init(left_imag);
+  Type* left_type;
+  if (!this->left_->complex_constant_value(left_real, left_imag, &left_type))
+    {
+      mpfr_clear(left_real);
+      mpfr_clear(left_imag);
+      return false;
+    }
+
+  mpfr_t right_real;
+  mpfr_t right_imag;
+  mpfr_init(right_real);
+  mpfr_init(right_imag);
+  Type* right_type;
+  if (!this->right_->complex_constant_value(right_real, right_imag,
+					    &right_type))
+    {
+      mpfr_clear(left_real);
+      mpfr_clear(left_imag);
+      mpfr_clear(right_real);
+      mpfr_clear(right_imag);
+      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_complex(this->op_, left_type,
+					  left_real, left_imag,
+					  right_type,
+					  right_real, right_imag,
+					  real, imag,
+					  this->location());
+  mpfr_clear(left_real);
+  mpfr_clear(left_imag);
+  mpfr_clear(right_real);
+  mpfr_clear(right_imag);
+
+  if (ret)
+    *ptype = left_type;
 
   return ret;
 }
@@ -4241,6 +5387,10 @@
 	  return left_type;
 	else if (!right_type->is_abstract())
 	  return right_type;
+	else if (left_type->complex_type() != NULL)
+	  return left_type;
+	else if (right_type->complex_type() != NULL)
+	  return right_type;
 	else if (left_type->float_type() != NULL)
 	  return left_type;
 	else if (right_type->float_type() != NULL)
@@ -4308,19 +5458,23 @@
   else if (subcontext.type == NULL)
     {
       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
+	  || (tleft->float_type() != NULL && tright->float_type() != NULL)
+	  || (tleft->complex_type() != NULL && tright->complex_type() != NULL))
+	{
+	  // Both sides have an abstract integer, abstract float, or
+	  // abstract complex 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;
-	}
+      else if (tleft->complex_type() != NULL)
+	subcontext.type = tleft;
+      else if (tright->complex_type() != NULL)
+	subcontext.type = tright;
+      else if (tleft->float_type() != NULL)
+	subcontext.type = tleft;
+      else if (tright->float_type() != NULL)
+	subcontext.type = tright;
+      else
+	subcontext.type = tleft;
     }
 
   this->left_->determine_type(&subcontext);
@@ -4359,6 +5513,7 @@
     case OPERATOR_NOTEQ:
       if (type->integer_type() == NULL
 	  && type->float_type() == NULL
+	  && type->complex_type() == NULL
 	  && !type->is_string_type()
 	  && type->points_to() == NULL
 	  && !type->is_nil_type()
@@ -4371,8 +5526,9 @@
 	  && type->function_type() == NULL)
 	{
 	  error_at(location,
-		   _("expected integer, floating, string, pointer, boolean, "
-		     "interface, slice, map, channel, or function type"));
+		   _("expected integer, floating, complex, string, pointer, "
+		     "boolean, interface, slice, map, channel, "
+		     "or function type"));
 	  return false;
 	}
       break;
@@ -4381,14 +5537,25 @@
     case OPERATOR_LE:
     case OPERATOR_GT:
     case OPERATOR_GE:
+      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;
+
     case OPERATOR_PLUS:
     case OPERATOR_PLUSEQ:
       if (type->integer_type() == NULL
 	  && type->float_type() == NULL
+	  && type->complex_type() == NULL
 	  && !type->is_string_type())
 	{
 	  error_at(location,
-		   _("expected integer, floating, or string type"));
+		   _("expected integer, floating, complex, or string type"));
 	  return false;
 	}
       break;
@@ -4400,9 +5567,10 @@
     case OPERATOR_DIV:
     case OPERATOR_DIVEQ:
       if (type->integer_type() == NULL
-	  && type->float_type() == NULL)
-	{
-	  error_at(location, _("expected integer or floating type"));
+	  && type->float_type() == NULL
+	  && type->complex_type() == NULL)
+	{
+	  error_at(location, _("expected integer, floating, or complex type"));
 	  return false;
 	}
       break;
@@ -4559,11 +5727,15 @@
       code = MULT_EXPR;
       break;
     case OPERATOR_DIV:
-      // FIXME: Code depends on whether integer or floating point.
-      code = TRUNC_DIV_EXPR;
+      {
+	Type *t = this->left_->type();
+	if (t->float_type() != NULL || t->complex_type() != NULL)
+	  code = RDIV_EXPR;
+	else
+	  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:
@@ -4606,8 +5778,8 @@
   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);
+      left = ::convert(compute_type, left);
+      right = ::convert(compute_type, right);
     }
 
   tree eval_saved = NULL_TREE;
@@ -4628,7 +5800,7 @@
 			     left, right);
 
   if (compute_type != NULL_TREE)
-    ret = convert_to_real(type, ret);
+    ret = ::convert(type, ret);
 
   // 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.
@@ -5074,6 +6246,12 @@
   bool
   do_integer_constant_value(bool, mpz_t, Type**) const;
 
+  bool
+  do_float_constant_value(mpfr_t, Type**) const;
+
+  bool
+  do_complex_constant_value(mpfr_t, mpfr_t, Type**) const;
+
   Type*
   do_type();
 
@@ -5107,7 +6285,9 @@
       BUILTIN_CAP,
       BUILTIN_CLOSE,
       BUILTIN_CLOSED,
+      BUILTIN_CMPLX,
       BUILTIN_COPY,
+      BUILTIN_IMAG,
       BUILTIN_LEN,
       BUILTIN_MAKE,
       BUILTIN_NEW,
@@ -5115,6 +6295,7 @@
       BUILTIN_PANICLN,
       BUILTIN_PRINT,
       BUILTIN_PRINTLN,
+      BUILTIN_REAL,
 
       // Builtin functions from the unsafe package.
       BUILTIN_ALIGNOF,
@@ -5128,6 +6309,12 @@
   bool
   check_one_arg();
 
+  static Type*
+  real_imag_type(Type*);
+
+  static Type*
+  cmplx_type(Type*);
+
   // A pointer back to the general IR structure.  This avoids a global
   // variable, or passing it around everywhere.
   Gogo* gogo_;
@@ -5151,8 +6338,12 @@
     this->code_ = BUILTIN_CLOSE;
   else if (name == "closed")
     this->code_ = BUILTIN_CLOSED;
+  else if (name == "cmplx")
+    this->code_ = BUILTIN_CMPLX;
   else if (name == "copy")
     this->code_ = BUILTIN_COPY;
+  else if (name == "imag")
+    this->code_ = BUILTIN_IMAG;
   else if (name == "len")
     this->code_ = BUILTIN_LEN;
   else if (name == "make")
@@ -5167,6 +6358,8 @@
     this->code_ = BUILTIN_PRINT;
   else if (name == "println")
     this->code_ = BUILTIN_PRINTLN;
+  else if (name == "real")
+    this->code_ = BUILTIN_REAL;
   else if (name == "Alignof")
     this->code_ = BUILTIN_ALIGNOF;
   else if (name == "Offsetof")
@@ -5246,9 +6439,80 @@
 	  return ret;
 	}
       mpz_clear(ival);
-    }
-
-  return this;
+
+      mpfr_t rval;
+      mpfr_init(rval);
+      if (this->float_constant_value(rval, &type))
+	{
+	  Expression* ret = Expression::make_float(&rval, type,
+						   this->location());
+	  mpfr_clear(rval);
+	  return ret;
+	}
+
+      mpfr_t imag;
+      mpfr_init(imag);
+      if (this->complex_constant_value(rval, imag, &type))
+	{
+	  Expression* ret = Expression::make_complex(&rval, &imag, type,
+						     this->location());
+	  mpfr_clear(rval);
+	  mpfr_clear(imag);
+	  return ret;
+	}
+      mpfr_clear(rval);
+      mpfr_clear(imag);
+    }
+
+  return this;
+}
+
+// Return the type of the real or imag functions, given the type of
+// the argument.  We need to map complex to float, complex64 to
+// float32, and complex128 to float64, so it has to be done by name.
+// This returns NULL if it can't figure out the type.
+
+Type*
+Builtin_call_expression::real_imag_type(Type* arg_type)
+{
+  if (arg_type == NULL || arg_type->is_abstract())
+    return NULL;
+  Named_type* nt = arg_type->named_type();
+  if (nt == NULL)
+    return NULL;
+  while (nt->real_type()->named_type() != NULL)
+    nt = nt->real_type()->named_type();
+  if (nt->name() == "complex")
+    return Type::lookup_float_type("float");
+  else if (nt->name() == "complex64")
+    return Type::lookup_float_type("float32");
+  else if (nt->name() == "complex128")
+    return Type::lookup_float_type("float64");
+  else
+    return NULL;
+}
+
+// Return the type of the cmplx function, given the type of one of the
+// argments.  Like real_imag_type, we have to map by name.
+
+Type*
+Builtin_call_expression::cmplx_type(Type* arg_type)
+{
+  if (arg_type == NULL || arg_type->is_abstract())
+    return NULL;
+  Named_type* nt = arg_type->named_type();
+  if (nt == NULL)
+    return NULL;
+  while (nt->real_type()->named_type() != NULL)
+    nt = nt->real_type()->named_type();
+  if (nt->name() == "float")
+    return Type::lookup_complex_type("complex");
+  else if (nt->name() == "float32")
+    return Type::lookup_complex_type("complex64");
+  else if (nt->name() == "float64")
+    return Type::lookup_complex_type("complex128");
+  else
+    return NULL;
 }
 
 // Return a single argument, or NULL if there isn't one.
@@ -5268,36 +6532,61 @@
 bool
 Builtin_call_expression::do_is_constant() const
 {
-  if (this->code_ == BUILTIN_LEN
-      || this->code_ == BUILTIN_CAP)
-    {
-      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();
-    }
-  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;
-    }
+  switch (this->code_)
+    {
+    case BUILTIN_LEN:
+    case BUILTIN_CAP:
+      {
+	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();
+      }
+      break;
+
+    case BUILTIN_SIZEOF:
+    case BUILTIN_ALIGNOF:
+      return this->one_arg() != NULL;
+
+    case BUILTIN_OFFSETOF:
+      {
+	Expression* arg = this->one_arg();
+	if (arg == NULL)
+	  return false;
+	return arg->field_reference_expression() != NULL;
+      }
+
+    case BUILTIN_CMPLX:
+      {
+	const Expression_list* args = this->args();
+	if (args != NULL && args->size() == 2)
+	  return args->front()->is_constant() && args->back()->is_constant();
+      }
+      break;
+
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      {
+	Expression* arg = this->one_arg();
+	return arg != NULL && arg->is_constant();
+      }
+
+    default:
+      break;
+    }
+
   return false;
 }
 
@@ -5427,6 +6716,87 @@
   return false;
 }
 
+// Return a floating point constant value if possible.
+
+bool
+Builtin_call_expression::do_float_constant_value(mpfr_t val,
+						 Type** ptype) const
+{
+  if (this->code_ == BUILTIN_REAL || this->code_ == BUILTIN_IMAG)
+    {
+      Expression* arg = this->one_arg();
+      if (arg == NULL)
+	return false;
+
+      mpfr_t real;
+      mpfr_t imag;
+      mpfr_init(real);
+      mpfr_init(imag);
+
+      bool ret = false;
+      Type* type;
+      if (arg->complex_constant_value(real, imag, &type))
+	{
+	  if (this->code_ == BUILTIN_REAL)
+	    mpfr_set(val, real, GMP_RNDN);
+	  else
+	    mpfr_set(val, imag, GMP_RNDN);
+	  *ptype = Builtin_call_expression::real_imag_type(type);
+	  ret = true;
+	}
+
+      mpfr_clear(real);
+      mpfr_clear(imag);
+      return ret;
+    }
+
+  return false;
+}
+
+// Return a complex constant value if possible.
+
+bool
+Builtin_call_expression::do_complex_constant_value(mpfr_t real, mpfr_t imag,
+						   Type** ptype) const
+{
+  if (this->code_ == BUILTIN_CMPLX)
+    {
+      const Expression_list* args = this->args();
+      if (args == NULL || args->size() != 2)
+	return false;
+
+      mpfr_t r;
+      mpfr_init(r);
+      Type* rtype;
+      if (!args->front()->float_constant_value(r, &rtype))
+	{
+	  mpfr_clear(r);
+	  return false;
+	}
+
+      mpfr_t i;
+      mpfr_init(i);
+
+      bool ret = false;
+      Type* itype;
+      if (args->back()->float_constant_value(i, &itype)
+	  && Type::are_identical(rtype, itype))
+	{
+	  mpfr_set(real, r, GMP_RNDN);
+	  mpfr_set(imag, i, GMP_RNDN);
+	  *ptype = Builtin_call_expression::cmplx_type(rtype);
+	  ret = true;
+	}
+
+      mpfr_clear(r);
+      mpfr_clear(i);
+
+      return ret;
+    }
+
+  return false;
+}
+
 // Return the type.
 
 Type*
@@ -5464,17 +6834,51 @@
 
     case BUILTIN_CLOSED:
       return Type::lookup_bool_type();
+
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      {
+	Expression* arg = this->one_arg();
+	if (arg == NULL)
+	  return Type::make_error_type();
+	Type* t = arg->type();
+	if (t->is_abstract())
+	  t = t->make_non_abstract_type();
+	t = Builtin_call_expression::real_imag_type(t);
+	if (t == NULL)
+	  t = Type::make_error_type();
+	return t;
+      }
+
+    case BUILTIN_CMPLX:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->size() != 2)
+	  return Type::make_error_type();
+	Type* t = args->front()->type();
+	if (t->is_abstract())
+	  {
+	    t = args->back()->type();
+	    if (t->is_abstract())
+	      t = t->make_non_abstract_type();
+	  }
+	t = Builtin_call_expression::cmplx_type(t);
+	if (t == NULL)
+	  t = Type::make_error_type();
+	return t;
+      }
     }
 }
 
 // Determine the type.
 
 void
-Builtin_call_expression::do_determine_type(const Type_context*)
+Builtin_call_expression::do_determine_type(const Type_context* context)
 {
   this->fn()->determine_type_no_context();
 
   bool is_print;
+  Type* arg_type = NULL;
   switch (this->code_)
     {
     case BUILTIN_PANIC:
@@ -5485,6 +6889,17 @@
       is_print = true;
       break;
 
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      arg_type = Builtin_call_expression::cmplx_type(context->type);
+      is_print = false;
+      break;
+
+    case BUILTIN_CMPLX:
+      arg_type = Builtin_call_expression::real_imag_type(context->type);
+      is_print = false;
+      break;
+
     default:
       is_print = false;
       break;
@@ -5498,6 +6913,7 @@
 	   ++pa)
 	{
 	  Type_context subcontext;
+	  subcontext.type = arg_type;
 
 	  if (is_print)
 	    {
@@ -5505,7 +6921,7 @@
 	      // use the appropriate nonabstract type.  Use uint64 for
 	      // an integer if we know it is nonnegative, otherwise
 	      // use int64 for a integer, otherwise use float64 for a
-	      // float.
+	      // float or complex128 for a complex.
 	      Type* want_type = NULL;
 	      Type* atype = (*pa)->type();
 	      if (atype->is_abstract())
@@ -5522,11 +6938,12 @@
 			want_type = Type::lookup_integer_type("int64");
 		      mpz_clear(val);
 		    }
+		  else if (atype->float_type() != NULL)
+		    want_type = Type::lookup_float_type("float64");
+		  else if (atype->complex_type() != NULL)
+		    want_type = Type::lookup_complex_type("complex128");
 		  else
-		    {
-		      gcc_assert(atype->float_type() != NULL);
-		      want_type = Type::lookup_float_type("float64");
-		    }
+		    gcc_unreachable();
 		  subcontext.type = want_type;
 		}
 	    }
@@ -5634,6 +7051,7 @@
 		    || type->is_string_type()
 		    || type->integer_type() != NULL
 		    || type->float_type() != NULL
+		    || type->complex_type() != NULL
 		    || type->is_boolean_type()
 		    || type->points_to() != NULL
 		    || type->interface_type() != NULL
@@ -5722,6 +7140,35 @@
       }
       break;
 
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      if (this->check_one_arg())
+	{
+	  if (this->one_arg()->type()->complex_type() == NULL)
+	    this->report_error(_("argument must have complex type"));
+	}
+      break;
+
+    case BUILTIN_CMPLX:
+      {
+	const Expression_list* args = this->args();
+	if (args == NULL || args->size() < 2)
+	  this->report_error(_("not enough arguments"));
+	else if (args->size() > 2)
+	  this->report_error(_("too many arguments"));
+	else if (args->front()->is_error_expression()
+		 || args->front()->type()->is_error_type()
+		 || args->back()->is_error_expression()
+		 || args->back()->type()->is_error_type())
+	  this->set_is_error();
+	else if (!Type::are_identical(args->front()->type(),
+				      args->back()->type()))
+	  this->report_error(_("arguments must have identical types"));
+	else if (args->front()->type()->float_type() == NULL)
+	  this->report_error(_("arguments must have floating-point type"));
+      }
+      break;
+
     default:
       gcc_unreachable();
     }
@@ -5892,6 +7339,14 @@
 		    fnname = "__go_print_double";
 		    arg = fold_convert_loc(location, double_type_node, arg);
 		  }
+		else if (type->complex_type() != NULL)
+		  {
+		    static tree print_complex_fndecl;
+		    pfndecl = &print_complex_fndecl;
+		    fnname = "__go_print_complex";
+		    arg = fold_convert_loc(location, complex_double_type_node,
+					   arg);
+		  }
 		else if (type->is_boolean_type())
 		  {
 		    static tree print_bool_fndecl;
@@ -6086,6 +7541,42 @@
 			       call, len);
       }
 
+    case BUILTIN_REAL:
+    case BUILTIN_IMAG:
+      {
+	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;
+	gcc_assert(COMPLEX_FLOAT_TYPE_P(TREE_TYPE(arg_tree)));
+	if (this->code_ == BUILTIN_REAL)
+	  return fold_build1_loc(location, REALPART_EXPR,
+				 TREE_TYPE(TREE_TYPE(arg_tree)),
+				 arg_tree);
+	else
+	  return fold_build1_loc(location, IMAGPART_EXPR,
+				 TREE_TYPE(TREE_TYPE(arg_tree)),
+				 arg_tree);
+      }
+
+    case BUILTIN_CMPLX:
+      {
+	const Expression_list* args = this->args();
+	gcc_assert(args != NULL && args->size() == 2);
+	tree r = args->front()->get_tree(context);
+	tree i = args->back()->get_tree(context);
+	if (r == error_mark_node || i == error_mark_node)
+	  return error_mark_node;
+	gcc_assert(TYPE_MAIN_VARIANT(TREE_TYPE(r))
+		   == TYPE_MAIN_VARIANT(TREE_TYPE(i)));
+	gcc_assert(SCALAR_FLOAT_TYPE_P(TREE_TYPE(r)));
+	return fold_build2_loc(location, COMPLEX_EXPR,
+			       build_complex_type(TREE_TYPE(r)),
+			       r, i);
+      }
+
     default:
       gcc_unreachable();
     }
@@ -6097,18 +7588,53 @@
 void
 Builtin_call_expression::do_export(Export* exp) const
 {
+  bool ok = false;
+
   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);
+  if (this->integer_constant_value(true, val, &dummy))
+    {
+      Integer_expression::export_integer(exp, val);
+      ok = true;
+    }
+  mpz_clear(val);
+
+  if (!ok)
+    {
+      mpfr_t fval;
+      mpfr_init(fval);
+      if (this->float_constant_value(fval, &dummy))
+	{
+	  Float_expression::export_float(exp, fval);
+	  ok = true;
+	}
+      mpfr_clear(fval);
+    }
+
+  if (!ok)
+    {
+      mpfr_t real;
+      mpfr_t imag;
+      mpfr_init(real);
+      mpfr_init(imag);
+      if (this->complex_constant_value(real, imag, &dummy))
+	{
+	  Complex_expression::export_complex(exp, real, imag);
+	  ok = true;
+	}
+      mpfr_clear(real);
+      mpfr_clear(imag);
+    }
+
+  if (!ok)
+    {
+      error_at(this->location(), "value is not constant");
+      return;
+    }
+
   // A trailing space lets us reliably identify the end of the number.
   exp->write_c_string(" ");
-  mpz_clear(val);
 }
 
 // Class Call_expression.
@@ -6813,8 +8339,10 @@
       && DECL_IS_BUILTIN(fndecl)
       && DECL_BUILT_IN_CLASS(fndecl) == BUILT_IN_NORMAL
       && nargs > 0
-      && SCALAR_FLOAT_TYPE_P(rettype)
-      && SCALAR_FLOAT_TYPE_P(TREE_TYPE(args[0])))
+      && ((SCALAR_FLOAT_TYPE_P(rettype)
+	   && SCALAR_FLOAT_TYPE_P(TREE_TYPE(args[0])))
+	  || (COMPLEX_FLOAT_TYPE_P(rettype)
+	      && COMPLEX_FLOAT_TYPE_P(TREE_TYPE(args[0])))))
     {
       excess_type = excess_precision_type(TREE_TYPE(args[0]));
       if (excess_type != NULL_TREE)
@@ -6827,10 +8355,7 @@
 	    {
 	      fn = build_fold_addr_expr_loc(location, excess_fndecl);
 	      for (int i = 0; i < nargs; ++i)
-		{
-		  if (SCALAR_FLOAT_TYPE_P(TREE_TYPE(args[i])))
-		    args[i] = convert_to_real(excess_type, args[i]);
-		}
+		args[i] = ::convert(excess_type, args[i]);
 	    }
 	}
     }
@@ -6850,8 +8375,8 @@
 
   if (excess_type != NULL_TREE)
     {
-      // Calling convert_to_real here can undo our excess precision
-      // change.  That may or may not be a bug in convert_to_real.
+      // Calling convert here can undo our excess precision change.
+      // That may or may not be a bug in convert_to_real.
       ret = build1(NOP_EXPR, rettype, ret);
     }
 
@@ -10892,7 +12417,7 @@
     return String_expression::do_import(imp);
   else if (c == '-' || (c >= '0' && c <= '9'))
     {
-      // This handles both integers and floats.
+      // This handles integers, floats and complex constants.
       return Integer_expression::do_import(imp);
     }
   else if (imp->match_c_string("nil"))
diff -r 56cbaf257cf7 go/expressions.h
--- a/go/expressions.h	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/expressions.h	Wed Mar 10 14:43:36 2010 -0800
@@ -66,6 +66,7 @@
     EXPRESSION_STRING,
     EXPRESSION_INTEGER,
     EXPRESSION_FLOAT,
+    EXPRESSION_COMPLEX,
     EXPRESSION_NIL,
     EXPRESSION_IOTA,
     EXPRESSION_CALL,
@@ -162,6 +163,11 @@
   static Expression*
   make_float(const mpfr_t*, Type*, source_location);
 
+  // Make a constant complex expression.  TYPE should be NULL for an
+  // abstract type.
+  static Expression*
+  make_complex(const mpfr_t* real, const mpfr_t* imag, Type*, source_location);
+
   // Make a nil expression.
   static Expression*
   make_nil(source_location);
@@ -314,6 +320,14 @@
   bool
   float_constant_value(mpfr_t val, Type** ptype) const;
 
+  // If this is not a constant expression with complex type, return
+  // false.  If it is one, return true, and set REAL and IMAG to the
+  // value.  REAL and IMAG should already be initialized.  If this
+  // return strue, it sets *PTYPE to the type of the value, or NULL
+  // for an abstract type.
+  bool
+  complex_constant_value(mpfr_t real, mpfr_t imag, Type** ptype) const;
+
   // If this is not a constant expression with string type, return
   // false.  If it is one, return true, and set VAL to the value.
   bool
@@ -507,8 +521,9 @@
   lower(Gogo* gogo, Named_object* function, int iota_value)
   { return this->do_lower(gogo, function, iota_value); }
 
-  // Determine the real type of an expression with abstract integer or
-  // floating point type.  TYPE_CONTEXT describes the expected type.
+  // Determine the real type of an expression with abstract integer,
+  // floating point, or complex type.  TYPE_CONTEXT describes the
+  // expected type.
   void
   determine_type(const Type_context*);
 
@@ -613,6 +628,10 @@
   static tree
   float_constant_tree(mpfr_t val, tree type);
 
+  // Return a tree for the complex value REAL/IMAG in TYPE.
+  static tree
+  complex_constant_tree(mpfr_t real, mpfr_t imag, tree type);
+
   // Export the expression.  This is only used for constants.  It will
   // be used for things like values of named constants and sizes of
   // arrays.
@@ -651,6 +670,12 @@
   do_float_constant_value(mpfr_t, Type**) const
   { return false; }
 
+  // Return whether this is a constant expression of complex type, and
+  // set REAL and IMAGE to the value.
+  virtual bool
+  do_complex_constant_value(mpfr_t, mpfr_t, Type**) const
+  { return false; }
+
   // Return whether this is a constant expression of string type, and
   // set VAL to the value.
   virtual bool
@@ -1077,6 +1102,14 @@
 	     Type* right_type, mpfr_t right_val, mpfr_t val,
 	     source_location);
 
+  // Apply binary opcode OP to LEFT_REAL/LEFT_IMAG and
+  // RIGHT_REAL/RIGHT_IMAG, setting REAL/IMAG.  Return true if this
+  // could be done, false if not.
+  static bool
+  eval_complex(Operator op, Type* left_type, mpfr_t left_real,
+	       mpfr_t left_imag, Type* right_type, mpfr_t right_real,
+	       mpfr_t right_imag, mpfr_t real, mpfr_t imag, source_location);
+
   // Compare integer constants according to OP.
   static bool
   compare_integer(Operator op, mpz_t left_val, mpz_t right_val);
@@ -1085,6 +1118,11 @@
   static bool
   compare_float(Operator op, Type* type, mpfr_t left_val, mpfr_t right_val);
 
+  // Compare complex constants according to OP.
+  static bool
+  compare_complex(Operator op, Type* type, mpfr_t left_real, mpfr_t left_imag,
+		  mpfr_t right_val, mpfr_t right_imag);
+
   static Expression*
   do_import(Import*);
 
@@ -1110,6 +1148,9 @@
   bool
   do_float_constant_value(mpfr_t val, Type**) const;
 
+  bool
+  do_complex_constant_value(mpfr_t real, mpfr_t imag, Type**) const;
+
   void
   do_discarding_value();
 
diff -r 56cbaf257cf7 go/go-lang.c
--- a/go/go-lang.c	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/go-lang.c	Wed Mar 10 14:43:36 2010 -0800
@@ -114,6 +114,10 @@
      requirements.  */
   flag_strict_aliasing = 1;
 
+  /* Default to avoiding range issues for complex multiply and
+     divide.  */
+  flag_complex_method = 2;
+
   /* The builtin math functions should not set errno.  */
   flag_errno_math = 0;
 
@@ -285,6 +289,8 @@
       return fold (convert_to_pointer (type, expr));
     case REAL_TYPE:
       return fold (convert_to_real (type, expr));
+    case COMPLEX_TYPE:
+      return fold (convert_to_complex (type, expr));
     default:
       break;
     }
diff -r 56cbaf257cf7 go/gogo-tree.cc
--- a/go/gogo-tree.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/gogo-tree.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -1686,10 +1686,34 @@
 	  type = Type::lookup_float_type("float64");
 	  break;
 	default:
+	  // We have to check for long double in order to support
+	  // i386 excess precision.
+	  if (mode == TYPE_MODE(long_double_type_node))
+	    return long_double_type_node;
 	  return NULL_TREE;
 	}
       return type->float_type()->type_tree();
     }
+  else if (mc == MODE_COMPLEX_FLOAT)
+    {
+      Type *type;
+      switch (GET_MODE_BITSIZE (mode))
+	{
+	case 64:
+	  type = Type::lookup_complex_type("complex64");
+	  break;
+	case 128:
+	  type = Type::lookup_complex_type("complex128");
+	  break;
+	default:
+	  // We have to check for long double in order to support
+	  // i386 excess precision.
+	  if (mode == TYPE_MODE(complex_long_double_type_node))
+	    return complex_long_double_type_node;
+	  return NULL_TREE;
+	}
+      return type->complex_type()->type_tree();
+    }
   else
     return NULL_TREE;
 }
@@ -2111,6 +2135,7 @@
     case Type::TYPE_BOOLEAN:
     case Type::TYPE_INTEGER:
     case Type::TYPE_FLOAT:
+    case Type::TYPE_COMPLEX:
     case Type::TYPE_POINTER:
     case Type::TYPE_FUNCTION:
     case Type::TYPE_CHANNEL:
@@ -3778,7 +3803,8 @@
 		      source_location location)
 {
   if (int_size_in_bytes(TREE_TYPE(val)) <= 8
-      && !AGGREGATE_TYPE_P(TREE_TYPE(val)))
+      && !AGGREGATE_TYPE_P(TREE_TYPE(val))
+      && !FLOAT_TYPE_P(TREE_TYPE(val)))
     {
       val = convert_to_integer(uint64_type_node, val);
       if (blocking)
@@ -3889,7 +3915,8 @@
 			   source_location location)
 {
   if (int_size_in_bytes(type_tree) <= 8
-      && !AGGREGATE_TYPE_P(type_tree))
+      && !AGGREGATE_TYPE_P(type_tree)
+      && !FLOAT_TYPE_P(type_tree))
     {
       static tree receive_small_fndecl;
       tree call = Gogo::call_builtin(&receive_small_fndecl,
diff -r 56cbaf257cf7 go/gogo.cc
--- a/go/gogo.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/gogo.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -63,6 +63,11 @@
   this->add_named_type(Type::make_float_type("float64", 64,
 					     RUNTIME_TYPE_CODE_FLOAT64));
 
+  this->add_named_type(Type::make_complex_type("complex64", 64,
+					       RUNTIME_TYPE_CODE_COMPLEX64));
+  this->add_named_type(Type::make_complex_type("complex128", 128,
+					       RUNTIME_TYPE_CODE_COMPLEX128));
+
   const int int_type_size = std::max(INT_TYPE_SIZE, 32);
   this->add_named_type(Type::make_integer_type("uint", true,
 					       int_type_size,
@@ -85,6 +90,9 @@
   this->add_named_type(Type::make_float_type("float", FLOAT_TYPE_SIZE,
 					     RUNTIME_TYPE_CODE_FLOAT));
 
+  this->add_named_type(Type::make_complex_type("complex", FLOAT_TYPE_SIZE * 2,
+					       RUNTIME_TYPE_CODE_COMPLEX));
+
   this->add_named_type(Type::make_named_bool_type());
 
   this->add_named_type(Type::make_named_string_type());
@@ -181,6 +189,21 @@
   copy_type->set_is_builtin();
   this->globals_->add_function_declaration("copy", NULL, copy_type, loc);
 
+  Function_type* cmplx_type = Type::make_function_type(NULL, NULL, NULL, loc);
+  cmplx_type->set_is_varargs();
+  cmplx_type->set_is_builtin();
+  this->globals_->add_function_declaration("cmplx", NULL, cmplx_type, loc);
+
+  Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc);
+  real_type->set_is_varargs();
+  real_type->set_is_builtin();
+  this->globals_->add_function_declaration("real", NULL, real_type, loc);
+
+  Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc);
+  imag_type->set_is_varargs();
+  imag_type->set_is_builtin();
+  this->globals_->add_function_declaration("imag", NULL, cmplx_type, loc);
+
   this->define_builtin_function_trees();
 
   // Declare "init", to ensure that it is not defined with parameters
@@ -1401,6 +1424,7 @@
   Type* ctype = constant->type();
   if (ctype->integer_type() == NULL
       && ctype->float_type() == NULL
+      && ctype->complex_type() == NULL
       && !ctype->is_boolean_type()
       && !ctype->is_string_type())
     {
diff -r 56cbaf257cf7 go/import.cc
--- a/go/import.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/import.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -632,10 +632,13 @@
   this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64);
   this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32);
   this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64);
+  this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64);
+  this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128);
   this->register_builtin_type(gogo, "int", BUILTIN_INT);
   this->register_builtin_type(gogo, "uint", BUILTIN_UINT);
   this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR);
   this->register_builtin_type(gogo, "float", BUILTIN_FLOAT);
+  this->register_builtin_type(gogo, "complex", BUILTIN_COMPLEX);
   this->register_builtin_type(gogo, "bool", BUILTIN_BOOL);
   this->register_builtin_type(gogo, "string", BUILTIN_STRING);
 }
diff -r 56cbaf257cf7 go/lex.cc
--- a/go/lex.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/lex.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -169,7 +169,8 @@
 {
   if (this->classification_ == TOKEN_INTEGER)
     mpz_clear(this->u_.integer_value);
-  else if (this->classification_ == TOKEN_FLOAT)
+  else if (this->classification_ == TOKEN_FLOAT
+	   || this->classification_ == TOKEN_IMAGINARY)
     mpfr_clear(this->u_.float_value);
 }
 
@@ -197,6 +198,7 @@
       mpz_init_set(this->u_.integer_value, tok.u_.integer_value);
       break;
     case TOKEN_FLOAT:
+    case TOKEN_IMAGINARY:
       mpfr_init_set(this->u_.float_value, tok.u_.float_value, GMP_RNDN);
       break;
     default:
@@ -235,6 +237,7 @@
       mpz_init_set(this->u_.integer_value, tok.u_.integer_value);
       break;
     case TOKEN_FLOAT:
+    case TOKEN_IMAGINARY:
       mpfr_init_set(this->u_.float_value, tok.u_.float_value, GMP_RNDN);
       break;
     default:
@@ -273,6 +276,10 @@
       fprintf(file, "float ");
       mpfr_out_str(file, 10, 0, this->u_.float_value, GMP_RNDN);
       break;
+    case TOKEN_IMAGINARY:
+      fprintf(file, "imaginary ");
+      mpfr_out_str(file, 10, 0, this->u_.float_value, GMP_RNDN);
+      break;
     case TOKEN_OPERATOR:
       fprintf(file, "operator ");
       switch (this->u_.op)
@@ -948,7 +955,7 @@
 	    }
 	}
 
-      if (*p != '.' && *p != 'e' && *p != 'E')
+      if (*p != '.' && *p != 'e' && *p != 'E' && *p != 'i')
 	{
 	  std::string s(pnum, p - pnum);
 	  mpz_t val;
@@ -972,7 +979,7 @@
       ++p;
     }
 
-  if (*p != '.' && *p != 'E' && *p != 'e')
+  if (*p != '.' && *p != 'E' && *p != 'e' && *p != 'i')
     {
       std::string s(pnum, p - pnum);
       mpz_t val;
@@ -988,34 +995,37 @@
       return ret;
     }
 
-  bool dot = *p == '.';
+  if (*p != 'i')
+    {
+      bool dot = *p == '.';
 
-  ++p;
+      ++p;
 
-  if (!dot)
-    {
-      if (*p == '+' || *p == '-')
-	++p;
-    }
+      if (!dot)
+	{
+	  if (*p == '+' || *p == '-')
+	    ++p;
+	}
 
-  while (p < pend)
-    {
-      if (*p < '0' || *p > '9')
-	break;
-      ++p;
-    }
-
-  if (dot && (*p == 'E' || *p == 'e'))
-    {
-      ++p;
-      if (*p == '+' || *p == '-')
-	++p;
       while (p < pend)
 	{
 	  if (*p < '0' || *p > '9')
 	    break;
 	  ++p;
 	}
+
+      if (dot && (*p == 'E' || *p == 'e'))
+	{
+	  ++p;
+	  if (*p == '+' || *p == '-')
+	    ++p;
+	  while (p < pend)
+	    {
+	      if (*p < '0' || *p > '9')
+		break;
+	      ++p;
+	    }
+	}
     }
 
   std::string s(pnum, p - pnum);
@@ -1026,10 +1036,23 @@
   if (neg)
     mpfr_neg(val, val, GMP_RNDN);
 
+  bool is_imaginary = *p == 'i';
+  if (is_imaginary)
+    ++p;
+
   this->lineoff_ = p - this->linebuf_;
-  Token ret = Token::make_float_token(val, location);
-  mpfr_clear(val);
-  return ret;
+  if (is_imaginary)
+    {
+      Token ret = Token::make_imaginary_token(val, location);
+      mpfr_clear(val);
+      return ret;
+    }
+  else
+    {
+      Token ret = Token::make_float_token(val, location);
+      mpfr_clear(val);
+      return ret;
+    }
 }
 
 // Advance one character, possibly escaped.  Return the pointer beyond
diff -r 56cbaf257cf7 go/lex.h
--- a/go/lex.h	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/lex.h	Wed Mar 10 14:43:36 2010 -0800
@@ -72,7 +72,9 @@
     // Token is an integer.
     TOKEN_INTEGER,
     // Token is a floating point number.
-    TOKEN_FLOAT
+    TOKEN_FLOAT,
+    // Token is an imaginary number.
+    TOKEN_IMAGINARY
   };
 
   ~Token();
@@ -152,6 +154,16 @@
     return tok;
   }
 
+  // Make a token for an imaginary number.
+  static Token
+  make_imaginary_token(mpfr_t val, source_location location)
+  {
+    Token tok(TOKEN_IMAGINARY, location);
+    mpfr_init(tok.u_.float_value);
+    mpfr_swap(tok.u_.float_value, val);
+    return tok;
+  }
+
   // Get the location of the token.
   source_location
   location() const
@@ -228,6 +240,14 @@
     return &this->u_.float_value;
   }
 
+  // Return the value of an imaginary number.
+  const mpfr_t*
+  imaginary_value() const
+  {
+    gcc_assert(this->classification_ == TOKEN_IMAGINARY);
+    return &this->u_.float_value;
+  }
+
   // Return the operator value for an operator token.
   Operator
   op() const
@@ -281,7 +301,7 @@
     std::string* string_value;
     // The token value for TOKEN_INTEGER.
     mpz_t integer_value;
-    // The token value for TOKEN_FLOAT.
+    // The token value for TOKEN_FLOAT or TOKEN_IMAGINARY.
     mpfr_t float_value;
     // The token value for TOKEN_OPERATOR or the keyword value
     Operator op;
diff -r 56cbaf257cf7 go/parse.cc
--- a/go/parse.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/parse.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -2035,9 +2035,9 @@
   return new Typed_identifier(name, type, location);
 }
 
-// Operand = Literal | QualifiedIdent | "(" Expression ")" .
-// Literal = int_lit | float_lit | char_lit | string_lit |
-//   CompositeLit | FunctionLit .
+// Operand    = Literal | QualifiedIdent | MethodExpr | "(" Expression ")" .
+// Literal    = BasicLit | CompositeLit | FunctionLit .
+// BasicLit   = int_lit | float_lit | imaginary_lit | char_lit | string_lit .
 
 // If MAY_BE_SINK is true, this operand may be "_".
 
@@ -2171,6 +2171,17 @@
       this->advance_token();
       return ret;
 
+    case Token::TOKEN_IMAGINARY:
+      {
+	mpfr_t zero;
+	mpfr_init_set_ui(zero, 0, GMP_RNDN);
+	ret = Expression::make_complex(&zero, token->imaginary_value(),
+				       NULL, token->location());
+	mpfr_clear(zero);
+	this->advance_token();
+	return ret;
+      }
+
     case Token::TOKEN_KEYWORD:
       switch (token->keyword())
 	{
@@ -2809,6 +2820,7 @@
 	}
     case Token::TOKEN_INTEGER:
     case Token::TOKEN_FLOAT:
+    case Token::TOKEN_IMAGINARY:
       return true;
     default:
       gcc_unreachable();
@@ -2971,6 +2983,7 @@
     case Token::TOKEN_STRING:
     case Token::TOKEN_INTEGER:
     case Token::TOKEN_FLOAT:
+    case Token::TOKEN_IMAGINARY:
       this->simple_stat(true, false, NULL, NULL);
       break;
 
@@ -3029,6 +3042,7 @@
     case Token::TOKEN_STRING:
     case Token::TOKEN_INTEGER:
     case Token::TOKEN_FLOAT:
+    case Token::TOKEN_IMAGINARY:
       return true;
 
     default:
diff -r 56cbaf257cf7 go/types.cc
--- a/go/types.cc	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/types.cc	Wed Mar 10 14:43:36 2010 -0800
@@ -134,6 +134,7 @@
     {
     case TYPE_INTEGER:
     case TYPE_FLOAT:
+    case TYPE_COMPLEX:
     case TYPE_BOOLEAN:
     case TYPE_STRING:
     case TYPE_NIL:
@@ -164,13 +165,17 @@
 bool
 Type::is_abstract() const
 {
-  const Integer_type* it = this->integer_type();
-  if (it != NULL)
-    return it->is_abstract();
-  const Float_type* ft = this->float_type();
-  if (ft != NULL)
-    return ft->is_abstract();
-  return false;
+  switch (this->classification())
+    {
+    case TYPE_INTEGER:
+      return this->integer_type()->is_abstract();
+    case TYPE_FLOAT:
+      return this->float_type()->is_abstract();
+    case TYPE_COMPLEX:
+      return this->complex_type()->is_abstract();
+    default:
+      return false;
+    }
 }
 
 // Return a non-abstract version of an abstract type.
@@ -179,12 +184,17 @@
 Type::make_non_abstract_type()
 {
   gcc_assert(this->is_abstract());
-  if (this->integer_type() != NULL)
-    return Type::lookup_integer_type("int");
-  else if (this->float_type() != NULL)
-    return Type::lookup_float_type("float");
-  else
-    gcc_unreachable();
+  switch (this->classification())
+    {
+    case TYPE_INTEGER:
+      return Type::lookup_integer_type("int");
+    case TYPE_FLOAT:
+      return Type::lookup_float_type("float");
+    case TYPE_COMPLEX:
+      return Type::lookup_complex_type("complex");
+    default:
+      gcc_unreachable();
+    }
 }
 
 // Add entries to the reference count queue for this type.  This is
@@ -360,6 +370,9 @@
     case TYPE_FLOAT:
       return t1base->float_type()->is_compatible(t2base->float_type());
 
+    case TYPE_COMPLEX:
+      return t1base->complex_type()->is_compatible(t2base->complex_type());
+
     case TYPE_FUNCTION:
       return t1base->function_type()->is_compatible(t2base->function_type(),
 						    compatible,
@@ -406,12 +419,16 @@
   lhs = lhs->base();
   rhs = rhs->base();
 
-  // A constant of abstract integer or float type may be mixed with an
-  // integer or float type.
+  // A constant of abstract integer, float, or complex type may be
+  // mixed with an integer, float, or complex type.
   if ((rhs->is_abstract()
-       && (lhs->integer_type() != NULL || lhs->float_type() != NULL))
+       && (lhs->integer_type() != NULL
+	   || lhs->float_type() != NULL
+	   || lhs->complex_type() != NULL))
       || (lhs->is_abstract()
-	  && (rhs->integer_type() != NULL || rhs->float_type() != NULL)))
+	  && (rhs->integer_type() != NULL
+	      || rhs->float_type() != NULL
+	      || rhs->complex_type() != NULL)))
     return true;
 
   // The nil type may be compared to a pointer, an interface type, a
@@ -526,9 +543,11 @@
     return true;
 
   // A constant of abstract integer or float type may be assigned to
-  // integer or float type.
+  // integer, float, or complex type.
   if (rhs->is_abstract()
-      && (lhs->integer_type() != NULL || lhs->float_type() != NULL))
+      && (lhs->integer_type() != NULL
+	  || lhs->float_type() != NULL
+	  || lhs->complex_type() != NULL))
     return true;
 
   // The nil type may be assigned to a pointer type, an interface
@@ -1253,6 +1272,169 @@
   return Float_type::lookup_float_type(name);
 }
 
+// Class Complex_type.
+
+Complex_type::Named_complex_types Complex_type::named_complex_types;
+
+// Create a new complex type.  Non-abstract complex types always have
+// names.
+
+Named_type*
+Complex_type::create_complex_type(const char* name, int bits,
+				  int runtime_type_code)
+{
+  Complex_type* complex_type = new Complex_type(false, bits,
+						runtime_type_code);
+  std::string sname(name);
+  Named_object* named_object = Named_object::make_type(sname, NULL,
+						       complex_type,
+						       BUILTINS_LOCATION);
+  Named_type* named_type = named_object->type_value();
+  std::pair<Named_complex_types::iterator, bool> ins =
+    Complex_type::named_complex_types.insert(std::make_pair(sname,
+							    named_type));
+  gcc_assert(ins.second);
+  return named_type;
+}
+
+// Look up an existing complex type.
+
+Named_type*
+Complex_type::lookup_complex_type(const char* name)
+{
+  Named_complex_types::const_iterator p =
+    Complex_type::named_complex_types.find(name);
+  gcc_assert(p != Complex_type::named_complex_types.end());
+  return p->second;
+}
+
+// Create a new abstract complex type.
+
+Complex_type*
+Complex_type::create_abstract_complex_type()
+{
+  static Complex_type* abstract_type;
+  if (abstract_type == NULL)
+    abstract_type = new Complex_type(true, FLOAT_TYPE_SIZE * 2,
+				     RUNTIME_TYPE_CODE_FLOAT);
+  return abstract_type;
+}
+
+// Whether this type is compatible with T.
+
+bool
+Complex_type::is_compatible(const Complex_type *t) const
+{
+  if (this->bits_ != t->bits_)
+    return false;
+  return this->is_abstract_ == t->is_abstract_;
+}
+
+// Hash code.
+
+unsigned int
+Complex_type::do_hash_for_method(Gogo*) const
+{
+  return (this->bits_ << 4) + ((this->is_abstract_ ? 1 : 0) << 8);
+}
+
+// Get a tree without using a Gogo*.
+
+tree
+Complex_type::type_tree() const
+{
+  if (this->bits_ == FLOAT_TYPE_SIZE * 2)
+    return complex_float_type_node;
+  else if (this->bits_ == DOUBLE_TYPE_SIZE * 2)
+    return complex_double_type_node;
+  else if (this->bits_ == LONG_DOUBLE_TYPE_SIZE * 2)
+    return complex_long_double_type_node;
+  else
+    {
+      tree ret = make_node(REAL_TYPE);
+      TYPE_PRECISION(ret) = this->bits_ / 2;
+      layout_type(ret);
+      return build_complex_type(ret);
+    }
+}
+
+// Get a tree.
+
+tree
+Complex_type::do_get_tree(Gogo*)
+{
+  return this->type_tree();
+}
+
+// Zero initializer.
+
+tree
+Complex_type::do_init_tree(Gogo* gogo, bool is_clear)
+{
+  if (is_clear)
+    return NULL;
+  tree type = this->get_tree(gogo);
+  REAL_VALUE_TYPE r;
+  real_from_integer(&r, TYPE_MODE(TREE_TYPE(type)), 0, 0, 0);
+  return build_complex(type, build_real(TREE_TYPE(type), r),
+		       build_real(TREE_TYPE(type), r));
+}
+
+// The type descriptor for a complex type.  Complex types are always
+// named.
+
+void
+Complex_type::do_type_descriptor_decl(Gogo* gogo, Named_type* name,
+				      tree* pdecl)
+{
+  gcc_assert(name != NULL);
+  gogo->type_descriptor_decl(this->runtime_type_code_, this, name, pdecl);
+}
+
+// We should not be asked for the reflection string of a basic type.
+
+void
+Complex_type::do_reflection(Gogo*, std::string*) const
+{
+  gcc_unreachable();
+}
+
+// Mangled name.
+
+void
+Complex_type::do_mangled_name(Gogo*, std::string* ret) const
+{
+  char buf[100];
+  snprintf(buf, sizeof buf, "c%s%de",
+	   this->is_abstract_ ? "a" : "",
+	   this->bits_);
+  ret->append(buf);
+}
+
+// Make a complex type.
+
+Named_type*
+Type::make_complex_type(const char* name, int bits, int runtime_type_code)
+{
+  return Complex_type::create_complex_type(name, bits, runtime_type_code);
+}
+
+// Make an abstract complex type.
+
+Complex_type*
+Type::make_abstract_complex_type()
+{
+  return Complex_type::create_abstract_complex_type();
+}
+
+// Look up a complex type.
+
+Named_type*
+Type::lookup_complex_type(const char* name)
+{
+  return Complex_type::lookup_complex_type(name);
+}
+
 // Class String_type.
 
 // Return the tree for String_type.  We represent strings as a pointer
diff -r 56cbaf257cf7 go/types.h
--- a/go/types.h	Thu Mar 04 17:00:54 2010 -0800
+++ b/go/types.h	Wed Mar 10 14:43:36 2010 -0800
@@ -17,6 +17,7 @@
 class Typed_identifier_list;
 class Integer_type;
 class Float_type;
+class Complex_type;
 class String_type;
 class Function_type;
 class Struct_field;
@@ -75,6 +76,9 @@
 static const int RUNTIME_TYPE_CODE_MAP = 22;
 static const int RUNTIME_TYPE_CODE_PTR = 23;
 static const int RUNTIME_TYPE_CODE_STRUCT = 24;
+static const int RUNTIME_TYPE_CODE_COMPLEX64 = 25;
+static const int RUNTIME_TYPE_CODE_COMPLEX128 = 26;
+static const int RUNTIME_TYPE_CODE_COMPLEX = 27;
 
 // To build the complete list of methods for a named type we need to
 // gather all methods from anonymous fields.  Those methods may
@@ -345,6 +349,7 @@
     TYPE_BOOLEAN,
     TYPE_INTEGER,
     TYPE_FLOAT,
+    TYPE_COMPLEX,
     TYPE_STRING,
     TYPE_SINK,
     TYPE_FUNCTION,
@@ -411,6 +416,20 @@
   static Named_type*
   lookup_float_type(const char* name);
 
+  // Make an abstract complex type.
+  static Complex_type*
+  make_abstract_complex_type();
+
+  // Make a named complex type with a specific size.
+  // RUNTIME_TYPE_CODE is the code to use in reflection information,
+  // to distinguish complex and complex64.
+  static Named_type*
+  make_complex_type(const char* name, int bits, int runtime_type_code);
+
+  // Look up a named complex type.
+  static Named_type*
+  lookup_complex_type(const char* name);
+
   // Get the unnamed string type.
   static Type*
   make_string_type();
@@ -555,8 +574,8 @@
   bool
   is_basic_type() const;
 
-  // Return true if this is an abstract type--an integer or floating
-  // point type whose size has not been determined.
+  // Return true if this is an abstract type--an integer, floating
+  // point, or complex type whose size has not been determined.
   bool
   is_abstract() const;
 
@@ -615,6 +634,16 @@
   float_type() const
   { return this->convert<const Float_type, TYPE_FLOAT>(); }
 
+  // If this is a complex type, return the Complex_type.  Otherwise,
+  // return NULL.
+  Complex_type*
+  complex_type()
+  { return this->convert<Complex_type, TYPE_COMPLEX>(); }
+
+  const Complex_type*
+  complex_type() const
+  { return this->convert<const Complex_type, TYPE_COMPLEX>(); }
+
   // Return true if this is a boolean type.
   bool
   is_boolean_type() const
@@ -1364,6 +1393,78 @@
   int runtime_type_code_;
 };
 
+// The type of a complex number.
+
+class Complex_type : public Type
+{
+ public:
+  // Create a new complex type.
+  static Named_type*
+  create_complex_type(const char* name, int bits, int runtime_type_code);
+
+  // Look up an existing complex type.
+  static Named_type*
+  lookup_complex_type(const char* name);
+
+  // Create an abstract complex type.
+  static Complex_type*
+  create_abstract_complex_type();
+
+  // Whether this is an abstract complex type.
+  bool
+  is_abstract() const
+  { return this->is_abstract_; }
+
+  // The number of bits: 64 or 128.
+  int bits() const
+  { return this->bits_; }
+
+  // Whether this type is the same as T.
+  bool
+  is_compatible(const Complex_type* t) const;
+
+  // Return a tree for this type without using a Gogo*.
+  tree
+  type_tree() const;
+
+ protected:
+  unsigned int
+  do_hash_for_method(Gogo*) const;
+
+  tree
+  do_get_tree(Gogo*);
+
+  tree
+  do_init_tree(Gogo*, bool);
+
+  void
+  do_type_descriptor_decl(Gogo*, Named_type*, tree*);
+
+  void
+  do_reflection(Gogo*, std::string*) const;
+
+  void
+  do_mangled_name(Gogo*, std::string*) const;
+
+ private:
+  Complex_type(bool is_abstract, int bits, int runtime_type_code)
+    : Type(TYPE_COMPLEX),
+      is_abstract_(is_abstract), bits_(bits),
+      runtime_type_code_(runtime_type_code)
+  { }
+
+  // Map names of complex types to the types themselves.
+  typedef std::map<std::string, Named_type*> Named_complex_types;
+  static Named_complex_types named_complex_types;
+
+  // True if this is an abstract type.
+  bool is_abstract_;
+  // The number of bits in the complex value--64 or 128.
+  int bits_;
+  // The runtime type code used in the type descriptor for this type.
+  int runtime_type_code_;
+};
+
 // The type of a string.
 
 class String_type : public Type
diff -r 56cbaf257cf7 libgo/go/fmt/format.go
--- a/libgo/go/fmt/format.go	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/go/fmt/format.go	Wed Mar 10 14:43:36 2010 -0800
@@ -42,13 +42,14 @@
 	wid  int
 	prec int
 	// flags
-	widPresent  bool
-	precPresent bool
-	minus       bool
-	plus        bool
-	sharp       bool
-	space       bool
-	zero        bool
+	widPresent    bool
+	precPresent   bool
+	minus         bool
+	plus          bool
+	sharp         bool
+	space         bool
+	zero          bool
+	preserveFlags bool // don't clear flags after this print; used to carry over in complex prints
 }
 
 func (f *fmt) clearflags() {
@@ -118,7 +119,9 @@
 	if right > 0 {
 		f.writePadding(right, padding)
 	}
-	f.clearflags()
+	if !f.preserveFlags {
+		f.clearflags()
+	}
 }
 
 // append s to buf, padded on left (w > 0) or right (w < 0 or f.minus).
@@ -136,7 +139,9 @@
 	if right > 0 {
 		f.writePadding(right, padding)
 	}
-	f.clearflags()
+	if !f.preserveFlags {
+		f.clearflags()
+	}
 }
 
 func putint(buf []byte, base, val uint64, digits string) int {
@@ -419,6 +424,64 @@
 // fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
 func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.Ftoa32(v, 'b', 0)) }
 
+// fmt_c64 formats a complex64 according to its fmt_x argument.
+// TODO pass in a method rather than a byte when the compilers mature.
+func (f *fmt) fmt_c64(v complex64, fmt_x byte) {
+	f.buf.WriteByte('(')
+	r := real(v)
+	f.preserveFlags = true
+	for i := 0; ; i++ {
+		switch fmt_x {
+		case 'e':
+			f.fmt_e32(r)
+		case 'E':
+			f.fmt_E32(r)
+		case 'f':
+			f.fmt_f32(r)
+		case 'g':
+			f.fmt_g32(r)
+		case 'G':
+			f.fmt_G32(r)
+		}
+		f.preserveFlags = false
+		if i != 0 {
+			break
+		}
+		f.plus = true
+		r = imag(v)
+	}
+	f.buf.Write(irparenBytes)
+}
+
+// fmt_c128 formats a complex128 according to its fmt_x argument.
+// TODO pass in a method rather than a byte when the compilers mature.
+func (f *fmt) fmt_c128(v complex128, fmt_x byte) {
+	f.buf.WriteByte('(')
+	r := real(v)
+	f.preserveFlags = true
+	for i := 0; ; i++ {
+		switch fmt_x {
+		case 'e':
+			f.fmt_e64(r)
+		case 'E':
+			f.fmt_E64(r)
+		case 'f':
+			f.fmt_f64(r)
+		case 'g':
+			f.fmt_g64(r)
+		case 'G':
+			f.fmt_G64(r)
+		}
+		f.preserveFlags = false
+		if i != 0 {
+			break
+		}
+		f.plus = true
+		r = imag(v)
+	}
+	f.buf.Write(irparenBytes)
+}
+
 // float
 func (x *fmt) f(a float) {
 	if strconv.FloatSize == 32 {
diff -r 56cbaf257cf7 libgo/go/fmt/print.go
--- a/libgo/go/fmt/print.go	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/go/fmt/print.go	Wed Mar 10 14:43:36 2010 -0800
@@ -24,7 +24,7 @@
 		%o	base 8
 		%x	base 16, with lower-case letters for a-f
 		%X	base 16, with upper-case letters for A-F
-	Floating-point:
+	Floating-point and complex constituents:
 		%e	scientific notation, e.g. -1234.456e+78
 		%E	scientific notation, e.g. -1234.456E+78
 		%f	decimal point but no exponent, e.g. 123.456
@@ -87,15 +87,16 @@
 // Some constants in the form of bytes, to avoid string overhead.
 // Needlessly fastidious, I suppose.
 var (
-	trueBytes       = []byte{'t', 'r', 'u', 'e'}
-	falseBytes      = []byte{'f', 'a', 'l', 's', 'e'}
-	commaSpaceBytes = []byte{',', ' '}
-	nilAngleBytes   = []byte{'<', 'n', 'i', 'l', '>'}
-	nilParenBytes   = []byte{'(', 'n', 'i', 'l', ')'}
-	nilBytes        = []byte{'n', 'i', 'l'}
-	mapBytes        = []byte{'m', 'a', 'p', '['}
-	missingBytes    = []byte{'m', 'i', 's', 's', 'i', 'n', 'g'}
-	extraBytes      = []byte{'?', '(', 'e', 'x', 't', 'r', 'a', ' '}
+	trueBytes       = []byte("true")
+	falseBytes      = []byte("false")
+	commaSpaceBytes = []byte(", ")
+	nilAngleBytes   = []byte("<nil>")
+	nilParenBytes   = []byte("(nil)")
+	nilBytes        = []byte("nil")
+	mapBytes        = []byte("map[")
+	missingBytes    = []byte("missing")
+	extraBytes      = []byte("?(extra ")
+	irparenBytes    = []byte("i)")
 )
 
 // State represents the printer state passed to custom formatters.
@@ -445,6 +446,52 @@
 	return
 }
 
+var complexBits = reflect.Typeof(complex(0i)).Size() * 8
+
+func getComplex64(a interface{}) (val complex64, ok bool) {
+	// Is it a regular complex type?
+	switch c := a.(type) {
+	case complex64:
+		return c, true
+	case complex:
+		if complexBits == 64 {
+			return complex64(c), true
+		}
+	}
+	// Must be a renamed complex type.
+	switch c := reflect.NewValue(a).(type) {
+	case *reflect.Complex64Value:
+		return complex64(c.Get()), true
+	case *reflect.ComplexValue:
+		if complexBits == 64 {
+			return complex64(c.Get()), true
+		}
+	}
+	return
+}
+
+func getComplex128(a interface{}) (val complex128, ok bool) {
+	// Is it a regular complex type?
+	switch c := a.(type) {
+	case complex128:
+		return c, true
+	case complex:
+		if complexBits == 128 {
+			return complex128(c), true
+		}
+	}
+	// Must be a renamed complex type.
+	switch c := reflect.NewValue(a).(type) {
+	case *reflect.Complex128Value:
+		return complex128(c.Get()), true
+	case *reflect.ComplexValue:
+		if complexBits == 128 {
+			return complex128(c.Get()), true
+		}
+	}
+	return
+}
+
 // Convert ASCII to integer.  n is 0 (and got is false) if no number present.
 
 func parsenum(s string, start, end int) (n int, got bool, newi int) {
@@ -509,6 +556,19 @@
 			p.fmt.fmt_g64(float64(f))
 		}
 		return false
+	case complex64:
+		p.fmt.fmt_c64(f, 'g')
+		return false
+	case complex128:
+		p.fmt.fmt_c128(f, 'g')
+		return false
+	case complex:
+		if complexBits == 64 {
+			p.fmt.fmt_c64(complex64(f), 'g')
+		} else {
+			p.fmt.fmt_c128(complex128(f), 'g')
+		}
+		return false
 	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
 		v, signed, ok := getInt(field)
 		if !ok {
@@ -855,12 +915,16 @@
 				goto badtype
 			}
 
-		// float
+		// float/complex
 		case 'e':
 			if v, ok := getFloat32(field); ok {
 				p.fmt.fmt_e32(v)
 			} else if v, ok := getFloat64(field); ok {
 				p.fmt.fmt_e64(v)
+			} else if v, ok := getComplex64(field); ok {
+				p.fmt.fmt_c64(v, 'e')
+			} else if v, ok := getComplex128(field); ok {
+				p.fmt.fmt_c128(v, 'e')
 			} else {
 				goto badtype
 			}
@@ -869,6 +933,10 @@
 				p.fmt.fmt_E32(v)
 			} else if v, ok := getFloat64(field); ok {
 				p.fmt.fmt_E64(v)
+			} else if v, ok := getComplex64(field); ok {
+				p.fmt.fmt_c64(v, 'E')
+			} else if v, ok := getComplex128(field); ok {
+				p.fmt.fmt_c128(v, 'E')
 			} else {
 				goto badtype
 			}
@@ -877,6 +945,10 @@
 				p.fmt.fmt_f32(v)
 			} else if v, ok := getFloat64(field); ok {
 				p.fmt.fmt_f64(v)
+			} else if v, ok := getComplex64(field); ok {
+				p.fmt.fmt_c64(v, 'f')
+			} else if v, ok := getComplex128(field); ok {
+				p.fmt.fmt_c128(v, 'f')
 			} else {
 				goto badtype
 			}
@@ -885,6 +957,10 @@
 				p.fmt.fmt_g32(v)
 			} else if v, ok := getFloat64(field); ok {
 				p.fmt.fmt_g64(v)
+			} else if v, ok := getComplex64(field); ok {
+				p.fmt.fmt_c64(v, 'g')
+			} else if v, ok := getComplex128(field); ok {
+				p.fmt.fmt_c128(v, 'g')
 			} else {
 				goto badtype
 			}
@@ -893,6 +969,10 @@
 				p.fmt.fmt_G32(v)
 			} else if v, ok := getFloat64(field); ok {
 				p.fmt.fmt_G64(v)
+			} else if v, ok := getComplex64(field); ok {
+				p.fmt.fmt_c64(v, 'G')
+			} else if v, ok := getComplex128(field); ok {
+				p.fmt.fmt_c128(v, 'G')
 			} else {
 				goto badtype
 			}
diff -r 56cbaf257cf7 libgo/go/reflect/type.go
--- a/libgo/go/reflect/type.go	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/go/reflect/type.go	Wed Mar 10 14:43:36 2010 -0800
@@ -80,6 +80,21 @@
 	commonType
 }
 
+// Complex64Type represents a complex64 type.
+type Complex64Type struct {
+	commonType
+}
+
+// Complex128Type represents acomplex128 type.
+type Complex128Type struct {
+	commonType
+}
+
+// ComplexType represents a complex type.
+type ComplexType struct {
+	commonType
+}
+
 // Int16Type represents an int16 type.
 type Int16Type struct {
 	commonType
@@ -604,6 +619,12 @@
 		return (*Float32Type)(unsafe.Pointer(v))
 	case *runtime.Float64Type:
 		return (*Float64Type)(unsafe.Pointer(v))
+	case *runtime.ComplexType:
+		return (*ComplexType)(unsafe.Pointer(v))
+	case *runtime.Complex64Type:
+		return (*Complex64Type)(unsafe.Pointer(v))
+	case *runtime.Complex128Type:
+		return (*Complex128Type)(unsafe.Pointer(v))
 	case *runtime.IntType:
 		return (*IntType)(unsafe.Pointer(v))
 	case *runtime.Int8Type:
@@ -662,6 +683,12 @@
 		r = (*Float32Type)(unsafe.Pointer(v))
 	case runtime.Float64TypeCode:
 		r = (*Float64Type)(unsafe.Pointer(v))
+	case runtime.ComplexTypeCode:
+		r = (*ComplexType)(unsafe.Pointer(v))
+	case runtime.Complex64TypeCode:
+		r = (*Complex64Type)(unsafe.Pointer(v))
+	case runtime.Complex128TypeCode:
+		r = (*Complex128Type)(unsafe.Pointer(v))
 	case runtime.IntTypeCode:
 		r = (*IntType)(unsafe.Pointer(v))
 	case runtime.Int8TypeCode:
diff -r 56cbaf257cf7 libgo/go/reflect/value.go
--- a/libgo/go/reflect/value.go	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/go/reflect/value.go	Wed Mar 10 14:43:36 2010 -0800
@@ -186,6 +186,63 @@
 // Set sets v to the value x.
 func (v *Float64Value) SetValue(x Value) { v.Set(x.(*Float64Value).Get()) }
 
+// ComplexValue represents a complex value.
+type ComplexValue struct {
+	value
+}
+
+// Get returns the underlying complex value.
+func (v *ComplexValue) Get() complex { return *(*complex)(v.addr) }
+
+// Set sets v to the value x.
+func (v *ComplexValue) Set(x complex) {
+	if !v.canSet {
+		panic(cannotSet)
+	}
+	*(*complex)(v.addr) = x
+}
+
+// Set sets v to the value x.
+func (v *ComplexValue) SetValue(x Value) { v.Set(x.(*ComplexValue).Get()) }
+
+// Complex64Value represents a complex64 value.
+type Complex64Value struct {
+	value
+}
+
+// Get returns the underlying complex64 value.
+func (v *Complex64Value) Get() complex64 { return *(*complex64)(v.addr) }
+
+// Set sets v to the value x.
+func (v *Complex64Value) Set(x complex64) {
+	if !v.canSet {
+		panic(cannotSet)
+	}
+	*(*complex64)(v.addr) = x
+}
+
+// Set sets v to the value x.
+func (v *Complex64Value) SetValue(x Value) { v.Set(x.(*Complex64Value).Get()) }
+
+// Complex128Value represents a complex128 value.
+type Complex128Value struct {
+	value
+}
+
+// Get returns the underlying complex128 value.
+func (v *Complex128Value) Get() complex128 { return *(*complex128)(v.addr) }
+
+// Set sets v to the value x.
+func (v *Complex128Value) Set(x complex128) {
+	if !v.canSet {
+		panic(cannotSet)
+	}
+	*(*complex128)(v.addr) = x
+}
+
+// Set sets v to the value x.
+func (v *Complex128Value) SetValue(x Value) { v.Set(x.(*Complex128Value).Get()) }
+
 // IntValue represents an int value.
 type IntValue struct {
 	value
@@ -1242,6 +1299,12 @@
 		return (*Float32Value)(v)
 	case *Float64Type:
 		return (*Float64Value)(v)
+	case *ComplexType:
+		return (*ComplexValue)(v)
+	case *Complex64Type:
+		return (*Complex64Value)(v)
+	case *Complex128Type:
+		return (*Complex128Value)(v)
 	case *IntType:
 		return (*IntValue)(v)
 	case *Int8Type:
diff -r 56cbaf257cf7 libgo/go/runtime/type.go
--- a/libgo/go/runtime/type.go	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/go/runtime/type.go	Wed Mar 10 14:43:36 2010 -0800
@@ -45,6 +45,9 @@
 	MapTypeCode           = 22
 	PtrTypeCode           = 23
 	StructTypeCode        = 24
+	Complex64TypeCode     = 25
+	Complex128TypeCode    = 26
+	ComplexTypeCode       = 27
 )
 
 // All types begin with a few common fields needed for
diff -r 56cbaf257cf7 libgo/runtime/go-print.c
--- a/libgo/runtime/go-print.c	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/runtime/go-print.c	Wed Mar 10 14:43:36 2010 -0800
@@ -52,6 +52,15 @@
 }
 
 void
+__go_print_complex (_Bool is_panic, __complex double val)
+{
+  fprintf (is_panic ? stderr : stdout, "(%.24g%s%.24gi)",
+	   __builtin_creal (val),
+	   __builtin_cimag (val) >= 0 ? "+" : "",
+	   __builtin_cimag (val));
+}
+
+void
 __go_print_bool (_Bool is_panic, _Bool val)
 {
   fputs (val ? "true" : "false", is_panic ? stderr : stdout);
diff -r 56cbaf257cf7 libgo/runtime/go-reflect-call.c
--- a/libgo/runtime/go-reflect-call.c	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/runtime/go-reflect-call.c	Wed Mar 10 14:43:36 2010 -0800
@@ -108,6 +108,10 @@
       abort ();
     case GO_FLOAT:
       return &ffi_type_float;
+    case GO_COMPLEX64:
+    case GO_COMPLEX128:
+    case GO_COMPLEX:
+      abort ();
     case GO_INT16:
       return &ffi_type_sint16;
     case GO_INT32:
diff -r 56cbaf257cf7 libgo/runtime/go-reflect.c
--- a/libgo/runtime/go-reflect.c	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/runtime/go-reflect.c	Wed Mar 10 14:43:36 2010 -0800
@@ -31,6 +31,12 @@
   asm ("__go_td_pN33_libgo_runtime.runtime.Float64Type");
 extern const struct __go_type_descriptor ptr_float_descriptor
   asm ("__go_td_pN31_libgo_runtime.runtime.FloatType");
+extern const struct __go_type_descriptor ptr_complex64_descriptor
+  asm ("__go_td_pN35_libgo_runtime.runtime.Complex64Type");
+extern const struct __go_type_descriptor ptr_complex128_descriptor
+  asm ("__go_td_pN36_libgo_runtime.runtime.Complex128Type");
+extern const struct __go_type_descriptor ptr_complex_descriptor
+  asm ("__go_td_pN33_libgo_runtime.runtime.ComplexType");
 extern const struct __go_type_descriptor ptr_int16_descriptor
   asm ("__go_td_pN31_libgo_runtime.runtime.Int16Type");
 extern const struct __go_type_descriptor ptr_int32_descriptor
@@ -87,6 +93,12 @@
       return &ptr_float64_descriptor;
     case GO_FLOAT:
       return &ptr_float_descriptor;
+    case GO_COMPLEX64:
+      return &ptr_complex64_descriptor;
+    case GO_COMPLEX128:
+      return &ptr_complex128_descriptor;
+    case GO_COMPLEX:
+      return &ptr_complex_descriptor;
     case GO_INT16:
       return &ptr_int16_descriptor;
     case GO_INT32:
diff -r 56cbaf257cf7 libgo/runtime/go-type.h
--- a/libgo/runtime/go-type.h	Thu Mar 04 17:00:54 2010 -0800
+++ b/libgo/runtime/go-type.h	Wed Mar 10 14:43:36 2010 -0800
@@ -54,6 +54,9 @@
 #define GO_MAP 22
 #define GO_PTR 23
 #define GO_STRUCT 24
+#define GO_COMPLEX64 25
+#define GO_COMPLEX128 26
+#define GO_COMPLEX 27
 
 /* For each Go type the compiler constructs one of these structures.
    This is used for type reflectin, interfaces, maps, and reference

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