]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: range clause to accept type sets with single underlying types
authorRobert Findley <rfindley@google.com>
Fri, 29 Oct 2021 22:01:09 +0000 (18:01 -0400)
committerRobert Findley <rfindley@google.com>
Fri, 29 Oct 2021 22:27:40 +0000 (22:27 +0000)
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>
src/go/types/stmt.go
src/go/types/testdata/check/typeparams.go2

index 92542597c51beabaf9072b8bc2ecc3162e683f00..7197310bd9c35e7c73ce6e4d3003c3f787483cd2 100644 (file)
@@ -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
+                       // 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
                        }
                }
@@ -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
 }
index 10f84314fdd104cfc4dfc1aed2ad620fa63c546d..c4b0c212d28cc8c4115fcae2e8e0c4c83b819d97 100644 (file)
@@ -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