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>
This commit is contained in:
Robert Findley 2021-08-15 21:11:18 -04:00
parent 02f932e173
commit 7b7d7d7818
3 changed files with 113 additions and 72 deletions

View file

@ -145,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
mode := invalid mode := invalid
var typ Type var typ Type
var val constant.Value var val constant.Value
switch typ = implicitArrayDeref(under(x.typ)); t := typ.(type) { switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
case *Basic: case *Basic:
if isString(t) && id == _Len { if isString(t) && id == _Len {
if x.mode == constant_ { if x.mode == constant_ {
@ -181,7 +181,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case *TypeParam: case *TypeParam:
if t.underIs(func(t Type) bool { if t.underIs(func(t Type) bool {
switch t := implicitArrayDeref(t).(type) { switch t := arrayPtrDeref(t).(type) {
case *Basic: case *Basic:
if isString(t) && id == _Len { if isString(t) && id == _Len {
return true return true
@ -866,10 +866,10 @@ func makeSig(res Type, args ...Type) *Signature {
return &Signature{params: params, results: result} 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. // otherwise it returns typ.
// //
func implicitArrayDeref(typ Type) Type { func arrayPtrDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok { if p, ok := typ.(*Pointer); ok {
if a := asArray(p.base); a != nil { if a := asArray(p.base); a != nil {
return a return a

View file

@ -783,9 +783,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
// determine key/value types // determine key/value types
var key, val Type var key, val Type
if x.mode != invalid { if x.mode != invalid {
// Ranging over a type parameter is permitted if it has a structural type.
typ := optype(x.typ) typ := optype(x.typ)
if _, ok := typ.(*Chan); ok && s.Value != nil { 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) check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
// ok to continue // 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 // 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. // type where not all keys or values are of the same type.
func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) { func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
switch typ := typ.(type) { switch typ := arrayPtrDeref(typ).(type) {
case *Basic: case *Basic:
if isString(typ) { if isString(typ) {
return Typ[Int], universeRune, "" // use 'rune' name 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, "" return Typ[Int], typ.elem, ""
case *Slice: case *Slice:
return Typ[Int], typ.elem, "" return Typ[Int], typ.elem, ""
case *Pointer:
if typ := asArray(typ.base); typ != nil {
return Typ[Int], typ.elem, ""
}
case *Map: case *Map:
return typ.key, typ.elem, "" return typ.key, typ.elem, ""
case *Chan: case *Chan:
var msg string var msg string
if typ.dir == SendOnly { if typ.dir == SendOnly {
// TODO(rfindley): this error message differs from types2. Reconcile this. msg = "receive from send-only channel"
msg = "send-only channel"
} }
return typ.elem, Typ[Invalid], msg return typ.elem, Typ[Invalid], msg
case *TypeParam: case *top:
first := true // we have a type parameter with no structural type
var key, val Type return nil, nil, "no structural 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
} }
return nil, nil, "" return nil, nil, ""
} }

View file

@ -149,40 +149,109 @@ func _[T interface{}](x T) {
for range x /* ERROR cannot range */ {} for range x /* ERROR cannot range */ {}
} }
// Disabled for now until we have clarified semantics of range. type myString string
// TODO(gri) fix this
// func _[
// func _[T interface{ ~string | ~[]string }](x T) { B1 interface{ string },
// for range x {} B2 interface{ string | myString },
// for i := range x { _ = i }
// for i, _ := range x { _ = i } C1 interface{ chan int },
// for i, e := range x /* ERROR must have the same element type */ { _ = i } C2 interface{ chan int | <-chan int },
// for _, e := range x /* ERROR must have the same element type */ {} C3 interface{ chan<- int },
// var e rune
// _ = e S1 interface{ []int },
// for _, (e) = range x /* ERROR must have the same element type */ {} S2 interface{ []int | [10]int },
// }
// A1 interface{ [10]int },
// A2 interface{ [10]int | []int },
// func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) {
// for _, e := range x { _ = e } P1 interface{ *[10]int },
// for i, e := range x { _ = i; _ = e } P2 interface{ *[10]int | *[]int },
// }
// M1 interface{ map[string]int },
// func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) { M2 interface{ map[string]int | map[string]string },
// for _, e := range x { _ = e } ]() {
// for i, e := range x /* ERROR must have the same key type */ { _ = e } var b0 string
// } for range b0 {}
// for _ = range b0 {}
// func _[T interface{ ~string | ~chan int }](x T) { for _, _ = range b0 {}
// for range x {}
// for i := range x { _ = i } var b1 B1
// for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value for range b1 {}
// } for _ = range b1 {}
// for _, _ = range b1 {}
// func _[T interface{ ~string | ~chan<-int }](x T) {
// for i := range x /* ERROR send-only channel */ { _ = i } 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 // type inference checks