зеркало из https://github.com/golang/tools.git
go/ssa: allows right operand of a shift to be signed.
Removes the forced conversion of the right operand of a shift to an unsigned type. This allows for clients to correctly model the runtime panic when a signed shift count is negative. Fixes golang/go#51363 Change-Id: If59000eeb503fd45cdc6d4143dcc249242e7a957 Reviewed-on: https://go-review.googlesource.com/c/tools/+/387995 Trust: Tim King <taking@google.com> Run-TryBot: Tim King <taking@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com> Reviewed-by: Dominik Honnef <dominik@honnef.co> Trust: Dominik Honnef <dominik@honnef.co> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Родитель
9ffa3ad372
Коммит
acdddf6756
|
@ -74,9 +74,16 @@ func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.
|
||||||
case token.SHL, token.SHR:
|
case token.SHL, token.SHR:
|
||||||
x = emitConv(f, x, t)
|
x = emitConv(f, x, t)
|
||||||
// y may be signed or an 'untyped' constant.
|
// y may be signed or an 'untyped' constant.
|
||||||
// TODO(adonovan): whence signed values?
|
|
||||||
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
|
// There is a runtime panic if y is signed and <0. Instead of inserting a check for y<0
|
||||||
y = emitConv(f, y, types.Typ[types.Uint64])
|
// and converting to an unsigned value (like the compiler) leave y as is.
|
||||||
|
|
||||||
|
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
|
||||||
|
// Untyped conversion:
|
||||||
|
// Spec https://go.dev/ref/spec#Operators:
|
||||||
|
// The right operand in a shift expression must have integer type or be an untyped constant
|
||||||
|
// representable by a value of type uint.
|
||||||
|
y = emitConv(f, y, types.Typ[types.Uint])
|
||||||
}
|
}
|
||||||
|
|
||||||
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
|
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
|
||||||
|
|
|
@ -109,6 +109,7 @@ var gorootTestTests = []string{
|
||||||
var testdataTests = []string{
|
var testdataTests = []string{
|
||||||
"boundmeth.go",
|
"boundmeth.go",
|
||||||
"complit.go",
|
"complit.go",
|
||||||
|
"convert.go",
|
||||||
"coverage.go",
|
"coverage.go",
|
||||||
"defer.go",
|
"defer.go",
|
||||||
"fieldprom.go",
|
"fieldprom.go",
|
||||||
|
|
|
@ -137,6 +137,26 @@ func asUint64(x value) uint64 {
|
||||||
panic(fmt.Sprintf("cannot convert %T to uint64", x))
|
panic(fmt.Sprintf("cannot convert %T to uint64", x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// asUnsigned returns the value of x, which must be an integer type, as its equivalent unsigned type,
|
||||||
|
// and returns true if x is non-negative.
|
||||||
|
func asUnsigned(x value) (value, bool) {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int:
|
||||||
|
return uint(x), x >= 0
|
||||||
|
case int8:
|
||||||
|
return uint8(x), x >= 0
|
||||||
|
case int16:
|
||||||
|
return uint16(x), x >= 0
|
||||||
|
case int32:
|
||||||
|
return uint32(x), x >= 0
|
||||||
|
case int64:
|
||||||
|
return uint64(x), x >= 0
|
||||||
|
case uint, uint8, uint32, uint64, uintptr:
|
||||||
|
return x, true
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot convert %T to unsigned", x))
|
||||||
|
}
|
||||||
|
|
||||||
// zero returns a new "zero" value of the specified type.
|
// zero returns a new "zero" value of the specified type.
|
||||||
func zero(t types.Type) value {
|
func zero(t types.Type) value {
|
||||||
switch t := t.(type) {
|
switch t := t.(type) {
|
||||||
|
@ -576,7 +596,11 @@ func binop(op token.Token, t types.Type, x, y value) value {
|
||||||
}
|
}
|
||||||
|
|
||||||
case token.SHL:
|
case token.SHL:
|
||||||
y := asUint64(y)
|
u, ok := asUnsigned(y)
|
||||||
|
if !ok {
|
||||||
|
panic("negative shift amount")
|
||||||
|
}
|
||||||
|
y := asUint64(u)
|
||||||
switch x.(type) {
|
switch x.(type) {
|
||||||
case int:
|
case int:
|
||||||
return x.(int) << y
|
return x.(int) << y
|
||||||
|
@ -603,7 +627,11 @@ func binop(op token.Token, t types.Type, x, y value) value {
|
||||||
}
|
}
|
||||||
|
|
||||||
case token.SHR:
|
case token.SHR:
|
||||||
y := asUint64(y)
|
u, ok := asUnsigned(y)
|
||||||
|
if !ok {
|
||||||
|
panic("negative shift amount")
|
||||||
|
}
|
||||||
|
y := asUint64(u)
|
||||||
switch x.(type) {
|
switch x.(type) {
|
||||||
case int:
|
case int:
|
||||||
return x.(int) >> y
|
return x.(int) >> y
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Test conversion operations.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func left(x int) { _ = 1 << x }
|
||||||
|
func right(x int) { _ = 1 >> x }
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wantPanic(
|
||||||
|
func() {
|
||||||
|
left(-1)
|
||||||
|
},
|
||||||
|
"runtime error: negative shift amount",
|
||||||
|
)
|
||||||
|
wantPanic(
|
||||||
|
func() {
|
||||||
|
right(-1)
|
||||||
|
},
|
||||||
|
"runtime error: negative shift amount",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wantPanic(fn func(), s string) {
|
||||||
|
defer func() {
|
||||||
|
err := recover()
|
||||||
|
if err == nil {
|
||||||
|
panic("expected panic")
|
||||||
|
}
|
||||||
|
if got := err.(error).Error(); got != s {
|
||||||
|
panic("expected panic " + s + " got " + got)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fn()
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче