]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: fix range over exprs of type parameter type
authorRobert Findley <rfindley@google.com>
Mon, 16 Aug 2021 01:11:18 +0000 (21:11 -0400)
committerRobert Findley <rfindley@google.com>
Mon, 16 Aug 2021 13:39:19 +0000 (13:39 +0000)
This is a port of CL 339897 to go/types. In addition, an error message
that was adjusted in CL 274974 is ported to go/types (CL 274974 was
only considered necessary for compiler compatibility).

Change-Id: Idfe44d759c925f9fed353a2d1898d3d4d8d85452
Reviewed-on: https://go-review.googlesource.com/c/go/+/342433
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/builtins.go
src/go/types/stmt.go
src/go/types/testdata/check/typeparams.go2

index 8a3fc14fea65c8dc6c33612034cff95da2ee5508..4ace1303a7c92856ac7419207e8a16f87593b041 100644 (file)
@@ -145,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                mode := invalid
                var typ Type
                var val constant.Value
-               switch typ = implicitArrayDeref(under(x.typ)); t := typ.(type) {
+               switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
                case *Basic:
                        if isString(t) && id == _Len {
                                if x.mode == constant_ {
@@ -181,7 +181,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 
                case *TypeParam:
                        if t.underIs(func(t Type) bool {
-                               switch t := implicitArrayDeref(t).(type) {
+                               switch t := arrayPtrDeref(t).(type) {
                                case *Basic:
                                        if isString(t) && id == _Len {
                                                return true
@@ -866,10 +866,10 @@ func makeSig(res Type, args ...Type) *Signature {
        return &Signature{params: params, results: result}
 }
 
-// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// arrayPtrDeref returns A if typ is of the form *A and A is an array;
 // otherwise it returns typ.
 //
-func implicitArrayDeref(typ Type) Type {
+func arrayPtrDeref(typ Type) Type {
        if p, ok := typ.(*Pointer); ok {
                if a := asArray(p.base); a != nil {
                        return a
index 29c189ec5d4f1184e49512d7323d59df0b986a8d..fd6eba2deb26f5bbaa55a41340f196a9ff55dd89 100644 (file)
@@ -783,9 +783,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
                // determine key/value types
                var key, val Type
                if x.mode != invalid {
+                       // Ranging over a type parameter is permitted if it has a structural type.
                        typ := optype(x.typ)
                        if _, ok := typ.(*Chan); ok && s.Value != nil {
-                               // TODO(gri) this also needs to happen for channels in generic variables
                                check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
                                // ok to continue
                        }
@@ -899,7 +899,7 @@ func isVarName(x ast.Expr) bool {
 // variables are used or present; this matters if we range over a generic
 // type where not all keys or values are of the same type.
 func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
-       switch typ := typ.(type) {
+       switch typ := arrayPtrDeref(typ).(type) {
        case *Basic:
                if isString(typ) {
                        return Typ[Int], universeRune, "" // use 'rune' name
@@ -908,45 +908,17 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
                return Typ[Int], typ.elem, ""
        case *Slice:
                return Typ[Int], typ.elem, ""
-       case *Pointer:
-               if typ := asArray(typ.base); typ != nil {
-                       return Typ[Int], typ.elem, ""
-               }
        case *Map:
                return typ.key, typ.elem, ""
        case *Chan:
                var msg string
                if typ.dir == SendOnly {
-                       // TODO(rfindley): this error message differs from types2. Reconcile this.
-                       msg = "send-only channel"
+                       msg = "receive from send-only channel"
                }
                return typ.elem, Typ[Invalid], msg
-       case *TypeParam:
-               first := true
-               var key, val Type
-               var msg string
-               typ.underIs(func(t Type) bool {
-                       k, v, m := rangeKeyVal(t, wantKey, wantVal)
-                       if k == nil || m != "" {
-                               key, val, msg = k, v, m
-                               return false
-                       }
-                       if first {
-                               key, val, msg = k, v, m
-                               first = false
-                               return true
-                       }
-                       if wantKey && !Identical(key, k) {
-                               key, val, msg = nil, nil, "all possible values must have the same key type"
-                               return false
-                       }
-                       if wantVal && !Identical(val, v) {
-                               key, val, msg = nil, nil, "all possible values must have the same element type"
-                               return false
-                       }
-                       return true
-               })
-               return key, val, msg
+       case *top:
+               // we have a type parameter with no structural type
+               return nil, nil, "no structural type"
        }
        return nil, nil, ""
 }
index 7ed0a5e847daac0f9e5faa889b48d277d8292bb6..5cd473007806c1a54eaddb44b86a64a8ddcf79d0 100644 (file)
@@ -149,40 +149,109 @@ func _[T interface{}](x T) {
         for range x /* ERROR cannot range */ {}
 }
 
-// Disabled for now until we have clarified semantics of range.
-// TODO(gri) fix this
-//
-// func _[T interface{ ~string | ~[]string }](x T) {
-//         for range x {}
-//         for i := range x { _ = i }
-//         for i, _ := range x { _ = i }
-//         for i, e := range x /* ERROR must have the same element type */ { _ = i }
-//         for _, e := range x /* ERROR must have the same element type */ {}
-//         var e rune
-//         _ = e
-//         for _, (e) = range x /* ERROR must have the same element type */ {}
-// }
-//
-//
-// func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) {
-//         for _, e := range x { _ = e }
-//         for i, e := range x { _ = i; _ = e }
-// }
-//
-// func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) {
-//         for _, e := range x { _ = e }
-//         for i, e := range x /* ERROR must have the same key type */ { _ = e }
-// }
-//
-// func _[T interface{ ~string | ~chan int }](x T) {
-//         for range x {}
-//         for i := range x { _ = i }
-//         for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
-// }
-//
-// func _[T interface{ ~string | ~chan<-int }](x T) {
-//         for i := range x /* ERROR send-only channel */ { _ = i }
-// }
+type myString string
+
+func _[
+        B1 interface{ string },
+        B2 interface{ string | myString },
+
+        C1 interface{ chan int },
+        C2 interface{ chan int | <-chan int },
+        C3 interface{ chan<- int },
+
+        S1 interface{ []int },
+        S2 interface{ []int | [10]int },
+
+        A1 interface{ [10]int },
+        A2 interface{ [10]int | []int },
+
+        P1 interface{ *[10]int },
+        P2 interface{ *[10]int | *[]int },
+
+        M1 interface{ map[string]int },
+        M2 interface{ map[string]int | map[string]string },
+]() {
+        var b0 string
+        for range b0 {}
+        for _ = range b0 {}
+        for _, _ = range b0 {}
+
+        var b1 B1
+        for range b1 {}
+        for _ = range b1 {}
+        for _, _ = range b1 {}
+
+        var b2 B2
+        for range b2 /* ERROR cannot range over b2 .* no structural type */ {}
+
+        var c0 chan int
+        for range c0 {}
+        for _ = range c0 {}
+        for _, _ /* ERROR permits only one iteration variable */ = range c0 {}
+
+        var c1 C1
+        for range c1 {}
+        for _ = range c1 {}
+        for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
+
+        var c2 C2
+        for range c2 /* ERROR cannot range over c2 .* no structural type */ {}
+
+        var c3 C3
+        for range c3 /* ERROR receive from send-only channel */ {}
+
+        var s0 []int
+        for range s0 {}
+        for _ = range s0 {}
+        for _, _ = range s0 {}
+
+        var s1 S1
+        for range s1 {}
+        for _ = range s1 {}
+        for _, _ = range s1 {}
+
+        var s2 S2
+        for range s2 /* ERROR cannot range over s2 .* no structural type */ {}
+
+        var a0 []int
+        for range a0 {}
+        for _ = range a0 {}
+        for _, _ = range a0 {}
+
+        var a1 A1
+        for range a1 {}
+        for _ = range a1 {}
+        for _, _ = range a1 {}
+
+        var a2 A2
+        for range a2 /* ERROR cannot range over a2 .* no structural type */ {}
+
+        var p0 *[10]int
+        for range p0 {}
+        for _ = range p0 {}
+        for _, _ = range p0 {}
+
+        var p1 P1
+        for range p1 {}
+        for _ = range p1 {}
+        for _, _ = range p1 {}
+
+        var p2 P2
+        for range p2 /* ERROR cannot range over p2 .* no structural type */ {}
+
+        var m0 map[string]int
+        for range m0 {}
+        for _ = range m0 {}
+        for _, _ = range m0 {}
+
+        var m1 M1
+        for range m1 {}
+        for _ = range m1 {}
+        for _, _ = range m1 {}
+
+        var m2 M2
+        for range m2 /* ERROR cannot range over m2 .* no structural type */ {}
+}
 
 // type inference checks