aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-08-04 16:52:25 -0700
committerRobert Griesemer <gri@golang.org>2021-08-06 20:34:45 +0000
commit93285c89d1146e2698d2b8e5bf45279961f5026e (patch)
tree27f5b9e4273adac607c5a615778a57d78f884e68
parent5aac85ad5ebfa9c2ecb01a3292bcf3513d876d7a (diff)
downloadgo-93285c89d1146e2698d2b8e5bf45279961f5026e.tar.gz
go-93285c89d1146e2698d2b8e5bf45279961f5026e.zip
[dev.typeparams] cmd/compile/internal/types2: fix range over exprs of type parameter type
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>
-rw-r--r--src/cmd/compile/internal/types2/builtins.go9
-rw-r--r--src/cmd/compile/internal/types2/stmt.go37
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/typeparams.go2137
3 files changed, 112 insertions, 71 deletions
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index c022e79c97..e9df605fd1 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index ad8efa91f8..7865c2d4f4 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -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, ""
}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
index 7392b88555..ba8e837346 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
@@ -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