compiler, runtime: implement shifts by signed amounts
Shifting by signed types is a new language feature in Go 1.13.
This requires a patch to the testsuite:
Index: go.test/test/fixedbugs/bug073.go
===================================================================
--- go.test/test/fixedbugs/bug073.go (revision 274239)
+++ go.test/test/fixedbugs/bug073.go (working copy)
@@ -1,4 +1,4 @@
-// errorcheck
+// compile
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
@@ -7,8 +7,8 @@
package main
func main() {
- var s int = 0;
- var x int = 0;
- x = x << s; // ERROR "illegal|inval|shift"
- x = x >> s; // ERROR "illegal|inval|shift"
+ var s int = 0
+ var x int = 0
+ x = x << s // as of 1.13, these are ok
+ x = x >> s // as of 1.13, these are ok
}
Updates golang/go#19113
Change-Id: Ida01c07dff6c5f1f47559083374688ff3800b272
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/190977
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/go/expressions.cc b/go/expressions.cc
index 995a18c..aa0fda0 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -6734,11 +6734,10 @@
this->report_error(_("shift of non-integer operand"));
if (right_type->is_string_type())
- this->report_error(_("shift count not unsigned integer"));
+ this->report_error(_("shift count not integer"));
else if (!right_type->is_abstract()
- && (right_type->integer_type() == NULL
- || !right_type->integer_type()->is_unsigned()))
- this->report_error(_("shift count not unsigned integer"));
+ && right_type->integer_type() == NULL)
+ this->report_error(_("shift count not integer"));
else
{
Numeric_constant nc;
@@ -6746,7 +6745,7 @@
{
mpz_t val;
if (!nc.to_int(&val))
- this->report_error(_("shift count not unsigned integer"));
+ this->report_error(_("shift count not integer"));
else
{
if (mpz_sgn(val) < 0)
@@ -6865,9 +6864,11 @@
// In Go, a shift larger than the size of the type is well-defined.
// This is not true in C, so we need to insert a conditional.
+ // We also need to check for a negative shift count.
if (is_shift_op)
{
go_assert(left_type->integer_type() != NULL);
+ go_assert(right_type->integer_type() != NULL);
int bits = left_type->integer_type()->bits();
@@ -6909,6 +6910,23 @@
ret, overflow, loc);
mpz_clear(bitsval);
}
+
+ if (!right_type->integer_type()->is_unsigned()
+ && (!this->right_->numeric_constant_value(&nc)
+ || nc.to_unsigned_long(&ul) != Numeric_constant::NC_UL_VALID))
+ {
+ Bexpression* zero_expr =
+ gogo->backend()->integer_constant_expression(right_btype, zero);
+ Bexpression* compare =
+ gogo->backend()->binary_expression(OPERATOR_LT, right, zero_expr,
+ loc);
+ const int errcode = RUNTIME_ERROR_SHIFT_BY_NEGATIVE;
+ Bexpression* crash =
+ gogo->runtime_error(errcode, loc)->get_backend(context);
+ Bfunction* bfn = context->function()->func_value()->get_decl();
+ ret = gogo->backend()->conditional_expression(bfn, btype, compare,
+ crash, ret, loc);
+ }
}
// Add checks for division by zero and division overflow as needed.
diff --git a/go/gogo.h b/go/gogo.h
index b3ec629..0abd4b4 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -3745,6 +3745,9 @@
// Go statement with nil function.
static const int RUNTIME_ERROR_GO_NIL = 12;
+// Shift by negative value.
+static const int RUNTIME_ERROR_SHIFT_BY_NEGATIVE = 13;
+
// This is used by some of the langhooks.
extern Gogo* go_get_gogo();
diff --git a/libgo/runtime/go-runtime-error.c b/libgo/runtime/go-runtime-error.c
index c9ccf98..8179e68 100644
--- a/libgo/runtime/go-runtime-error.c
+++ b/libgo/runtime/go-runtime-error.c
@@ -55,7 +55,10 @@
DIVISION_BY_ZERO = 11,
/* Go statement with nil function. */
- GO_NIL = 12
+ GO_NIL = 12,
+
+ /* Shift by negative value. */
+ SHIFT_BY_NEGATIVE = 13
};
extern void __go_runtime_error (int32) __attribute__ ((noreturn));
@@ -112,6 +115,9 @@
runtime_g()->m->throwing = -1;
runtime_throw ("go of nil func value");
+ case SHIFT_BY_NEGATIVE:
+ runtime_panicstring ("negative shift amount");
+
default:
runtime_panicstring ("unknown runtime error");
}