// ordinary index expression
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ).(type) {
+ switch typ := under(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
var key operand
check.expr(&key, index)
x.mode = mapindex
x.typ = typ.elem
x.expr = e
- return
+ return false
- case *Union:
- // A union type can be indexed if all of the union's terms
- // support indexing and have the same index and element
- // type. Special rules apply for maps in the union type.
- var tkey, telem Type // key is for map types only
- nmaps := 0 // number of map types in union type
- if typ.underIs(func(t Type) bool {
- var e Type
- switch t := t.(type) {
+ case *TypeParam:
+ // TODO(gri) report detailed failure cause for better error messages
+ var tkey, telem Type // tkey != nil if we have maps
+ if typ.underIs(func(u Type) bool {
+ var key, elem Type
+ alen := int64(-1) // valid if >= 0
+ switch t := u.(type) {
case *Basic:
- if isString(t) {
- e = universeByte
+ if !isString(t) {
+ return false
}
+ elem = universeByte
case *Array:
- e = t.elem
+ elem = t.elem
+ alen = t.len
case *Pointer:
- if t := asArray(t.base); t != nil {
- e = t.elem
+ a, _ := under(t.base).(*Array)
+ if a == nil {
+ return false
}
+ elem = a.elem
+ alen = a.len
case *Slice:
- e = t.elem
+ elem = t.elem
case *Map:
- // If there are multiple maps in the union 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) {
+ key = t.key
+ elem = t.elem
+ default:
+ return false
+ }
+ assert(elem != nil)
+ if telem == nil {
+ // first type
+ tkey, telem = key, elem
+ length = alen
+ } else {
+ // all map keys must be identical (incl. all nil)
+ if !Identical(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:
- unimplemented()
- }
- if e == nil || telem != nil && !Identical(e, telem) {
- return false
+ // all element types must be identical
+ if !Identical(elem, telem) {
+ return false
+ }
+ tkey, telem = key, elem
+ // track the minimal length for arrays
+ if alen >= 0 && alen < length {
+ length = alen
+ }
}
- 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 {
+ // For maps, the index expression must be assignable to the map key type.
+ if tkey != nil {
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
var key operand
check.expr(&key, 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 == typ.NumTerms() {
- 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.mode = mapindex
+ x.typ = telem
+ x.expr = e
+ return false
}
+
+ // no maps
+ valid = true
+ x.mode = variable
x.typ = telem
}
}
if !valid {
check.errorf(x, invalidOp+"cannot index %s", x)
x.mode = invalid
- return
+ return false
}
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
func _[T interface{ ~string }] (x T, i int) { _ = x[i] }
func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] }
-func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] }
func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-// indexing with various combinations of map types in type lists (see issue #42616)
-func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x[i] }
+// indexing with various combinations of map types in type sets (see issue #42616)
+func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] }
func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted
-func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
-func _[T interface{ ~[]E | ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
-func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x[i /* ERROR cannot use i */ ] }
+func _[T interface{ ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
+func _[T interface{ ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
+func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
+
+// indexing with various combinations of array and other types in type sets
+func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
// slicing
// TODO(gri) implement this
var _ N[] /* ERROR expecting type */
type I interface {
- ~map[int]int | ~[]int
+ ~[]int
}
func _[T I](i, j int) {
_ = s[i, j /* ERROR more than one index */ ]
var t T
- // TODO(gri) fix multiple error below
- _ = t[i, j /* ERROR more than one index */ /* ERROR more than one index */ ]
+ _ = t[i, j /* ERROR more than one index */ ]
}
func (t *TypeParam) Underlying() Type { return t }
func (t *TypeParam) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (t *TypeParam) underIs(f func(Type) bool) bool {
+ return t.Bound().typeSet().underIs(f)
+}
// ----------------------------------------------------------------------------
// Implementation
+// underIs reports whether f returned true for the underlying types of the
+// enumerable types in the type set s. If the type set comprises all types
+// f is called once with the top type; if the type set is empty, the result
+// is false.
+func (s *TypeSet) underIs(f func(Type) bool) bool {
+ switch t := s.types.(type) {
+ case nil:
+ return f(theTop)
+ default:
+ return f(t)
+ case *Union:
+ return t.underIs(f)
+ }
+}
+
// topTypeSet may be used as type set for the empty interface.
var topTypeSet TypeSet
}
for i, e := range t.types {
if i > 0 {
- buf.WriteString("|")
+ buf.WriteByte('|')
}
if t.tilde[i] {
buf.WriteByte('~')
return true
}
-// is reports whether f returned true for the underlying types of all terms of u.
+// underIs reports whether f returned true for the underlying types of all terms of u.
func (u *Union) underIs(f func(Type) bool) bool {
if u.IsEmpty() {
return false