aboutsummaryrefslogtreecommitdiff
path: root/src/go
diff options
context:
space:
mode:
authorRobert Findley <rfindley@google.com>2021-08-15 21:11:18 -0400
committerRobert Findley <rfindley@google.com>2021-08-16 13:39:19 +0000
commit7b7d7d7818dfc1db22930be9333e8cc9f8c7f68c (patch)
treea4596936c43fab0c901ee28b0f69594760580379 /src/go
parent02f932e173c713333693993c509a663b9cb2bc8b (diff)
downloadgo-7b7d7d7818dfc1db22930be9333e8cc9f8c7f68c.tar.gz
go-7b7d7d7818dfc1db22930be9333e8cc9f8c7f68c.zip
go/types: fix range over exprs of type parameter type
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>
Diffstat (limited to 'src/go')
-rw-r--r--src/go/types/builtins.go8
-rw-r--r--src/go/types/stmt.go40
-rw-r--r--src/go/types/testdata/check/typeparams.go2137
3 files changed, 113 insertions, 72 deletions
diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go
index 8a3fc14fea..4ace1303a7 100644
--- a/src/go/types/builtins.go
+++ b/src/go/types/builtins.go
@@ -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
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index 29c189ec5d..fd6eba2deb 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -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, ""
}
diff --git a/src/go/types/testdata/check/typeparams.go2 b/src/go/types/testdata/check/typeparams.go2
index 7ed0a5e847..5cd4730078 100644
--- a/src/go/types/testdata/check/typeparams.go2
+++ b/src/go/types/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