mirror of
https://github.com/golang/go
synced 2024-11-26 04:47:57 -07:00
exp/types/staging: index and slice type checks
Also: handle assignments to the blank identifier. R=rsc CC=golang-dev https://golang.org/cl/6658050
This commit is contained in:
parent
f24323c93e
commit
71588bc2bc
@ -165,6 +165,22 @@ func isZeroConst(x interface{}) bool {
|
|||||||
return ok && i == 0
|
return ok && i == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isNegConst reports whether the value of constant x is < 0.
|
||||||
|
// x must be a non-complex numeric value.
|
||||||
|
//
|
||||||
|
func isNegConst(x interface{}) bool {
|
||||||
|
switch x := x.(type) {
|
||||||
|
case int64:
|
||||||
|
return x < 0
|
||||||
|
case *big.Int:
|
||||||
|
return x.Sign() < 0
|
||||||
|
case *big.Rat:
|
||||||
|
return x.Sign() < 0
|
||||||
|
}
|
||||||
|
unreachable()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// isRepresentableConst reports whether the value of constant x can
|
// isRepresentableConst reports whether the value of constant x can
|
||||||
// be represented as a value of the basic type Typ[as] without loss
|
// be represented as a value of the basic type Typ[as] without loss
|
||||||
// of precision.
|
// of precision.
|
||||||
|
@ -397,11 +397,39 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
|
|||||||
// x.typ is unchanged
|
// x.typ is unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) index(x *operand, e ast.Expr, iota int) {
|
// index checks an index expression for validity. If length >= 0, it is the upper
|
||||||
check.expr(x, e, nil, iota)
|
// bound for the index. The result is a valid constant index >= 0, or a negative
|
||||||
if !isInteger(x.typ) {
|
// value.
|
||||||
check.errorf(x.pos(), "array index %s must be integer", x)
|
//
|
||||||
|
func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
|
||||||
|
var x operand
|
||||||
|
var i int64 // index value, valid if >= 0
|
||||||
|
|
||||||
|
check.expr(&x, index, nil, iota)
|
||||||
|
if !x.isInteger() {
|
||||||
|
check.errorf(x.pos(), "index %s must be integer", &x)
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
if x.mode != constant {
|
||||||
|
return -1 // we cannot check more
|
||||||
|
}
|
||||||
|
// x.mode == constant and the index value must be >= 0
|
||||||
|
if isNegConst(x.val) {
|
||||||
|
check.errorf(x.pos(), "index %s must not be negative", &x)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
if i, ok = x.val.(int64); !ok {
|
||||||
|
// index value doesn't fit into an int64
|
||||||
|
i = length // trigger out of bounds check below if we know length (>= 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if length >= 0 && i >= length {
|
||||||
|
check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) callRecord(x *operand) {
|
func (check *checker) callRecord(x *operand) {
|
||||||
@ -553,20 +581,34 @@ func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cy
|
|||||||
goto Error
|
goto Error
|
||||||
|
|
||||||
case *ast.IndexExpr:
|
case *ast.IndexExpr:
|
||||||
var index operand
|
|
||||||
check.expr(x, e.X, hint, iota)
|
check.expr(x, e.X, hint, iota)
|
||||||
|
|
||||||
|
valid := false
|
||||||
|
length := int64(-1) // valid if >= 0
|
||||||
switch typ := underlying(x.typ).(type) {
|
switch typ := underlying(x.typ).(type) {
|
||||||
case *Array:
|
case *Basic:
|
||||||
check.index(&index, e.Index, iota)
|
if isString(typ) {
|
||||||
|
valid = true
|
||||||
if x.mode == constant {
|
if x.mode == constant {
|
||||||
// TODO(gri) range check
|
length = int64(len(x.val.(string)))
|
||||||
|
}
|
||||||
|
// an indexed string always yields a byte value
|
||||||
|
// (not a constant) even if the string and the
|
||||||
|
// index are constant
|
||||||
|
x.mode = value
|
||||||
|
x.typ = Typ[Byte]
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
valid = true
|
||||||
|
length = typ.Len
|
||||||
|
if x.mode != variable {
|
||||||
|
x.mode = value
|
||||||
}
|
}
|
||||||
// TODO(gri) only variable if array is variable
|
|
||||||
x.mode = variable
|
|
||||||
x.typ = typ.Elt
|
x.typ = typ.Elt
|
||||||
|
|
||||||
case *Slice:
|
case *Slice:
|
||||||
check.index(&index, e.Index, iota)
|
valid = true
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
x.typ = typ.Elt
|
x.typ = typ.Elt
|
||||||
|
|
||||||
@ -574,44 +616,76 @@ func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cy
|
|||||||
// TODO(gri) check index type
|
// TODO(gri) check index type
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
x.typ = typ.Elt
|
x.typ = typ.Elt
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
if !valid {
|
||||||
check.invalidOp(e.Pos(), "cannot index %s", x.typ)
|
check.invalidOp(x.pos(), "cannot index %s", x)
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.Index == nil {
|
||||||
|
check.invalidAST(e.Pos(), "missing index expression for %s", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.index(e.Index, length, iota)
|
||||||
|
// ok to continue
|
||||||
|
|
||||||
case *ast.SliceExpr:
|
case *ast.SliceExpr:
|
||||||
var lo, hi operand
|
|
||||||
check.expr(x, e.X, hint, iota)
|
check.expr(x, e.X, hint, iota)
|
||||||
if e.Low != nil {
|
|
||||||
check.index(&lo, e.Low, iota)
|
valid := false
|
||||||
} else {
|
length := int64(-1) // valid if >= 0
|
||||||
lo.mode = constant
|
switch typ := underlying(x.typ).(type) {
|
||||||
lo.expr = nil // TODO(gri) should not use nil here
|
case *Basic:
|
||||||
lo.typ = Typ[UntypedInt]
|
if isString(typ) {
|
||||||
lo.val = zeroConst
|
valid = true
|
||||||
|
if x.mode == constant {
|
||||||
|
length = int64(len(x.val.(string))) + 1 // +1 for slice
|
||||||
}
|
}
|
||||||
if e.High != nil {
|
// a sliced string always yields a string value
|
||||||
check.index(&hi, e.High, iota)
|
// of the same type as the original string (not
|
||||||
} else {
|
// a constant) even if the string and the indexes
|
||||||
unimplemented()
|
// are constant
|
||||||
|
x.mode = value
|
||||||
|
// x.typ doesn't change
|
||||||
}
|
}
|
||||||
switch typ := x.typ.(type) {
|
|
||||||
case *Array:
|
case *Array:
|
||||||
unimplemented()
|
valid = true
|
||||||
case *Slice:
|
length = typ.Len + 1 // +1 for slice
|
||||||
assert(x.mode == variable)
|
if x.mode != variable {
|
||||||
// x.typ does not change
|
check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
|
||||||
case *Pointer:
|
|
||||||
if typ, ok := underlying(typ.Base).(*Array); ok {
|
|
||||||
// TODO(gri) array slice
|
|
||||||
_ = typ
|
|
||||||
}
|
|
||||||
unimplemented()
|
|
||||||
default:
|
|
||||||
check.invalidOp(e.Pos(), "cannot slice %s", x.typ)
|
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
|
x.typ = &Slice{Elt: typ.Elt}
|
||||||
|
|
||||||
|
case *Slice:
|
||||||
|
valid = true
|
||||||
|
x.mode = variable
|
||||||
|
// x.typ doesn't change
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
check.invalidOp(x.pos(), "cannot slice %s", x)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var lo int64
|
||||||
|
if e.Low != nil {
|
||||||
|
lo = check.index(e.Low, length, iota)
|
||||||
|
}
|
||||||
|
|
||||||
|
var hi int64 = length
|
||||||
|
if e.High != nil {
|
||||||
|
hi = check.index(e.High, length, iota)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hi >= 0 && lo > hi {
|
||||||
|
check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
|
||||||
case *ast.TypeAssertExpr:
|
case *ast.TypeAssertExpr:
|
||||||
check.expr(x, e.X, hint, iota)
|
check.expr(x, e.X, hint, iota)
|
||||||
|
@ -108,6 +108,15 @@ func (check *checker) assignment(lhs ast.Expr, x *operand, decl bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) assign1to1(lhs, rhs ast.Expr, decl bool, iota int) {
|
func (check *checker) assign1to1(lhs, rhs ast.Expr, decl bool, iota int) {
|
||||||
|
ident, _ := lhs.(*ast.Ident)
|
||||||
|
|
||||||
|
if ident != nil && ident.Name == "_" {
|
||||||
|
// anything can be assigned to a blank identifier - check rhs only
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, rhs, nil, iota)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !decl {
|
if !decl {
|
||||||
// regular assignment - start with lhs[0] to obtain a type hint
|
// regular assignment - start with lhs[0] to obtain a type hint
|
||||||
var z operand
|
var z operand
|
||||||
@ -127,8 +136,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, decl bool, iota int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// declaration - rhs may or may not be typed yet
|
// declaration - rhs may or may not be typed yet
|
||||||
ident, ok := lhs.(*ast.Ident)
|
if ident == nil {
|
||||||
if !ok {
|
|
||||||
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
|
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ var (
|
|||||||
s4 = s + t
|
s4 = s + t
|
||||||
s5 = s /* ERROR "invalid operation" */ / t
|
s5 = s /* ERROR "invalid operation" */ / t
|
||||||
s6 = array[t1]
|
s6 = array[t1]
|
||||||
s7 = array[x /* ERROR "array index" */]
|
s7 = array[x /* ERROR "index" */]
|
||||||
s8 = &a
|
s8 = &a
|
||||||
s10 = &42 /* ERROR "cannot take address" */
|
s10 = &42 /* ERROR "cannot take address" */
|
||||||
s11 = &v
|
s11 = &v
|
||||||
@ -56,7 +56,7 @@ var (
|
|||||||
t4 string = s + t
|
t4 string = s + t
|
||||||
t5 string = s /* ERROR "invalid operation" */ / t
|
t5 string = s /* ERROR "invalid operation" */ / t
|
||||||
t6 byte = array[t1]
|
t6 byte = array[t1]
|
||||||
t7 byte = array[x /* ERROR "array index" */]
|
t7 byte = array[x /* ERROR "index" */]
|
||||||
t8 *int = & /* ERROR "cannot assign" */ a
|
t8 *int = & /* ERROR "cannot assign" */ a
|
||||||
t10 *int = &42 /* ERROR "cannot take address" */
|
t10 *int = &42 /* ERROR "cannot take address" */
|
||||||
t11 *complex64 = &v
|
t11 *complex64 = &v
|
||||||
|
74
src/pkg/exp/types/staging/testdata/expr3.src
vendored
74
src/pkg/exp/types/staging/testdata/expr3.src
vendored
@ -2,10 +2,12 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// shifts
|
// various expressions
|
||||||
|
|
||||||
package expr3
|
package expr3
|
||||||
|
|
||||||
|
// TODO(gri) Move the code below into function "shifts" once we check
|
||||||
|
// declarations with initilizations inside functions.
|
||||||
var (
|
var (
|
||||||
i0 int
|
i0 int
|
||||||
u0 uint
|
u0 uint
|
||||||
@ -41,3 +43,73 @@ var (
|
|||||||
v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
|
v float32 = 1 /* ERROR "must be integer" */ <<s // illegal: 1 has type float32, cannot shift
|
||||||
w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
|
w int64 = 1.0<<33 // 1.0<<33 is a constant shift expression
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(gri) The error messages below depond on adjusting the spec
|
||||||
|
// to reflect what gc is doing at the moment (the spec
|
||||||
|
// asks for run-time errors at the moment - see issue 4231).
|
||||||
|
//
|
||||||
|
func indexes() {
|
||||||
|
_ = 1 /* ERROR "cannot index" */ [0]
|
||||||
|
_ = indexes /* ERROR "cannot index" */ [0]
|
||||||
|
_ = ( /* ERROR "cannot slice" */ 12 + 3)[1:2]
|
||||||
|
|
||||||
|
var a [10]int
|
||||||
|
_ = a[true /* ERROR "must be integer" */ ]
|
||||||
|
_ = a["foo" /* ERROR "must be integer" */ ]
|
||||||
|
_ = a[1.1 /* ERROR "must be integer" */ ]
|
||||||
|
_ = a[1.0]
|
||||||
|
_ = a[- /* ERROR "index .* negative" */ 1]
|
||||||
|
_ = a[- /* ERROR "index .* negative" */ 1 :]
|
||||||
|
_ = a[: - /* ERROR "index .* negative" */ 1]
|
||||||
|
var a0 int
|
||||||
|
a0 = a[0]
|
||||||
|
var a1 int32
|
||||||
|
a1 = a /* ERROR "cannot assign" */ [1]
|
||||||
|
_ = a[9]
|
||||||
|
_ = a[10 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = a[10:]
|
||||||
|
_ = a[:10]
|
||||||
|
_ = a[10:10]
|
||||||
|
_ = a[11 /* ERROR "index .* out of bounds" */ :]
|
||||||
|
_ = a[: 11 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
|
||||||
|
var b [0]int
|
||||||
|
_ = b[0 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = b[:]
|
||||||
|
_ = b[0:]
|
||||||
|
_ = b[:0]
|
||||||
|
_ = b[0:0]
|
||||||
|
|
||||||
|
var s []int
|
||||||
|
_ = s[- /* ERROR "index .* negative" */ 1]
|
||||||
|
_ = s[- /* ERROR "index .* negative" */ 1 :]
|
||||||
|
_ = s[: - /* ERROR "index .* negative" */ 1]
|
||||||
|
_ = s[0]
|
||||||
|
_ = s[1 : 2]
|
||||||
|
_ = s[2 /* ERROR "inverted slice range" */ : 1]
|
||||||
|
_ = s[2 :]
|
||||||
|
|
||||||
|
var t string
|
||||||
|
_ = t[- /* ERROR "index .* negative" */ 1]
|
||||||
|
_ = t[- /* ERROR "index .* negative" */ 1 :]
|
||||||
|
_ = t[: - /* ERROR "index .* negative" */ 1]
|
||||||
|
var t0 byte
|
||||||
|
t0 = t[0]
|
||||||
|
var t1 rune
|
||||||
|
t1 = t /* ERROR "cannot assign" */ [2]
|
||||||
|
_ = ("foo" + "bar")[5]
|
||||||
|
_ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
|
||||||
|
const c = "foo"
|
||||||
|
_ = c[- /* ERROR "index .* negative" */ 1]
|
||||||
|
_ = c[- /* ERROR "index .* negative" */ 1 :]
|
||||||
|
_ = c[: - /* ERROR "index .* negative" */ 1]
|
||||||
|
var c0 byte
|
||||||
|
c0 = c[0]
|
||||||
|
var c2 float32
|
||||||
|
c2 = c /* ERROR "cannot assign" */ [2]
|
||||||
|
_ = c[3 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
_ = ""[0 /* ERROR "index .* out of bounds" */ ]
|
||||||
|
|
||||||
|
_ = s[1<<30] // no compile-time error here
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user