// RangeKeyVal returns the key and value types for a range over typ.
// It panics if range over typ is invalid.
func RangeKeyVal(typ Type) (Type, Type) {
- key, val, _, ok := rangeKeyVal(typ, nil)
+ key, val, _, ok := rangeKeyVal(nil, typ, nil)
assert(ok)
return key, val
}
// determine key/value types
var key, val Type
if x.mode != invalid {
- // Ranging over a type parameter is permitted if it has a core type.
- k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+ k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
return check.allowVersion(v)
})
switch {
}
// rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ.
+// over an expression of type orig.
// If allowVersion != nil, it is used to check the required language version.
// If the range clause is not permitted, rangeKeyVal returns ok = false.
// When ok = false, rangeKeyVal may also return a reason in cause.
-func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
+// The check parameter is only used in case of an error; it may be nil.
+func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
bad := func(cause string) (Type, Type, string, bool) {
return Typ[Invalid], Typ[Invalid], cause, false
}
- orig := typ
- switch typ := arrayPtrDeref(coreType(typ)).(type) {
- case nil:
- return bad("no core type")
+ var cause1 string
+ rtyp := sharedUnderOrChan(check, orig, &cause1)
+ if rtyp == nil {
+ return bad(cause1)
+ }
+
+ switch typ := arrayPtrDeref(rtyp).(type) {
case *Basic:
if isString(typ) {
return Typ[Int], universeRune, "", true // use 'rune' name
case *Map:
return typ.key, typ.elem, "", true
case *Chan:
- if typ.dir == SendOnly {
- return bad("receive from send-only channel")
- }
+ assert(typ.dir != SendOnly)
return typ.elem, nil, "", true
case *Signature:
if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
yield(t, under(t))
}
+// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
+// if that type is a channel type it must permit receive operations.
+// If t is a type parameter, sharedUnderOrChan returns the single underlying
+// type of all types in its type set if it exists, or, if the type set contains
+// only channel types permitting receive operations and with identical element
+// types, sharedUnderOrChan returns one of those channel types.
+// Otherwise the result is nil, and *cause reports the error if a non-nil cause
+// is provided.
+// The check parameter is only used if *cause reports an error; it may be nil.
+func sharedUnderOrChan(check *Checker, t Type, cause *string) Type {
+ var s, su Type
+ var sc *Chan
+
+ bad := func(s string) bool {
+ if cause != nil {
+ *cause = s
+ }
+ su = nil
+ return false
+ }
+
+ typeset(t, func(t, u Type) bool {
+ if u == nil {
+ return bad("no specific type")
+ }
+ c, _ := u.(*Chan)
+ if c != nil && c.dir == SendOnly {
+ return bad(check.sprintf("receive from send-only channel %s", t))
+ }
+ if su == nil {
+ s, su = t, u
+ sc = c // possibly nil
+ return true
+ }
+ // su != nil
+ if sc != nil && c != nil {
+ if !Identical(sc.elem, c.elem) {
+ return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
+ }
+ return true
+ }
+ // sc == nil
+ if !Identical(su, u) {
+ return bad(check.sprintf("%s and %s have different underlying types", s, t))
+ }
+ return true
+ })
+
+ return su
+}
+
// If t is not a type parameter, coreType returns the underlying type.
// If t is a type parameter, coreType returns the single underlying
// type of all types in its type set if it exists, or nil otherwise. If the
// determine key/value types
var key, val Type
if x.mode != invalid {
- // Ranging over a type parameter is permitted if it has a core type.
- k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+ k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
return check.allowVersion(v)
})
switch {
}
// rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ.
+// over an expression of type orig.
// If allowVersion != nil, it is used to check the required language version.
// If the range clause is not permitted, rangeKeyVal returns ok = false.
// When ok = false, rangeKeyVal may also return a reason in cause.
-func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
+// The check parameter is only used in case of an error; it may be nil.
+func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
bad := func(cause string) (Type, Type, string, bool) {
return Typ[Invalid], Typ[Invalid], cause, false
}
- orig := typ
- switch typ := arrayPtrDeref(coreType(typ)).(type) {
- case nil:
- return bad("no core type")
+ var cause1 string
+ rtyp := sharedUnderOrChan(check, orig, &cause1)
+ if rtyp == nil {
+ return bad(cause1)
+ }
+
+ switch typ := arrayPtrDeref(rtyp).(type) {
case *Basic:
if isString(typ) {
return Typ[Int], universeRune, "", true // use 'rune' name
case *Map:
return typ.key, typ.elem, "", true
case *Chan:
- if typ.dir == SendOnly {
- return bad("receive from send-only channel")
- }
+ assert(typ.dir != SendOnly)
return typ.elem, nil, "", true
case *Signature:
if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
yield(t, under(t))
}
+// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
+// if that type is a channel type it must permit receive operations.
+// If t is a type parameter, sharedUnderOrChan returns the single underlying
+// type of all types in its type set if it exists, or, if the type set contains
+// only channel types permitting receive operations and with identical element
+// types, sharedUnderOrChan returns one of those channel types.
+// Otherwise the result is nil, and *cause reports the error if a non-nil cause
+// is provided.
+// The check parameter is only used if *cause reports an error; it may be nil.
+func sharedUnderOrChan(check *Checker, t Type, cause *string) Type {
+ var s, su Type
+ var sc *Chan
+
+ bad := func(s string) bool {
+ if cause != nil {
+ *cause = s
+ }
+ su = nil
+ return false
+ }
+
+ typeset(t, func(t, u Type) bool {
+ if u == nil {
+ return bad("no specific type")
+ }
+ c, _ := u.(*Chan)
+ if c != nil && c.dir == SendOnly {
+ return bad(check.sprintf("receive from send-only channel %s", t))
+ }
+ if su == nil {
+ s, su = t, u
+ sc = c // possibly nil
+ return true
+ }
+ // su != nil
+ if sc != nil && c != nil {
+ if !Identical(sc.elem, c.elem) {
+ return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
+ }
+ return true
+ }
+ // sc == nil
+ if !Identical(su, u) {
+ return bad(check.sprintf("%s and %s have different underlying types", s, t))
+ }
+ return true
+ })
+
+ return su
+}
+
// If t is not a type parameter, coreType returns the underlying type.
// If t is a type parameter, coreType returns the single underlying
// type of all types in its type set if it exists, or nil otherwise. If the
for _, _ = range s1 {}
var s2 S2
- for range s2 /* ERRORx `cannot range over s2.*no core type` */ {}
+ for range s2 /* ERRORx `cannot range over s2.*\[\]int and \[10\]int have different underlying types` */ {}
var a0 []int
for range a0 {}
for _, _ = range a1 {}
var a2 A2
- for range a2 /* ERRORx `cannot range over a2.*no core type` */ {}
+ for range a2 /* ERRORx `cannot range over a2.*\[10\]int and \[\]int have different underlying types` */ {}
var p0 *[10]int
for range p0 {}
for _, _ = range p1 {}
var p2 P2
- for range p2 /* ERRORx `cannot range over p2.*no core type` */ {}
+ for range p2 /* ERRORx `cannot range over p2.*\*\[10\]int and \*\[\]int have different underlying types` */ {}
var m0 map[string]int
for range m0 {}
for _, _ = range m1 {}
var m2 M2
- for range m2 /* ERRORx `cannot range over m2.*no core type` */ {}
+ for range m2 /* ERRORx `cannot range over m2.*map\[string\]int and map\[string\]string` */ {}
}
// type inference checks
}
}
+func _[T any](x T) {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by any): no specific type" */ {
+ }
+}
+
+func _[T interface{int; string}](x T) {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by interface{int; string} with empty type set): no specific type" */ {
+ }
+}
+
func _[T int | string](x T) {
- for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): no core type" */ {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): int and string have different underlying types" */ {
}
}
func _[T int | int64](x T) {
- for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): no core type" */ {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): int and int64 have different underlying types" */ {
}
}
}
func _[T int | string](x T) {
- for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): no core type" */ {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by int | string): int and string have different underlying types" */ {
}
}
func _[T int | int64](x T) {
- for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): no core type" */ {
+ for range x /* ERROR "cannot range over x (variable of type T constrained by int | int64): int and int64 have different underlying types" */ {
}
}