diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index e826f35105d..10741a90e2e 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -834,19 +834,28 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s // 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 && sValue != nil { - check.softErrorf(sValue, "range over %s permits only one iteration variable", &x) - // ok to continue - } - var msg string - key, val, msg = rangeKeyVal(typ, isVarName(sKey), isVarName(sValue)) - if key == nil || msg != "" { - if msg != "" { - msg = ": " + msg + // 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 sValue != nil { + check.softErrorf(sValue, "range over %s permits only one iteration variable", &x) + // ok to continue + } + if t.dir == SendOnly { + cause = "receive from send-only channel" + } + } + key, val = rangeKeyVal(u) + if key == nil || cause != "" { + if cause == "" { + check.softErrorf(&x, "cannot range over %s", &x) + } else { + check.softErrorf(&x, "cannot range over %s (%s)", &x, cause) } - check.softErrorf(&x, "cannot range over %s%s", &x, msg) // ok to continue } } @@ -927,44 +936,23 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s check.stmt(inner, s.Body) } -// isVarName reports whether x is a non-nil, non-blank (_) expression. -func isVarName(x syntax.Expr) bool { - if x == nil { - return false - } - ident, _ := unparen(x).(*syntax.Name) - return ident == nil || ident.Value != "_" -} - // 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 } diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 index 29c25b0bb48..d1f07a20d4f 100644 --- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 +++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 @@ -184,7 +184,7 @@ func _[ 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 {} @@ -197,7 +197,7 @@ func _[ 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 */ {} @@ -213,7 +213,7 @@ func _[ 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 {} @@ -226,7 +226,7 @@ func _[ 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 {} @@ -239,7 +239,7 @@ func _[ 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 {} @@ -252,7 +252,7 @@ func _[ 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