diff mbox

Go patch committed: Fix shift expressions

Message ID mcrd3et63uz.fsf@coign.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor Sept. 21, 2011, 8:24 p.m. UTC
This patch to the Go frontend fixes a couple of cases with shift
expressions.  A shift of a floating point constant (which would have to
have integral value) is still a constant, and as such must be lowered to
a constant value.  This patch fixes the general case of shifting a
non-numeric value, where the type comes from the context.  Bootstrapped
and ran Go testsuite on x86_64-unknown-linux-gnu.  Committed to
mainline.

Ian
diff mbox

Patch

diff -r a7c7e2006ab7 go/expressions.cc
--- a/go/expressions.cc	Wed Sep 21 10:34:46 2011 -0700
+++ b/go/expressions.cc	Wed Sep 21 13:05:32 2011 -0700
@@ -5671,6 +5671,50 @@ 
 	return Expression::make_string(left_string + right_string, location);
     }
 
+  // Special case for shift of a floating point constant.
+  if (op == OPERATOR_LSHIFT || op == OPERATOR_RSHIFT)
+    {
+      mpfr_t left_val;
+      mpfr_init(left_val);
+      Type* left_type;
+      mpz_t right_val;
+      mpz_init(right_val);
+      Type* right_type;
+      if (left->float_constant_value(left_val, &left_type)
+	  && right->integer_constant_value(false, right_val, &right_type)
+	  && mpfr_integer_p(left_val)
+	  && (left_type == NULL
+	      || left_type->is_abstract()
+	      || left_type->integer_type() != NULL))
+	{
+	  mpz_t left_int;
+	  mpz_init(left_int);
+	  mpfr_get_z(left_int, left_val, GMP_RNDN);
+
+	  mpz_t val;
+	  mpz_init(val);
+
+	  Expression* ret = NULL;
+	  if (Binary_expression::eval_integer(op, left_type, left_int,
+					      right_type, right_val,
+					      location, val))
+	    ret = Expression::make_integer(&val, left_type, location);
+
+	  mpz_clear(left_int);
+	  mpz_clear(val);
+
+	  if (ret != NULL)
+	    {
+	      mpfr_clear(left_val);
+	      mpz_clear(right_val);
+	      return ret;
+	    }
+	}
+
+      mpfr_clear(left_val);
+      mpz_clear(right_val);
+    }
+
   return this;
 }
 
@@ -5939,14 +5983,8 @@ 
   // Set the context for the left hand operand.
   if (is_shift_op)
     {
-      // The right hand operand plays no role in determining the type
-      // of the left hand operand.  A shift of an abstract integer in
-      // a string context gets special treatment, which may be a
-      // language bug.
-      if (subcontext.type != NULL
-	  && subcontext.type->is_string_type()
-	  && tleft->is_abstract())
-	error_at(this->location(), "shift of non-integer operand");
+      // The right hand operand of a shift plays no role in
+      // determining the type of the left hand operand.
     }
   else if (!tleft->is_abstract())
     subcontext.type = tleft;
@@ -5979,10 +6017,21 @@ 
 
   this->left_->determine_type(&subcontext);
 
-  // The context for the right hand operand is the same as for the
-  // left hand operand, except for a shift operator.
   if (is_shift_op)
     {
+      // We may have inherited an unusable type for the shift operand.
+      // Give a useful error if that happened.
+      if (tleft->is_abstract()
+	  && subcontext.type != NULL
+	  && (this->left_->type()->integer_type() == NULL
+	      || (subcontext.type->integer_type() == NULL
+		  && subcontext.type->float_type() == NULL
+		  && subcontext.type->complex_type() == NULL)))
+	this->report_error(("invalid context-determined non-integer type "
+			    "for shift operand"));
+
+      // The context for the right hand operand is the same as for the
+      // left hand operand, except for a shift operator.
       subcontext.type = Type::lookup_integer_type("uint");
       subcontext.may_be_abstract = false;
     }