]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: fix indexing of generic types
authorRobert Findley <rfindley@google.com>
Tue, 2 Nov 2021 22:36:36 +0000 (18:36 -0400)
committerRobert Findley <rfindley@google.com>
Wed, 3 Nov 2021 00:07:10 +0000 (00:07 +0000)
This is a clean port of CL 360603 to go/types.

Change-Id: Iadb312f07e509ff83339d5525765b7b7987bf233
Reviewed-on: https://go-review.googlesource.com/c/go/+/360936
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/index.go
src/go/types/testdata/check/typeparams.go2

index 807cf58980d9473d56336fe8f7544aba895cf73e..a85d314efa641011d9a448407cd13b98a4c10532 100644 (file)
@@ -101,77 +101,80 @@ func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst
 
        case *TypeParam:
                // TODO(gri) report detailed failure cause for better error messages
-               var tkey, telem Type // tkey != nil if we have maps
+               var key, elem Type // key != nil: we must have all maps
+               mode := variable   // non-maps result mode
+               // TODO(gri) factor out closure and use it for non-typeparam cases as well
                if typ.underIs(func(u Type) bool {
-                       var key, elem Type
-                       alen := int64(-1) // valid if >= 0
+                       l := int64(-1) // valid if >= 0
+                       var k, e Type  // k is only set for maps
                        switch t := u.(type) {
                        case *Basic:
-                               if !isString(t) {
-                                       return false
+                               if isString(t) {
+                                       e = universeByte
+                                       mode = value
                                }
-                               elem = universeByte
                        case *Array:
-                               elem = t.elem
-                               alen = t.len
+                               l = t.len
+                               e = t.elem
+                               if x.mode != variable {
+                                       mode = value
+                               }
                        case *Pointer:
-                               a, _ := under(t.base).(*Array)
-                               if a == nil {
-                                       return false
+                               if t := asArray(t.base); t != nil {
+                                       l = t.len
+                                       e = t.elem
                                }
-                               elem = a.elem
-                               alen = a.len
                        case *Slice:
-                               elem = t.elem
+                               e = t.elem
                        case *Map:
-                               key = t.key
-                               elem = t.elem
-                       default:
+                               k = t.key
+                               e = t.elem
+                       }
+                       if e == nil {
                                return false
                        }
-                       assert(elem != nil)
-                       if telem == nil {
+                       if elem == 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
-                               }
-                               // 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
-                               }
+                               length = l
+                               key, elem = k, e
+                               return true
+                       }
+                       // all map keys must be identical (incl. all nil)
+                       // (that is, we cannot mix maps with other types)
+                       if !Identical(key, k) {
+                               return false
+                       }
+                       // all element types must be identical
+                       if !Identical(elem, e) {
+                               return false
+                       }
+                       // track the minimal length for arrays, if any
+                       if l >= 0 && l < length {
+                               length = l
                        }
                        return true
                }) {
                        // For maps, the index expression must be assignable to the map key type.
-                       if tkey != nil {
+                       if key != nil {
                                index := check.singleIndex(e)
                                if index == nil {
                                        x.mode = invalid
                                        return false
                                }
-                               var key operand
-                               check.expr(&key, index)
-                               check.assignment(&key, tkey, "map index")
+                               var k operand
+                               check.expr(&k, index)
+                               check.assignment(&kkey, "map index")
                                // ok to continue even if indexing failed - map element type is known
                                x.mode = mapindex
-                               x.typ = telem
+                               x.typ = elem
                                x.expr = e
                                return false
                        }
 
                        // no maps
                        valid = true
-                       x.mode = variable
-                       x.typ = telem
+                       x.mode = mode
+                       x.typ = elem
                }
        }
 
index 446667cd668ff210f8c4f9cc435c16d9b174e744..09d478c4d75a046e65a2cc8db62b306c70e4848f 100644 (file)
@@ -114,6 +114,14 @@ func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERR
 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 */ ] }
 
+// indexing with strings and non-variable arrays (assignment not permitted)
+func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 }
+func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 }
+func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 }
+func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 }
+
 // slicing
 
 func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] }