]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: fix range over exprs of type parameter...
authorRobert Griesemer <gri@golang.org>
Wed, 4 Aug 2021 23:52:25 +0000 (16:52 -0700)
committerRobert Griesemer <gri@golang.org>
Fri, 6 Aug 2021 20:34:45 +0000 (20:34 +0000)
For range expressions of type parameter type, the structural type
of the type parameter's constraint determines the range operation.

While at it, rename implicitArrayDeref to arrayPtrDeref.

Change-Id: Ib631a8a14e717498e5264944f659309df1f68cc2
Reviewed-on: https://go-review.googlesource.com/c/go/+/339897
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/builtins.go
src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/testdata/check/typeparams.go2

index c022e79c97267f7b38ece090de44cd42db658b46..e9df605fd12bee6b6b76fa57248ac69b6f78cf4c 100644 (file)
@@ -144,7 +144,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                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_ {
@@ -180,7 +180,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                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
@@ -862,10 +862,9 @@ 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 ad8efa91f86f05b38d102b23ab265c4f5ec94623..7865c2d4f45fe399ae96e77d0993f0585b5a026d 100644 (file)
@@ -789,9 +789,9 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
        // 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 && sValue != nil {
-                       // TODO(gri) this also needs to happen for channels in generic variables
                        check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
                        // ok to continue
                }
@@ -900,7 +900,7 @@ func isVarName(x syntax.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
@@ -909,10 +909,6 @@ 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:
@@ -921,32 +917,9 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
                        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 7392b88555dea211b1f58119329bd93f49ed48b9..ba8e837346423e5349aa0c00aaf2960fbe05bd00 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