]> Cypherpunks repositories - gostls13.git/commitdiff
exp/types/staging: index and slice type checks
authorRobert Griesemer <gri@golang.org>
Tue, 16 Oct 2012 17:20:03 +0000 (10:20 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 16 Oct 2012 17:20:03 +0000 (10:20 -0700)
Also: handle assignments to the blank identifier.

R=rsc
CC=golang-dev
https://golang.org/cl/6658050

src/pkg/exp/types/staging/const.go
src/pkg/exp/types/staging/expr.go
src/pkg/exp/types/staging/stmt.go
src/pkg/exp/types/staging/testdata/decls1.src
src/pkg/exp/types/staging/testdata/expr3.src

index 79b89e19153f18168cfa6724caea8a6bad8107fe..e817f1fd85860733c383e0c55208e29f72df3fc2 100644 (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.
index 7c7445b23653de11e3880eb7757a1c67dac193e5..560b16c16f363e08ee445679ee72765651dc8519 100644 (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 *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:
-                       check.index(&index, e.Index, iota)
-                       if x.mode == constant {
-                               // TODO(gri) range check
+                       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,43 +616,75 @@ 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)
+
+               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 {
-                       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
+                       lo = check.index(e.Low, length, iota)
                }
+
+               var hi int64 = length
                if e.High != nil {
-                       check.index(&hi, e.High, iota)
-               } else {
-                       unimplemented()
+                       hi = check.index(e.High, length, iota)
                }
-               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)
-                       goto Error
+
+               if hi >= 0 && lo > hi {
+                       check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
+                       // ok to continue
                }
 
        case *ast.TypeAssertExpr:
index 9a2ea74895c5eb2b452c646c0eeecd0e144be169..be5caa1e3645d64d703d9d903e041a755e357fb4 100644 (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
        }
index 6accee61ed5a40e812f710a7112d77c16230217a..32859fc0ee5187caac59b2f825b255fa680a8486 100644 (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
index ecdb54f4bc92157c571f758ffde98a6ffd5ece34..5635e12eeb0ba0b25e5e87f3b6a74874f4e0c243 100644 (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
+}