mirror of
https://github.com/golang/go
synced 2024-11-17 20:04:47 -07:00
go/types: range clause to accept type sets with single underlying types
This is a port of CL 357778 to go/types, adjusted to include error codes and to use the different range statement syntax in go/ast. Change-Id: Id537c195cd33a8b422a366269ca8730c2a5bccf1 Reviewed-on: https://go-review.googlesource.com/c/go/+/359875 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
3571ab58b8
commit
02bd226b8a
@ -832,20 +832,28 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||
// 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
|
||||
}
|
||||
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
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -930,44 +938,23 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
12
src/go/types/testdata/check/typeparams.go2
vendored
12
src/go/types/testdata/check/typeparams.go2
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user