mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile/internal/types2: add unsafe.Add and unsafe.Slice
This is a port of CL 312212, CL 312591 (except check_test.go), and CL 312790 to types2. Updates #19367. Updates #40481. Change-Id: I58ba0b0dad157baba3f82c909d5eb1268b931be4 Reviewed-on: https://go-review.googlesource.com/c/go/+/312511 Trust: Matthew Dempsky <mdempsky@google.com> Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
050b408dcc
commit
14056d0d00
@ -577,6 +577,25 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||||||
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case _Add:
|
||||||
|
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
|
||||||
|
check.assignment(x, Typ[UnsafePointer], "argument to unsafe.Add")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if !check.isValidIndex(&y, "length", true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = Typ[UnsafePointer]
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, x.typ, y.typ))
|
||||||
|
}
|
||||||
|
|
||||||
case _Alignof:
|
case _Alignof:
|
||||||
// unsafe.Alignof(x T) uintptr
|
// unsafe.Alignof(x T) uintptr
|
||||||
if asTypeParam(x.typ) != nil {
|
if asTypeParam(x.typ) != nil {
|
||||||
@ -654,6 +673,26 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||||||
x.typ = Typ[Uintptr]
|
x.typ = Typ[Uintptr]
|
||||||
// result is constant - no need to record signature
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Slice:
|
||||||
|
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||||
|
typ := asPointer(x.typ)
|
||||||
|
if typ == nil {
|
||||||
|
check.errorf(x, invalidArg+"%s is not a pointer", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if !check.isValidIndex(&y, "length", false) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = NewSlice(typ.base)
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ, y.typ))
|
||||||
|
}
|
||||||
|
|
||||||
case _Assert:
|
case _Assert:
|
||||||
// assert(pred) causes a typechecker error if pred is false.
|
// assert(pred) causes a typechecker error if pred is false.
|
||||||
// The result of assert is the value of pred if there is no error.
|
// The result of assert is the value of pred if there is no error.
|
||||||
|
@ -85,6 +85,9 @@ var builtinCalls = []struct {
|
|||||||
{"make", `var c int32; _ = make([]float64 , 0, c)`, `func([]float64, int, int32) []float64`},
|
{"make", `var c int32; _ = make([]float64 , 0, c)`, `func([]float64, int, int32) []float64`},
|
||||||
{"make", `var l, c uint ; _ = make([]complex128, l, c)`, `func([]complex128, uint, uint) []complex128`},
|
{"make", `var l, c uint ; _ = make([]complex128, l, c)`, `func([]complex128, uint, uint) []complex128`},
|
||||||
|
|
||||||
|
// issue #45667
|
||||||
|
{"make", `const l uint = 1; _ = make([]int, l)`, `func([]int, uint) []int`},
|
||||||
|
|
||||||
{"new", `_ = new(int)`, `func(int) *int`},
|
{"new", `_ = new(int)`, `func(int) *int`},
|
||||||
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||||
|
|
||||||
@ -102,6 +105,10 @@ var builtinCalls = []struct {
|
|||||||
{"recover", `recover()`, `func() interface{}`},
|
{"recover", `recover()`, `func() interface{}`},
|
||||||
{"recover", `_ = recover()`, `func() interface{}`},
|
{"recover", `_ = recover()`, `func() interface{}`},
|
||||||
|
|
||||||
|
{"Add", `var p unsafe.Pointer; _ = unsafe.Add(p, -1.0)`, `func(unsafe.Pointer, int) unsafe.Pointer`},
|
||||||
|
{"Add", `var p unsafe.Pointer; var n uintptr; _ = unsafe.Add(p, n)`, `func(unsafe.Pointer, uintptr) unsafe.Pointer`},
|
||||||
|
{"Add", `_ = unsafe.Add(nil, 0)`, `func(unsafe.Pointer, int) unsafe.Pointer`},
|
||||||
|
|
||||||
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
|
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
|
||||||
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
|
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
@ -111,6 +118,9 @@ var builtinCalls = []struct {
|
|||||||
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
|
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
|
||||||
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
|
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`},
|
||||||
|
{"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`},
|
||||||
|
|
||||||
{"assert", `assert(true)`, `invalid type`}, // constant
|
{"assert", `assert(true)`, `invalid type`}, // constant
|
||||||
{"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
|
{"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
@ -364,14 +364,14 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
|
func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
|
||||||
// f must be a (possibly parenthesized) identifier denoting a built-in
|
// f must be a (possibly parenthesized, possibly qualified)
|
||||||
// (built-ins in package unsafe always produce a constant result and
|
// identifier denoting a built-in (including unsafe's non-constant
|
||||||
// we don't record their signatures, so we don't see qualified idents
|
// functions Add and Slice): record the signature for f and possible
|
||||||
// here): record the signature for f and possible children.
|
// children.
|
||||||
for {
|
for {
|
||||||
check.recordTypeAndValue(f, builtin, sig, nil)
|
check.recordTypeAndValue(f, builtin, sig, nil)
|
||||||
switch p := f.(type) {
|
switch p := f.(type) {
|
||||||
case *syntax.Name:
|
case *syntax.Name, *syntax.SelectorExpr:
|
||||||
return // we're done
|
return // we're done
|
||||||
case *syntax.ParenExpr:
|
case *syntax.ParenExpr:
|
||||||
f = p.X
|
f = p.X
|
||||||
|
@ -250,7 +250,7 @@ func TestCheck(t *testing.T) {
|
|||||||
checkFiles(t, strings.Split(*testFiles, ","), *goVersion, 0, testing.Verbose())
|
checkFiles(t, strings.Split(*testFiles, ","), *goVersion, 0, testing.Verbose())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) go/types has an extra TestLongConstants test
|
// TODO(gri) go/types has extra TestLongConstants and TestIndexRepresentability tests
|
||||||
|
|
||||||
func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "testdata", 75) } // TODO(gri) narrow column tolerance
|
func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, "testdata", 75) } // TODO(gri) narrow column tolerance
|
||||||
func TestExamples(t *testing.T) { testDir(t, "examples", 0) }
|
func TestExamples(t *testing.T) { testDir(t, "examples", 0) }
|
||||||
|
@ -320,19 +320,7 @@ func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64)
|
|||||||
|
|
||||||
var x operand
|
var x operand
|
||||||
check.expr(&x, index)
|
check.expr(&x, index)
|
||||||
if x.mode == invalid {
|
if !check.isValidIndex(&x, "index", false) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// an untyped constant must be representable as Int
|
|
||||||
check.convertUntyped(&x, Typ[Int])
|
|
||||||
if x.mode == invalid {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// the index must be of integer type
|
|
||||||
if !isInteger(x.typ) {
|
|
||||||
check.errorf(&x, invalidArg+"index %s must be integer", &x)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,14 +328,13 @@ func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64)
|
|||||||
return x.typ, -1
|
return x.typ, -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// a constant index i must be in bounds
|
if x.val.Kind() == constant.Unknown {
|
||||||
if constant.Sign(x.val) < 0 {
|
|
||||||
check.errorf(&x, invalidArg+"index %s must not be negative", &x)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, valid := constant.Int64Val(constant.ToInt(x.val))
|
v, ok := constant.Int64Val(x.val)
|
||||||
if !valid || max >= 0 && v >= max {
|
assert(ok)
|
||||||
|
if max >= 0 && v >= max {
|
||||||
if check.conf.CompilerErrorMessages {
|
if check.conf.CompilerErrorMessages {
|
||||||
check.errorf(&x, invalidArg+"array index %s out of bounds [0:%d]", x.val.String(), max)
|
check.errorf(&x, invalidArg+"array index %s out of bounds [0:%d]", x.val.String(), max)
|
||||||
} else {
|
} else {
|
||||||
@ -360,6 +347,40 @@ func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64)
|
|||||||
return x.typ, v
|
return x.typ, v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *Checker) isValidIndex(x *operand, what string, allowNegative bool) bool {
|
||||||
|
if x.mode == invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "a constant index that is untyped is given type int"
|
||||||
|
check.convertUntyped(x, Typ[Int])
|
||||||
|
if x.mode == invalid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "the index x must be of integer type or an untyped constant"
|
||||||
|
if !isInteger(x.typ) {
|
||||||
|
check.errorf(x, invalidArg+"%s %s must be integer", what, x)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == constant_ {
|
||||||
|
// spec: "a constant index must be non-negative ..."
|
||||||
|
if !allowNegative && constant.Sign(x.val) < 0 {
|
||||||
|
check.errorf(x, invalidArg+"%s %s must not be negative", what, x)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "... and representable by a value of type int"
|
||||||
|
if !representableConst(x.val, check, Typ[Int], &x.val) {
|
||||||
|
check.errorf(x, invalidArg+"%s %s overflows int", what, x)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// indexElts checks the elements (elts) of an array or slice composite literal
|
// indexElts checks the elements (elts) of an array or slice composite literal
|
||||||
// against the literal's element type (typ), and the element indices against
|
// against the literal's element type (typ), and the element indices against
|
||||||
// the literal length if known (length >= 0). It returns the length of the
|
// the literal length if known (length >= 0). It returns the length of the
|
||||||
|
@ -35,6 +35,7 @@ func indexes() {
|
|||||||
_ = a[9]
|
_ = a[9]
|
||||||
_ = a[10 /* ERROR "index .* out of bounds" */ ]
|
_ = a[10 /* ERROR "index .* out of bounds" */ ]
|
||||||
_ = a[1 /* ERROR "overflows" */ <<100]
|
_ = a[1 /* ERROR "overflows" */ <<100]
|
||||||
|
_ = a[1<< /* ERROR "constant shift overflow" */ 1000] // no out-of-bounds follow-on error
|
||||||
_ = a[10:]
|
_ = a[10:]
|
||||||
_ = a[:10]
|
_ = a[:10]
|
||||||
_ = a[10:10]
|
_ = a[10:10]
|
||||||
|
@ -136,9 +136,11 @@ const (
|
|||||||
_Recover
|
_Recover
|
||||||
|
|
||||||
// package unsafe
|
// package unsafe
|
||||||
|
_Add
|
||||||
_Alignof
|
_Alignof
|
||||||
_Offsetof
|
_Offsetof
|
||||||
_Sizeof
|
_Sizeof
|
||||||
|
_Slice
|
||||||
|
|
||||||
// testing support
|
// testing support
|
||||||
_Assert
|
_Assert
|
||||||
@ -167,9 +169,11 @@ var predeclaredFuncs = [...]struct {
|
|||||||
_Real: {"real", 1, false, expression},
|
_Real: {"real", 1, false, expression},
|
||||||
_Recover: {"recover", 0, false, statement},
|
_Recover: {"recover", 0, false, statement},
|
||||||
|
|
||||||
|
_Add: {"Add", 2, false, expression},
|
||||||
_Alignof: {"Alignof", 1, false, expression},
|
_Alignof: {"Alignof", 1, false, expression},
|
||||||
_Offsetof: {"Offsetof", 1, false, expression},
|
_Offsetof: {"Offsetof", 1, false, expression},
|
||||||
_Sizeof: {"Sizeof", 1, false, expression},
|
_Sizeof: {"Sizeof", 1, false, expression},
|
||||||
|
_Slice: {"Slice", 2, false, expression},
|
||||||
|
|
||||||
_Assert: {"assert", 1, false, statement},
|
_Assert: {"assert", 1, false, statement},
|
||||||
_Trace: {"trace", 0, true, statement},
|
_Trace: {"trace", 0, true, statement},
|
||||||
|
@ -16,7 +16,7 @@ var sink T
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
sink = make(T, -1) // ERROR "negative buffer argument in make.*|must not be negative"
|
sink = make(T, -1) // ERROR "negative buffer argument in make.*|must not be negative"
|
||||||
sink = make(T, uint64(1<<63)) // ERROR "buffer argument too large in make.*|out of bounds"
|
sink = make(T, uint64(1<<63)) // ERROR "buffer argument too large in make.*|overflows int"
|
||||||
|
|
||||||
sink = make(T, 0.5) // ERROR "constant 0.5 truncated to integer|truncated to int"
|
sink = make(T, 0.5) // ERROR "constant 0.5 truncated to integer|truncated to int"
|
||||||
sink = make(T, 1.0)
|
sink = make(T, 1.0)
|
||||||
|
@ -16,13 +16,13 @@ var sink T
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
sink = make(T, -1) // ERROR "negative size argument in make.*|must not be negative"
|
sink = make(T, -1) // ERROR "negative size argument in make.*|must not be negative"
|
||||||
sink = make(T, uint64(1<<63)) // ERROR "size argument too large in make.*|out of bounds"
|
sink = make(T, uint64(1<<63)) // ERROR "size argument too large in make.*|overflows int"
|
||||||
|
|
||||||
// Test that errors are emitted at call sites, not const declarations
|
// Test that errors are emitted at call sites, not const declarations
|
||||||
const x = -1
|
const x = -1
|
||||||
sink = make(T, x) // ERROR "negative size argument in make.*|must not be negative"
|
sink = make(T, x) // ERROR "negative size argument in make.*|must not be negative"
|
||||||
const y = uint64(1 << 63)
|
const y = uint64(1 << 63)
|
||||||
sink = make(T, y) // ERROR "size argument too large in make.*|out of bounds"
|
sink = make(T, y) // ERROR "size argument too large in make.*|overflows int"
|
||||||
|
|
||||||
sink = make(T, 0.5) // ERROR "constant 0.5 truncated to integer|truncated to int"
|
sink = make(T, 0.5) // ERROR "constant 0.5 truncated to integer|truncated to int"
|
||||||
sink = make(T, 1.0)
|
sink = make(T, 1.0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user