diff mbox

[gccgo] Permit passing a floating point constant to make

Message ID mcrr5jyka97.fsf@dhcp-172-17-9-151.mtv.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor June 22, 2010, 9:50 p.m. UTC
Some Go code passes a floating point constant to the predeclared
function make, which is OK provided the floating point constant
actually has an integer value (e.g., 1e2).  This patch permits this in
the gccgo frontend.  Committed to gccgo branch.

Ian
diff mbox

Patch

diff -r 3de4aff5ab46 go/types.cc
--- a/go/types.cc	Tue Jun 22 13:34:56 2010 -0700
+++ b/go/types.cc	Tue Jun 22 14:47:49 2010 -0700
@@ -638,6 +638,59 @@ 
   gcc_unreachable();
 }
 
+// Return whether an expression has an integer value.  Report an error
+// if not.  This is used when handling calls to the predeclared make
+// function.
+
+bool
+Type::check_int_value(Expression* e, const char* errmsg,
+		      source_location location)
+{
+  if (e->type()->integer_type() != NULL)
+    return true;
+
+  // Check for a floating point constant with integer value.
+  mpfr_t fval;
+  mpfr_init(fval);
+
+  Type* dummy;
+  if (e->float_constant_value(fval, &dummy))
+    {
+      mpz_t ival;
+      mpz_init(ival);
+
+      bool ok = false;
+
+      mpfr_clear_overflow();
+      mpfr_clear_erangeflag();
+      mpfr_get_z(ival, fval, GMP_RNDN);
+      if (!mpfr_overflow_p()
+	  && !mpfr_erangeflag_p()
+	  && mpz_sgn(ival) >= 0)
+	{
+	  Named_type* ntype = Type::lookup_integer_type("int");
+	  Integer_type* inttype = ntype->integer_type();
+	  mpz_t max;
+	  mpz_init_set_ui(max, 1);
+	  mpz_mul_2exp(max, max, inttype->bits() - 1);
+	  ok = mpz_cmp(ival, max) < 0;
+	  mpz_clear(max);
+	}
+      mpz_clear(ival);
+
+      if (ok)
+	{
+	  mpfr_clear(fval);
+	  return true;
+	}
+    }
+
+  mpfr_clear(fval);
+
+  error_at(location, errmsg);
+  return false;
+}
+
 // Return a tree representing this type.
 
 tree
@@ -3334,21 +3387,18 @@ 
     }
   else
     {
-      Type* type = (*args->begin())->type();
-      if (type == NULL || type->integer_type() == NULL)
-	{
-	  error_at(location, "bad type for length when making slice");
-	  return false;
-	}
+      if (!Type::check_int_value(args->front(),
+				 _("bad length when making slice"), location))
+	return false;
+
       if (args->size() > 1)
 	{
-	  type = args->back()->type();
-	  if (type == NULL || type->integer_type() == NULL)
-	    {
-	      error_at(location, "bad type for capacity when making slice");
-	      return false;
-	    }
-	}
+	  if (!Type::check_int_value(args->back(),
+				     _("bad capacity when making slice"),
+				     location))
+	    return false;
+	}
+
       return true;
     }
 }
@@ -3546,7 +3596,7 @@ 
   tree length_tree = args->front()->get_tree(context);
   if (length_tree == error_mark_node)
     return error_mark_node;
-  length_tree = fold_convert_loc(location, TREE_TYPE(count_field), length_tree);
+  length_tree = ::convert(TREE_TYPE(count_field), length_tree);
   length_tree = save_expr(length_tree);
   tree capacity_tree;
   if (args->size() == 1)
@@ -3554,8 +3604,7 @@ 
   else
     {
       capacity_tree = args->back()->get_tree(context);
-      capacity_tree = fold_convert_loc(location, TREE_TYPE(count_field),
-				       capacity_tree);
+      capacity_tree = ::convert(TREE_TYPE(count_field), capacity_tree);
       capacity_tree = save_expr(capacity_tree);
       capacity_tree = fold_build3_loc(location, COND_EXPR,
 				      TREE_TYPE(count_field),
@@ -3866,12 +3915,9 @@ 
 {
   if (args != NULL && !args->empty())
     {
-      Type* type = (*args->begin())->type();
-      if (type == NULL || type->integer_type() == NULL)
-	{
-	  error_at(location, "bad type for map size");
-	  return false;
-	}
+      if (!Type::check_int_value(args->front(), _("bad size when making map"),
+				 location))
+	return false;
       else if (args->size() > 1)
 	{
 	  error_at(location, "too many arguments when making map");
@@ -3960,9 +4006,10 @@ 
     expr_tree = size_zero_node;
   else
     {
-      expr_tree = (*args->begin())->get_tree(context);
+      expr_tree = args->front()->get_tree(context);
       if (expr_tree == error_mark_node)
 	return error_mark_node;
+      expr_tree = ::convert(sizetype, expr_tree);
     }
 
   tree map_type = this->get_tree(context->gogo());
@@ -4080,12 +4127,10 @@ 
 {
   if (args != NULL && !args->empty())
     {
-      Type* type = args->front()->type();
-      if (type == NULL || type->integer_type() == NULL)
-	{
-	  error_at(location, "bad type for channel buffer size");
-	  return false;
-	}
+      if (!Type::check_int_value(args->front(),
+				 _("bad buffer size when making channel"),
+				 location))
+	return false;
       else if (args->size() > 1)
 	{
 	  error_at(location, "too many arguments when making channel");
@@ -4140,7 +4185,7 @@ 
 
   tree expr_tree;
   if (args != NULL && !args->empty())
-    expr_tree = fold_convert(sizetype, (*args->begin())->get_tree(context));
+    expr_tree = ::convert(sizetype, args->front()->get_tree(context));
   else
     expr_tree = size_zero_node;
 
diff -r 3de4aff5ab46 go/types.h
--- a/go/types.h	Tue Jun 22 13:34:56 2010 -0700
+++ b/go/types.h	Tue Jun 22 14:47:49 2010 -0700
@@ -932,6 +932,10 @@ 
   virtual void
   do_export(Export*) const;
 
+  // Return whether an expression is an integer.
+  static bool
+  check_int_value(Expression*, const char*, source_location);
+
   // Return whether a method expects a pointer as the receiver.
   static bool
   method_expects_pointer(const Named_object*);