Commit 71588bc2 authored by Robert Griesemer's avatar Robert Griesemer

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
parent f24323c9
...@@ -165,6 +165,22 @@ func isZeroConst(x interface{}) bool { ...@@ -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) { ...@@ -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 ...@@ -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 *Basic:
if isString(typ) {
valid = true
if x.mode == constant {
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: case *Array:
check.index(&index, e.Index, iota) valid = true
if x.mode == constant { length = typ.Len
// TODO(gri) range check 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,43 +616,75 @@ func (check *checker) exprOrType(x *operand, e ast.Expr, hint Type, iota int, cy ...@@ -574,43 +616,75 @@ 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)
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
}
// 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
}
case *Array:
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 { if e.Low != nil {
check.index(&lo, e.Low, iota) lo = check.index(e.Low, length, iota)
} else {
lo.mode = constant
lo.expr = nil // TODO(gri) should not use nil here
lo.typ = Typ[UntypedInt]
lo.val = zeroConst
} }
var hi int64 = length
if e.High != nil { if e.High != nil {
check.index(&hi, e.High, iota) hi = check.index(e.High, length, iota)
} else {
unimplemented()
} }
switch typ := x.typ.(type) {
case *Array: if hi >= 0 && lo > hi {
unimplemented() check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
case *Slice: // ok to continue
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)
goto Error
} }
case *ast.TypeAssertExpr: case *ast.TypeAssertExpr:
......
...@@ -108,6 +108,15 @@ func (check *checker) assignment(lhs ast.Expr, x *operand, decl bool) { ...@@ -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) { ...@@ -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 ( ...@@ -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 ( ...@@ -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
......
...@@ -2,10 +2,12 @@ ...@@ -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 ( ...@@ -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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment