1
0
mirror of https://github.com/golang/go synced 2024-11-26 02:37:58 -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:
Robert Griesemer 2012-10-16 10:20:03 -07:00
parent f24323c93e
commit 71588bc2bc
5 changed files with 216 additions and 46 deletions

View File

@ -165,6 +165,22 @@ func isZeroConst(x interface{}) bool {
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
// be represented as a value of the basic type Typ[as] without loss
// of precision.

View File

@ -397,11 +397,39 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
// x.typ is unchanged
}
func (check *checker) index(x *operand, e ast.Expr, iota int) {
check.expr(x, e, nil, iota)
if !isInteger(x.typ) {
check.errorf(x.pos(), "array index %s must be integer", x)
// index checks an index expression for validity. If length >= 0, it is the upper
// bound for the index. The result is a valid constant index >= 0, or a negative
// value.
//
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) {
@ -553,20 +581,34 @@ func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cy
goto Error
case *ast.IndexExpr:
var index operand
check.expr(x, e.X, hint, iota)
valid := false
length := int64(-1) // valid if >= 0
switch typ := underlying(x.typ).(type) {
case *Array:
check.index(&index, e.Index, iota)
case *Basic:
if isString(typ) {
valid = true
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
case *Slice:
check.index(&index, e.Index, iota)
valid = true
x.mode = variable
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
x.mode = variable
x.typ = typ.Elt
return
}
default:
check.invalidOp(e.Pos(), "cannot index %s", x.typ)
if !valid {
check.invalidOp(x.pos(), "cannot index %s", x)
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:
var lo, hi operand
check.expr(x, e.X, hint, iota)
if e.Low != nil {
check.index(&lo, e.Low, iota)
} else {
lo.mode = constant
lo.expr = nil // TODO(gri) should not use nil here
lo.typ = Typ[UntypedInt]
lo.val = zeroConst
valid := false
length := int64(-1) // valid if >= 0
switch typ := underlying(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
if x.mode == constant {
length = int64(len(x.val.(string))) + 1 // +1 for slice
}
if e.High != nil {
check.index(&hi, e.High, iota)
} else {
unimplemented()
// a sliced string always yields a string value
// of the same type as the original string (not
// a constant) even if the string and the indexes
// are constant
x.mode = value
// x.typ doesn't change
}
switch typ := x.typ.(type) {
case *Array:
unimplemented()
case *Slice:
assert(x.mode == variable)
// x.typ does not change
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)
valid = true
length = typ.Len + 1 // +1 for slice
if x.mode != variable {
check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
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:
check.expr(x, e.X, hint, iota)

View File

@ -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) {
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 {
// regular assignment - start with lhs[0] to obtain a type hint
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
ident, ok := lhs.(*ast.Ident)
if !ok {
if ident == nil {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
return
}

View File

@ -37,7 +37,7 @@ var (
s4 = s + t
s5 = s /* ERROR "invalid operation" */ / t
s6 = array[t1]
s7 = array[x /* ERROR "array index" */]
s7 = array[x /* ERROR "index" */]
s8 = &a
s10 = &42 /* ERROR "cannot take address" */
s11 = &v
@ -56,7 +56,7 @@ var (
t4 string = s + t
t5 string = s /* ERROR "invalid operation" */ / t
t6 byte = array[t1]
t7 byte = array[x /* ERROR "array index" */]
t7 byte = array[x /* ERROR "index" */]
t8 *int = & /* ERROR "cannot assign" */ a
t10 *int = &42 /* ERROR "cannot take address" */
t11 *complex64 = &v

View File

@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// shifts
// various expressions
package expr3
// TODO(gri) Move the code below into function "shifts" once we check
// declarations with initilizations inside functions.
var (
i0 int
u0 uint
@ -41,3 +43,73 @@ var (
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
)
// 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
}