]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/types2: factor out index/slice expr handling
authorRobert Griesemer <gri@golang.org>
Thu, 8 Apr 2021 00:47:14 +0000 (17:47 -0700)
committerRobert Griesemer <gri@golang.org>
Sat, 10 Apr 2021 19:02:05 +0000 (19:02 +0000)
First step towards lightening the load of Checker.exprInternal by
factoring out the code for index and slice expressions; incl. moving
a couple of related methods (Checker.index, Checker.indexedElts).

The code for handling index/slice expressions is copied 1:1 but
occurrences of "goto Error" are replaced by "x.mode = invalid"
followed by a "return".

Change-Id: I44048dcc4851dc5e24f5f169c17f536a37a6a676
Reviewed-on: https://go-review.googlesource.com/c/go/+/308370
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/index.go [new file with mode: 0644]

index 7d2240d41e4547b264dc0d9d14af6d096c458a2f..4a2e658a632bacfebea849aa8f57f0f65411aac5 100644 (file)
@@ -1043,104 +1043,6 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
        // x.typ is unchanged
 }
 
-// index checks an index expression for validity.
-// If max >= 0, it is the upper bound for index.
-// If the result typ is != Typ[Invalid], index is valid and typ is its (possibly named) integer type.
-// If the result val >= 0, index is valid and val is its constant int value.
-func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64) {
-       typ = Typ[Invalid]
-       val = -1
-
-       var x operand
-       check.expr(&x, index)
-       if x.mode == invalid {
-               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
-       }
-
-       if x.mode != constant_ {
-               return x.typ, -1
-       }
-
-       // a constant index i must be in bounds
-       if constant.Sign(x.val) < 0 {
-               check.errorf(&x, invalidArg+"index %s must not be negative", &x)
-               return
-       }
-
-       v, valid := constant.Int64Val(constant.ToInt(x.val))
-       if !valid || max >= 0 && v >= max {
-               if check.conf.CompilerErrorMessages {
-                       check.errorf(&x, "array index %s out of bounds [0:%d]", x.val.String(), max)
-               } else {
-                       check.errorf(&x, "index %s is out of bounds", &x)
-               }
-               return
-       }
-
-       // 0 <= v [ && v < max ]
-       return Typ[Int], v
-}
-
-// indexElts checks the elements (elts) of an array or slice composite literal
-// 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
-// literal (maximum index value + 1).
-//
-func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 {
-       visited := make(map[int64]bool, len(elts))
-       var index, max int64
-       for _, e := range elts {
-               // determine and check index
-               validIndex := false
-               eval := e
-               if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
-                       if typ, i := check.index(kv.Key, length); typ != Typ[Invalid] {
-                               if i >= 0 {
-                                       index = i
-                                       validIndex = true
-                               } else {
-                                       check.errorf(e, "index %s must be integer constant", kv.Key)
-                               }
-                       }
-                       eval = kv.Value
-               } else if length >= 0 && index >= length {
-                       check.errorf(e, "index %d is out of bounds (>= %d)", index, length)
-               } else {
-                       validIndex = true
-               }
-
-               // if we have a valid index, check for duplicate entries
-               if validIndex {
-                       if visited[index] {
-                               check.errorf(e, "duplicate index %d in array or slice literal", index)
-                       }
-                       visited[index] = true
-               }
-               index++
-               if index > max {
-                       max = index
-               }
-
-               // check element against composite literal element type
-               var x operand
-               check.exprWithHint(&x, eval, typ)
-               check.assignment(&x, typ, "array or slice literal")
-       }
-       return max
-}
-
 // exprKind describes the kind of an expression; the kind
 // determines if an expression is valid in 'statement context'.
 type exprKind int
@@ -1485,291 +1387,17 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
                check.selector(x, e)
 
        case *syntax.IndexExpr:
-               check.exprOrType(x, e.X)
+               check.indexExpr(x, e)
                if x.mode == invalid {
-                       check.use(e.Index)
-                       goto Error
-               }
-
-               if x.mode == typexpr {
-                       // type instantiation
-                       x.mode = invalid
-                       x.typ = check.varType(e)
-                       if x.typ != Typ[Invalid] {
-                               x.mode = typexpr
-                       }
-                       return expression
-               }
-
-               if x.mode == value {
-                       if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
-                               // function instantiation
-                               check.funcInst(x, e)
-                               return expression
-                       }
-               }
-
-               // ordinary index expression
-               valid := false
-               length := int64(-1) // valid if >= 0
-               switch typ := optype(x.typ).(type) {
-               case *Basic:
-                       if isString(typ) {
-                               valid = true
-                               if x.mode == constant_ {
-                                       length = int64(len(constant.StringVal(x.val)))
-                               }
-                               // 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 = universeByte // use 'byte' name
-                       }
-
-               case *Array:
-                       valid = true
-                       length = typ.len
-                       if x.mode != variable {
-                               x.mode = value
-                       }
-                       x.typ = typ.elem
-
-               case *Pointer:
-                       if typ := asArray(typ.base); typ != nil {
-                               valid = true
-                               length = typ.len
-                               x.mode = variable
-                               x.typ = typ.elem
-                       }
-
-               case *Slice:
-                       valid = true
-                       x.mode = variable
-                       x.typ = typ.elem
-
-               case *Map:
-                       var key operand
-                       check.expr(&key, e.Index)
-                       check.assignment(&key, typ.key, "map index")
-                       // ok to continue even if indexing failed - map element type is known
-                       x.mode = mapindex
-                       x.typ = typ.elem
-                       x.expr = e
-                       return expression
-
-               case *Sum:
-                       // A sum type can be indexed if all of the sum's types
-                       // support indexing and have the same index and element
-                       // type. Special rules apply for maps in the sum type.
-                       var tkey, telem Type // key is for map types only
-                       nmaps := 0           // number of map types in sum type
-                       if typ.is(func(t Type) bool {
-                               var e Type
-                               switch t := under(t).(type) {
-                               case *Basic:
-                                       if isString(t) {
-                                               e = universeByte
-                                       }
-                               case *Array:
-                                       e = t.elem
-                               case *Pointer:
-                                       if t := asArray(t.base); t != nil {
-                                               e = t.elem
-                                       }
-                               case *Slice:
-                                       e = t.elem
-                               case *Map:
-                                       // If there are multiple maps in the sum type,
-                                       // they must have identical key types.
-                                       // TODO(gri) We may be able to relax this rule
-                                       // but it becomes complicated very quickly.
-                                       if tkey != nil && !Identical(t.key, tkey) {
-                                               return false
-                                       }
-                                       tkey = t.key
-                                       e = t.elem
-                                       nmaps++
-                               case *TypeParam:
-                                       check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
-                               case *instance:
-                                       panic("unimplemented")
-                               }
-                               if e == nil || telem != nil && !Identical(e, telem) {
-                                       return false
-                               }
-                               telem = e
-                               return true
-                       }) {
-                               // If there are maps, the index expression must be assignable
-                               // to the map key type (as for simple map index expressions).
-                               if nmaps > 0 {
-                                       var key operand
-                                       check.expr(&key, e.Index)
-                                       check.assignment(&key, tkey, "map index")
-                                       // ok to continue even if indexing failed - map element type is known
-
-                                       // If there are only maps, we are done.
-                                       if nmaps == len(typ.types) {
-                                               x.mode = mapindex
-                                               x.typ = telem
-                                               x.expr = e
-                                               return expression
-                                       }
-
-                                       // Otherwise we have mix of maps and other types. For
-                                       // now we require that the map key be an integer type.
-                                       // TODO(gri) This is probably not good enough.
-                                       valid = isInteger(tkey)
-                                       // avoid 2nd indexing error if indexing failed above
-                                       if !valid && key.mode == invalid {
-                                               goto Error
-                                       }
-                                       x.mode = value // map index expressions are not addressable
-                               } else {
-                                       // no maps
-                                       valid = true
-                                       x.mode = variable
-                               }
-                               x.typ = telem
-                       }
-               }
-
-               if !valid {
-                       check.errorf(x, invalidOp+"cannot index %s", x)
-                       goto Error
-               }
-
-               if e.Index == nil {
-                       check.errorf(e, invalidAST+"missing index for %s", x)
                        goto Error
                }
 
-               index := e.Index
-               if l, _ := index.(*syntax.ListExpr); l != nil {
-                       if n := len(l.ElemList); n <= 1 {
-                               check.errorf(e, invalidAST+"invalid use of ListExpr for index expression %v with %d indices", e, n)
-                               goto Error
-                       }
-                       // len(l.ElemList) > 1
-                       check.error(l.ElemList[1], invalidOp+"more than one index")
-                       index = l.ElemList[0] // continue with first index
-               }
-
-               // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
-               // the element type may be accessed before it's set. Make sure we have
-               // a valid type.
-               if x.typ == nil {
-                       x.typ = Typ[Invalid]
-               }
-
-               check.index(index, length)
-               // ok to continue
-
        case *syntax.SliceExpr:
-               check.expr(x, e.X)
+               check.sliceExpr(x, e)
                if x.mode == invalid {
-                       check.use(e.Index[:]...)
-                       goto Error
-               }
-
-               valid := false
-               length := int64(-1) // valid if >= 0
-               switch typ := optype(x.typ).(type) {
-               case *Basic:
-                       if isString(typ) {
-                               if e.Full {
-                                       check.error(x, invalidOp+"3-index slice of string")
-                                       goto Error
-                               }
-                               valid = true
-                               if x.mode == constant_ {
-                                       length = int64(len(constant.StringVal(x.val)))
-                               }
-                               // spec: "For untyped string operands the result
-                               // is a non-constant value of type string."
-                               if typ.kind == UntypedString {
-                                       x.typ = Typ[String]
-                               }
-                       }
-
-               case *Array:
-                       valid = true
-                       length = typ.len
-                       if x.mode != variable {
-                               check.errorf(x, invalidOp+"%s (slice of unaddressable value)", x)
-                               goto Error
-                       }
-                       x.typ = &Slice{elem: typ.elem}
-
-               case *Pointer:
-                       if typ := asArray(typ.base); typ != nil {
-                               valid = true
-                               length = typ.len
-                               x.typ = &Slice{elem: typ.elem}
-                       }
-
-               case *Slice:
-                       valid = true
-                       // x.typ doesn't change
-
-               case *Sum, *TypeParam:
-                       check.error(x, "generic slice expressions not yet implemented")
-                       goto Error
-               }
-
-               if !valid {
-                       check.errorf(x, invalidOp+"cannot slice %s", x)
                        goto Error
                }
 
-               x.mode = value
-
-               // spec: "Only the first index may be omitted; it defaults to 0."
-               if e.Full && (e.Index[1] == nil || e.Index[2] == nil) {
-                       check.error(e, invalidAST+"2nd and 3rd index required in 3-index slice")
-                       goto Error
-               }
-
-               // check indices
-               var ind [3]int64
-               for i, expr := range e.Index {
-                       x := int64(-1)
-                       switch {
-                       case expr != nil:
-                               // The "capacity" is only known statically for strings, arrays,
-                               // and pointers to arrays, and it is the same as the length for
-                               // those types.
-                               max := int64(-1)
-                               if length >= 0 {
-                                       max = length + 1
-                               }
-                               if _, v := check.index(expr, max); v >= 0 {
-                                       x = v
-                               }
-                       case i == 0:
-                               // default is 0 for the first index
-                               x = 0
-                       case length >= 0:
-                               // default is length (== capacity) otherwise
-                               x = length
-                       }
-                       ind[i] = x
-               }
-
-               // constant indices must be in range
-               // (check.index already checks that existing indices >= 0)
-       L:
-               for i, x := range ind[:len(ind)-1] {
-                       if x > 0 {
-                               for _, y := range ind[i+1:] {
-                                       if y >= 0 && x > y {
-                                               check.errorf(e, "invalid slice indices: %d > %d", x, y)
-                                               break L // only report one error, ok to continue
-                                       }
-                               }
-                       }
-               }
-
        case *syntax.AssertExpr:
                check.expr(x, e.X)
                if x.mode == invalid {
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
new file mode 100644 (file)
index 0000000..0f4adab
--- /dev/null
@@ -0,0 +1,405 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of index/slice expressions.
+
+package types2
+
+import (
+       "cmd/compile/internal/syntax"
+       "go/constant"
+)
+
+func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) {
+       check.exprOrType(x, e.X)
+       if x.mode == invalid {
+               check.use(e.Index)
+               return
+       }
+
+       if x.mode == typexpr {
+               // type instantiation
+               x.mode = invalid
+               x.typ = check.varType(e)
+               if x.typ != Typ[Invalid] {
+                       x.mode = typexpr
+               }
+               return
+       }
+
+       if x.mode == value {
+               if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
+                       // function instantiation
+                       check.funcInst(x, e)
+                       return
+               }
+       }
+
+       // ordinary index expression
+       valid := false
+       length := int64(-1) // valid if >= 0
+       switch typ := optype(x.typ).(type) {
+       case *Basic:
+               if isString(typ) {
+                       valid = true
+                       if x.mode == constant_ {
+                               length = int64(len(constant.StringVal(x.val)))
+                       }
+                       // 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 = universeByte // use 'byte' name
+               }
+
+       case *Array:
+               valid = true
+               length = typ.len
+               if x.mode != variable {
+                       x.mode = value
+               }
+               x.typ = typ.elem
+
+       case *Pointer:
+               if typ := asArray(typ.base); typ != nil {
+                       valid = true
+                       length = typ.len
+                       x.mode = variable
+                       x.typ = typ.elem
+               }
+
+       case *Slice:
+               valid = true
+               x.mode = variable
+               x.typ = typ.elem
+
+       case *Map:
+               var key operand
+               check.expr(&key, e.Index)
+               check.assignment(&key, typ.key, "map index")
+               // ok to continue even if indexing failed - map element type is known
+               x.mode = mapindex
+               x.typ = typ.elem
+               x.expr = e
+               return
+
+       case *Sum:
+               // A sum type can be indexed if all of the sum's types
+               // support indexing and have the same index and element
+               // type. Special rules apply for maps in the sum type.
+               var tkey, telem Type // key is for map types only
+               nmaps := 0           // number of map types in sum type
+               if typ.is(func(t Type) bool {
+                       var e Type
+                       switch t := under(t).(type) {
+                       case *Basic:
+                               if isString(t) {
+                                       e = universeByte
+                               }
+                       case *Array:
+                               e = t.elem
+                       case *Pointer:
+                               if t := asArray(t.base); t != nil {
+                                       e = t.elem
+                               }
+                       case *Slice:
+                               e = t.elem
+                       case *Map:
+                               // If there are multiple maps in the sum type,
+                               // they must have identical key types.
+                               // TODO(gri) We may be able to relax this rule
+                               // but it becomes complicated very quickly.
+                               if tkey != nil && !Identical(t.key, tkey) {
+                                       return false
+                               }
+                               tkey = t.key
+                               e = t.elem
+                               nmaps++
+                       case *TypeParam:
+                               check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
+                       case *instance:
+                               panic("unimplemented")
+                       }
+                       if e == nil || telem != nil && !Identical(e, telem) {
+                               return false
+                       }
+                       telem = e
+                       return true
+               }) {
+                       // If there are maps, the index expression must be assignable
+                       // to the map key type (as for simple map index expressions).
+                       if nmaps > 0 {
+                               var key operand
+                               check.expr(&key, e.Index)
+                               check.assignment(&key, tkey, "map index")
+                               // ok to continue even if indexing failed - map element type is known
+
+                               // If there are only maps, we are done.
+                               if nmaps == len(typ.types) {
+                                       x.mode = mapindex
+                                       x.typ = telem
+                                       x.expr = e
+                                       return
+                               }
+
+                               // Otherwise we have mix of maps and other types. For
+                               // now we require that the map key be an integer type.
+                               // TODO(gri) This is probably not good enough.
+                               valid = isInteger(tkey)
+                               // avoid 2nd indexing error if indexing failed above
+                               if !valid && key.mode == invalid {
+                                       x.mode = invalid
+                                       return
+                               }
+                               x.mode = value // map index expressions are not addressable
+                       } else {
+                               // no maps
+                               valid = true
+                               x.mode = variable
+                       }
+                       x.typ = telem
+               }
+       }
+
+       if !valid {
+               check.errorf(x, invalidOp+"cannot index %s", x)
+               x.mode = invalid
+               return
+       }
+
+       if e.Index == nil {
+               check.errorf(e, invalidAST+"missing index for %s", x)
+               x.mode = invalid
+               return
+       }
+
+       index := e.Index
+       if l, _ := index.(*syntax.ListExpr); l != nil {
+               if n := len(l.ElemList); n <= 1 {
+                       check.errorf(e, invalidAST+"invalid use of ListExpr for index expression %v with %d indices", e, n)
+                       x.mode = invalid
+                       return
+               }
+               // len(l.ElemList) > 1
+               check.error(l.ElemList[1], invalidOp+"more than one index")
+               index = l.ElemList[0] // continue with first index
+       }
+
+       // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
+       // the element type may be accessed before it's set. Make sure we have
+       // a valid type.
+       if x.typ == nil {
+               x.typ = Typ[Invalid]
+       }
+
+       check.index(index, length)
+}
+
+func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
+       check.expr(x, e.X)
+       if x.mode == invalid {
+               check.use(e.Index[:]...)
+               return
+       }
+
+       valid := false
+       length := int64(-1) // valid if >= 0
+       switch typ := optype(x.typ).(type) {
+       case *Basic:
+               if isString(typ) {
+                       if e.Full {
+                               check.error(x, invalidOp+"3-index slice of string")
+                               x.mode = invalid
+                               return
+                       }
+                       valid = true
+                       if x.mode == constant_ {
+                               length = int64(len(constant.StringVal(x.val)))
+                       }
+                       // spec: "For untyped string operands the result
+                       // is a non-constant value of type string."
+                       if typ.kind == UntypedString {
+                               x.typ = Typ[String]
+                       }
+               }
+
+       case *Array:
+               valid = true
+               length = typ.len
+               if x.mode != variable {
+                       check.errorf(x, invalidOp+"%s (slice of unaddressable value)", x)
+                       x.mode = invalid
+                       return
+               }
+               x.typ = &Slice{elem: typ.elem}
+
+       case *Pointer:
+               if typ := asArray(typ.base); typ != nil {
+                       valid = true
+                       length = typ.len
+                       x.typ = &Slice{elem: typ.elem}
+               }
+
+       case *Slice:
+               valid = true
+               // x.typ doesn't change
+
+       case *Sum, *TypeParam:
+               check.error(x, "generic slice expressions not yet implemented")
+               x.mode = invalid
+               return
+       }
+
+       if !valid {
+               check.errorf(x, invalidOp+"cannot slice %s", x)
+               x.mode = invalid
+               return
+       }
+
+       x.mode = value
+
+       // spec: "Only the first index may be omitted; it defaults to 0."
+       if e.Full && (e.Index[1] == nil || e.Index[2] == nil) {
+               check.error(e, invalidAST+"2nd and 3rd index required in 3-index slice")
+               x.mode = invalid
+               return
+       }
+
+       // check indices
+       var ind [3]int64
+       for i, expr := range e.Index {
+               x := int64(-1)
+               switch {
+               case expr != nil:
+                       // The "capacity" is only known statically for strings, arrays,
+                       // and pointers to arrays, and it is the same as the length for
+                       // those types.
+                       max := int64(-1)
+                       if length >= 0 {
+                               max = length + 1
+                       }
+                       if _, v := check.index(expr, max); v >= 0 {
+                               x = v
+                       }
+               case i == 0:
+                       // default is 0 for the first index
+                       x = 0
+               case length >= 0:
+                       // default is length (== capacity) otherwise
+                       x = length
+               }
+               ind[i] = x
+       }
+
+       // constant indices must be in range
+       // (check.index already checks that existing indices >= 0)
+L:
+       for i, x := range ind[:len(ind)-1] {
+               if x > 0 {
+                       for _, y := range ind[i+1:] {
+                               if y >= 0 && x > y {
+                                       check.errorf(e, "invalid slice indices: %d > %d", x, y)
+                                       break L // only report one error, ok to continue
+                               }
+                       }
+               }
+       }
+}
+
+// index checks an index expression for validity.
+// If max >= 0, it is the upper bound for index.
+// If the result typ is != Typ[Invalid], index is valid and typ is its (possibly named) integer type.
+// If the result val >= 0, index is valid and val is its constant int value.
+func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64) {
+       typ = Typ[Invalid]
+       val = -1
+
+       var x operand
+       check.expr(&x, index)
+       if x.mode == invalid {
+               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
+       }
+
+       if x.mode != constant_ {
+               return x.typ, -1
+       }
+
+       // a constant index i must be in bounds
+       if constant.Sign(x.val) < 0 {
+               check.errorf(&x, invalidArg+"index %s must not be negative", &x)
+               return
+       }
+
+       v, valid := constant.Int64Val(constant.ToInt(x.val))
+       if !valid || max >= 0 && v >= max {
+               if check.conf.CompilerErrorMessages {
+                       check.errorf(&x, "array index %s out of bounds [0:%d]", x.val.String(), max)
+               } else {
+                       check.errorf(&x, "index %s is out of bounds", &x)
+               }
+               return
+       }
+
+       // 0 <= v [ && v < max ]
+       return Typ[Int], v
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// 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
+// literal (maximum index value + 1).
+func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 {
+       visited := make(map[int64]bool, len(elts))
+       var index, max int64
+       for _, e := range elts {
+               // determine and check index
+               validIndex := false
+               eval := e
+               if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
+                       if typ, i := check.index(kv.Key, length); typ != Typ[Invalid] {
+                               if i >= 0 {
+                                       index = i
+                                       validIndex = true
+                               } else {
+                                       check.errorf(e, "index %s must be integer constant", kv.Key)
+                               }
+                       }
+                       eval = kv.Value
+               } else if length >= 0 && index >= length {
+                       check.errorf(e, "index %d is out of bounds (>= %d)", index, length)
+               } else {
+                       validIndex = true
+               }
+
+               // if we have a valid index, check for duplicate entries
+               if validIndex {
+                       if visited[index] {
+                               check.errorf(e, "duplicate index %d in array or slice literal", index)
+                       }
+                       visited[index] = true
+               }
+               index++
+               if index > max {
+                       max = index
+               }
+
+               // check element against composite literal element type
+               var x operand
+               check.exprWithHint(&x, eval, typ)
+               check.assignment(&x, typ, "array or slice literal")
+       }
+       return max
+}