// 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 {
- check.softErrorf(atPos(s.Value.Pos()), _InvalidIterVar, "range over %s permits only one iteration variable", &x)
- // ok to continue
+ // Ranging over a type parameter is permitted if it has a single underlying type.
+ var cause string
+ u := singleUnder(x.typ)
+ switch t := u.(type) {
+ case nil:
+ cause = "type set has no single underlying type"
+ case *Chan:
+ if s.Value != nil {
+ check.softErrorf(s.Value, _InvalidIterVar, "range over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ if t.dir == SendOnly {
+ cause = "receive from send-only channel"
+ }
}
- var msg string
- key, val, msg = rangeKeyVal(typ, isVarName(s.Key), isVarName(s.Value))
- if key == nil || msg != "" {
- if msg != "" {
- // TODO(rFindley) should this be parenthesized, to be consistent with other qualifiers?
- msg = ": " + msg
+ key, val = rangeKeyVal(u)
+ if key == nil || cause != "" {
+ if cause == "" {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s", &x)
+ } else {
+ check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s (%s)", &x, cause)
}
- check.softErrorf(&x, _InvalidRangeExpr, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
}
}
-// isVarName reports whether x is a non-nil, non-blank (_) expression.
-func isVarName(x ast.Expr) bool {
- if x == nil {
- return false
- }
- ident, _ := unparen(x).(*ast.Ident)
- return ident == nil || ident.Name != "_"
-}
-
// rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ, and possibly an error message. If the
-// range clause is not permitted the returned key is nil or msg is not
-// empty (in that case we still may have a non-nil key type which can be
-// used to reduce the chance for follow-on errors).
-// The wantKey, wantVal, and hasVal flags indicate which of the iteration
-// 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) {
+// over an expression of type typ. If the range clause is not permitted
+// the results are nil.
+func rangeKeyVal(typ Type) (key, val Type) {
switch typ := arrayPtrDeref(typ).(type) {
case *Basic:
if isString(typ) {
- return Typ[Int], universeRune, "" // use 'rune' name
+ return Typ[Int], universeRune // use 'rune' name
}
case *Array:
- return Typ[Int], typ.elem, ""
+ return Typ[Int], typ.elem
case *Slice:
- return Typ[Int], typ.elem, ""
+ return Typ[Int], typ.elem
case *Map:
- return typ.key, typ.elem, ""
+ return typ.key, typ.elem
case *Chan:
- var msg string
- if typ.dir == SendOnly {
- msg = "receive from send-only channel"
- }
- return typ.elem, Typ[Invalid], msg
- case *top:
- // we have a type parameter with no structural type
- return nil, nil, "no structural type"
+ return typ.elem, Typ[Invalid]
}
- return nil, nil, ""
+ return
}
for _, _ = range b1 {}
var b2 B2
- for range b2 /* ERROR cannot range over b2 .* no structural type */ {}
+ for range b2 {}
var c0 chan int
for range c0 {}
for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
var c2 C2
- for range c2 /* ERROR cannot range over c2 .* no structural type */ {}
+ for range c2 /* ERROR cannot range over c2.*no single underlying type */ {}
var c3 C3
for range c3 /* ERROR receive from send-only channel */ {}
for _, _ = range s1 {}
var s2 S2
- for range s2 /* ERROR cannot range over s2 .* no structural type */ {}
+ for range s2 /* ERROR cannot range over s2.*no single underlying type */ {}
var a0 []int
for range a0 {}
for _, _ = range a1 {}
var a2 A2
- for range a2 /* ERROR cannot range over a2 .* no structural type */ {}
+ for range a2 /* ERROR cannot range over a2.*no single underlying type */ {}
var p0 *[10]int
for range p0 {}
for _, _ = range p1 {}
var p2 P2
- for range p2 /* ERROR cannot range over p2 .* no structural type */ {}
+ for range p2 /* ERROR cannot range over p2.*no single underlying type */ {}
var m0 map[string]int
for range m0 {}
for _, _ = range m1 {}
var m2 M2
- for range m2 /* ERROR cannot range over m2 .* no structural type */ {}
+ for range m2 /* ERROR cannot range over m2.*no single underlying type */ {}
}
// type inference checks