diff mbox

Go patch committed: Fix nil func calls, constant float type conversions

Message ID mcrzk1vstnu.fsf@google.com
State New
Headers show

Commit Message

Ian Lance Taylor Dec. 3, 2012, 3:26 a.m. UTC
This patch to the Go frontend fixes two small, unrelated, things.

Calls to a nil func value (corresponding to a NULL function pointer in
C) should panic in Go, and panics should be recoverable.  They were not
recoverable in gccgo, because the exception unwinder does not unwind
past a zero PC (because uw_frame_state_for checks for this and returns
_URC_END_OF_STACK).  To fix this I added a runtime nil check before
every call through a func value.

I changed the way the code handles explicit conversions of constant
float or complex values.  Rather than postponing them to runtime, I use
mpfr_set_prec to reduce the precision as needed.

Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline and 4.7 branch.

Ian
diff mbox

Patch

diff -r 623a0f780e0f go/expressions.cc
--- a/go/expressions.cc	Fri Nov 30 16:14:46 2012 -0800
+++ b/go/expressions.cc	Sun Dec 02 18:57:12 2012 -0800
@@ -2965,46 +2965,6 @@ 
 	{
 	  if (!nc.set_type(type, true, location))
 	    return Expression::make_error(location);
-
-	  // Don't simply convert to or from a float or complex type
-	  // with a different size.  That may change the value.
-	  Type* vtype = val->type();
-	  if (vtype->is_abstract())
-	    ;
-	  else if (type->float_type() != NULL)
-	    {
-	      if (vtype->float_type() != NULL)
-		{
-		  if (type->float_type()->bits() != vtype->float_type()->bits())
-		    return this;
-		}
-	      else if (vtype->complex_type() != NULL)
-		{
-		  if (type->float_type()->bits() * 2
-		      != vtype->complex_type()->bits())
-		    return this;
-		}
-	    }
-	  else if (type->complex_type() != NULL)
-	    {
-	      if (vtype->complex_type() != NULL)
-		{
-		  if (type->complex_type()->bits()
-		      != vtype->complex_type()->bits())
-		    return this;
-		}
-	      else if (vtype->float_type() != NULL)
-		{
-		  if (type->complex_type()->bits()
-		      != vtype->float_type()->bits() * 2)
-		    return this;
-		}
-	    }
-	  else if (vtype->float_type() != NULL)
-	    return this;
-	  else if (vtype->complex_type() != NULL)
-	    return this;
-
 	  return nc.expression(location);
 	}
     }
@@ -9239,6 +9199,9 @@ 
 	}
     }
 
+  if (func == NULL)
+    fn = save_expr(fn);
+
   tree ret = build_call_array(excess_type != NULL_TREE ? excess_type : rettype,
 			      fn, nargs, args);
   delete[] args;
@@ -9272,6 +9235,24 @@ 
   if (this->results_ != NULL)
     ret = this->set_results(context, ret);
 
+  // We can't unwind the stack past a call to nil, so we need to
+  // insert an explicit check so that the panic can be recovered.
+  if (func == NULL)
+    {
+      tree compare = fold_build2_loc(location.gcc_location(), EQ_EXPR,
+				     boolean_type_node, fn,
+				     fold_convert_loc(location.gcc_location(),
+						      TREE_TYPE(fn),
+						      null_pointer_node));
+      tree crash = build3_loc(location.gcc_location(), COND_EXPR,
+			      void_type_node, compare,
+			      gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
+						  location),
+			      NULL_TREE);
+      ret = fold_build2_loc(location.gcc_location(), COMPOUND_EXPR,
+			    TREE_TYPE(ret), crash, ret);
+    }
+
   this->tree_ = ret;
 
   return ret;
@@ -14229,7 +14210,7 @@ 
 
 bool
 Numeric_constant::check_float_type(Float_type* type, bool issue_error,
-				   Location location) const
+				   Location location)
 {
   mpfr_t val;
   switch (this->classification_)
@@ -14282,6 +14263,29 @@ 
 	}
 
       ret = exp <= max_exp;
+
+      if (ret)
+	{
+	  // Round the constant to the desired type.
+	  mpfr_t t;
+	  mpfr_init(t);
+	  switch (type->bits())
+	    {
+	    case 32:
+	      mpfr_set_prec(t, 24);
+	      break;
+	    case 64:
+	      mpfr_set_prec(t, 53);
+	      break;
+	    default:
+	      go_unreachable();
+	    }
+	  mpfr_set(t, val, GMP_RNDN);
+	  mpfr_set(val, t, GMP_RNDN);
+	  mpfr_clear(t);
+
+	  this->set_float(type, val);
+	}
     }
 
   mpfr_clear(val);
@@ -14296,7 +14300,7 @@ 
 
 bool
 Numeric_constant::check_complex_type(Complex_type* type, bool issue_error,
-				     Location location) const
+				     Location location)
 {
   if (type->is_abstract())
     return true;
@@ -14315,46 +14319,77 @@ 
     }
 
   mpfr_t real;
+  mpfr_t imag;
   switch (this->classification_)
     {
     case NC_INT:
     case NC_RUNE:
       mpfr_init_set_z(real, this->u_.int_val, GMP_RNDN);
+      mpfr_init_set_ui(imag, 0, GMP_RNDN);
       break;
 
     case NC_FLOAT:
       mpfr_init_set(real, this->u_.float_val, GMP_RNDN);
+      mpfr_init_set_ui(imag, 0, GMP_RNDN);
       break;
 
     case NC_COMPLEX:
-      if (!mpfr_nan_p(this->u_.complex_val.imag)
-	  && !mpfr_inf_p(this->u_.complex_val.imag)
-	  && !mpfr_zero_p(this->u_.complex_val.imag))
-	{
-	  if (mpfr_get_exp(this->u_.complex_val.imag) > max_exp)
-	    {
-	      if (issue_error)
-		error_at(location, "complex imaginary part overflow");
-	      return false;
-	    }
-	}
       mpfr_init_set(real, this->u_.complex_val.real, GMP_RNDN);
+      mpfr_init_set(imag, this->u_.complex_val.imag, GMP_RNDN);
       break;
 
     default:
       go_unreachable();
     }
 
-  bool ret;
-  if (mpfr_nan_p(real) || mpfr_inf_p(real) || mpfr_zero_p(real))
-    ret = true;
-  else
-    ret = mpfr_get_exp(real) <= max_exp;
+  bool ret = true;
+  if (!mpfr_nan_p(real)
+      && !mpfr_inf_p(real)
+      && !mpfr_zero_p(real)
+      && mpfr_get_exp(real) > max_exp)
+    {
+      if (issue_error)
+	error_at(location, "complex real part overflow");
+      ret = false;
+    }
+
+  if (!mpfr_nan_p(imag)
+      && !mpfr_inf_p(imag)
+      && !mpfr_zero_p(imag)
+      && mpfr_get_exp(imag) > max_exp)
+    {
+      if (issue_error)
+	error_at(location, "complex imaginary part overflow");
+      ret = false;
+    }
+
+  if (ret)
+    {
+      // Round the constant to the desired type.
+      mpfr_t t;
+      mpfr_init(t);
+      switch (type->bits())
+	{
+	case 64:
+	  mpfr_set_prec(t, 24);
+	  break;
+	case 128:
+	  mpfr_set_prec(t, 53);
+	  break;
+	default:
+	  go_unreachable();
+	}
+      mpfr_set(t, real, GMP_RNDN);
+      mpfr_set(real, t, GMP_RNDN);
+      mpfr_set(t, imag, GMP_RNDN);
+      mpfr_set(imag, t, GMP_RNDN);
+      mpfr_clear(t);
+
+      this->set_complex(type, real, imag);
+    }
 
   mpfr_clear(real);
-
-  if (!ret && issue_error)
-    error_at(location, "complex real part overflow");
+  mpfr_clear(imag);
 
   return ret;
 }
diff -r 623a0f780e0f go/expressions.h
--- a/go/expressions.h	Fri Nov 30 16:14:46 2012 -0800
+++ b/go/expressions.h	Sun Dec 02 18:57:12 2012 -0800
@@ -2224,10 +2224,10 @@ 
   check_int_type(Integer_type*, bool, Location) const;
 
   bool
-  check_float_type(Float_type*, bool, Location) const;
+  check_float_type(Float_type*, bool, Location);
 
   bool
-  check_complex_type(Complex_type*, bool, Location) const;
+  check_complex_type(Complex_type*, bool, Location);
 
   // The kinds of constants.
   enum Classification