1
0
mirror of https://github.com/golang/go synced 2024-11-23 00:10:07 -07:00

[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>
This commit is contained in:
Robert Griesemer 2021-08-04 16:52:25 -07:00
parent 5aac85ad5e
commit 93285c89d1
3 changed files with 112 additions and 71 deletions

View File

@ -144,7 +144,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
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_ {
@ -180,7 +180,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
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
@ -862,10 +862,9 @@ 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 arrayPtrDeref(typ Type) Type {
func implicitArrayDeref(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

@ -789,9 +789,9 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
// 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 && sValue != nil { 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) check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
// ok to continue // 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 // 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
@ -909,10 +909,6 @@ 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:
@ -921,32 +917,9 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
msg = "receive from send-only channel" msg = "receive from 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