]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] go/types: clean up index expr implementation for type parameters
authorRob Findley <rfindley@google.com>
Fri, 16 Jul 2021 16:54:37 +0000 (12:54 -0400)
committerRobert Findley <rfindley@google.com>
Sat, 17 Jul 2021 01:38:18 +0000 (01:38 +0000)
This is a port of CL 332553 to go/types. The "expr" variable is renamed to
"e" in Checker.indexExpr to be consistent with types2.

Change-Id: I7905bebf2e8dab47256361362b16becf7596cf95
Reviewed-on: https://go-review.googlesource.com/c/go/+/335110
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>

src/go/types/index.go
src/go/types/testdata/check/typeparams.go2
src/go/types/testdata/fixedbugs/issue45635.go2
src/go/types/typeparam.go
src/go/types/typeset.go
src/go/types/typestring.go
src/go/types/union.go

index 769626dcc2c86563eb97c5be602d7ddb0ec7516d..036752c7340e428bc6fb21bf6493c9608c3fadc7 100644 (file)
@@ -15,18 +15,18 @@ import (
 // If e is a valid function instantiation, indexExpr returns true.
 // In that case x represents the uninstantiated function value and
 // it is the caller's responsibility to instantiate the function.
-func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncInst bool) {
-       check.exprOrType(x, expr.X)
+func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
+       check.exprOrType(x, e.X)
 
        switch x.mode {
        case invalid:
-               check.use(expr.Indices...)
+               check.use(e.Indices...)
                return false
 
        case typexpr:
                // type instantiation
                x.mode = invalid
-               x.typ = check.varType(expr.Orig)
+               x.typ = check.varType(e.Orig)
                if x.typ != Typ[Invalid] {
                        x.mode = typexpr
                }
@@ -41,7 +41,7 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
 
        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
@@ -77,10 +77,10 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
                x.typ = typ.elem
 
        case *Map:
-               index := check.singleIndex(expr)
+               index := check.singleIndex(e)
                if index == nil {
                        x.mode = invalid
-                       return
+                       return false
                }
                var key operand
                check.expr(&key, index)
@@ -88,88 +88,81 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
                // ok to continue even if indexing failed - map element type is known
                x.mode = mapindex
                x.typ = typ.elem
-               x.expr = expr.Orig
-               return
+               x.expr = e.Orig
+               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, 0, "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
+                               // 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 {
-                               index := check.singleIndex(expr)
+                       // 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 = expr.Orig
-                                       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
                }
        }
@@ -177,13 +170,13 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
        if !valid {
                check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
                x.mode = invalid
-               return
+               return false
        }
 
-       index := check.singleIndex(expr)
+       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)
index 5b4361d2791f62a5f6380da473a76aee324cf3d7..b832e6b760cf26524dfc34535a6d32e89292f5f4 100644 (file)
@@ -98,18 +98,23 @@ func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
 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
index c6784e12fdaff55d7aa7d54854c946cffb10f3d6..fc50797b17ca76929275a00ff343e0b25484bb72 100644 (file)
@@ -13,7 +13,7 @@ type N[T any] struct{}
 var _ N [] // ERROR expected type argument list
 
 type I interface {
-       ~map[int]int | ~[]int
+       ~[]int
 }
 
 func _[T I](i, j int) {
@@ -27,6 +27,5 @@ func _[T I](i, j int) {
        _ = s[i, j /* ERROR "more than one index" */ ]
 
        var t T
-       // TODO(rFindley) Fix the duplicate error below.
-       _ = t[i, j /* ERROR "more than one index" */ /* ERROR "more than one index" */ ]
+       _ = t[i, j /* ERROR "more than one index" */ ]
 }
index e1345088555b9bc3d5bd9e554b0548fe41b57731..92b048f2478c8d2ae3162bb79520d9498e32cf13 100644 (file)
@@ -70,3 +70,10 @@ func (t *TypeParam) Bound() *Interface {
 
 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)
+}
index e979e90e6f5b8a33332c6c67a018a4b52678edc6..3fe48892fe9c04b5f4b07b700ba714f9d4f4f902 100644 (file)
@@ -75,6 +75,21 @@ func (s *TypeSet) String() string {
 // ----------------------------------------------------------------------------
 // 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
 
index d234d86e61d59acca6dfa07ef9ed002009239f02..aef5e2013bddd9cd98fdac82295568f315a64611 100644 (file)
@@ -165,7 +165,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                }
                for i, e := range t.types {
                        if i > 0 {
-                               buf.WriteString("|")
+                               buf.WriteByte('|')
                        }
                        if t.tilde[i] {
                                buf.WriteByte('~')
index 690b734d76d1399a38dbb6f7bbbb55f636c463f3..7c69ec7b1086c1fde6d4eee5e92014d2110a5a0b 100644 (file)
@@ -60,7 +60,7 @@ func (u *Union) is(f func(Type, bool) bool) bool {
        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