]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: allow range-over-func to omit iteration variables
authorRobert Griesemer <gri@golang.org>
Wed, 12 Jun 2024 20:19:25 +0000 (13:19 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 13 Jun 2024 15:47:50 +0000 (15:47 +0000)
For #65236.

Change-Id: I63e57c1d8e9765979e9e58b45948008964b32384
Reviewed-on: https://go-review.googlesource.com/c/go/+/592176
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/stmt.go
src/go/types/stmt.go
src/internal/types/testdata/spec/range.go

index 656f0e2eb2bc2868a6cca562a9839e3c0bf1b2fc..f9e17aa6168eff6331bbc0cf5da5a04ecb3d030d 100644 (file)
@@ -857,7 +857,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
        var key, val Type
        if x.mode != invalid {
                // Ranging over a type parameter is permitted if it has a core type.
-               k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+               k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
                        return check.allowVersion(x.expr, v)
                })
                switch {
@@ -871,17 +871,6 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
                        check.softErrorf(sValue, InvalidIterVar, "range over %s permits only one iteration variable", &x)
                case sExtra != nil:
                        check.softErrorf(sExtra, InvalidIterVar, "range clause permits at most two iteration variables")
-               case isFunc && ((k == nil) != (sKey == nil) || (v == nil) != (sValue == nil)):
-                       var count string
-                       switch {
-                       case k == nil:
-                               count = "no iteration variables"
-                       case v == nil:
-                               count = "one iteration variable"
-                       default:
-                               count = "two iteration variables"
-                       }
-                       check.softErrorf(&x, InvalidIterVar, "range over %s must have %s", &x, count)
                }
                key, val = k, v
        }
@@ -1001,7 +990,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
 // RangeKeyVal returns the key and value types for a range over typ.
 // Exported for use by the compiler (does not exist in go/types).
 func RangeKeyVal(typ Type) (Type, Type) {
-       key, val, _, _, _ := rangeKeyVal(typ, nil)
+       key, val, _, _ := rangeKeyVal(typ, nil)
        return key, val
 }
 
@@ -1010,9 +999,9 @@ func RangeKeyVal(typ Type) (Type, Type) {
 // 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, isFunc, ok bool) {
-       bad := func(cause string) (Type, Type, string, bool, bool) {
-               return Typ[Invalid], Typ[Invalid], cause, false, false
+func rangeKeyVal(typ 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
        }
        toSig := func(t Type) *Signature {
                sig, _ := coreType(t).(*Signature)
@@ -1025,25 +1014,25 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
                return bad("no core type")
        case *Basic:
                if isString(typ) {
-                       return Typ[Int], universeRune, "", false, true // use 'rune' name
+                       return Typ[Int], universeRune, "", true // use 'rune' name
                }
                if isInteger(typ) {
                        if allowVersion != nil && !allowVersion(go1_22) {
                                return bad("requires go1.22 or later")
                        }
-                       return orig, nil, "", false, true
+                       return orig, nil, "", true
                }
        case *Array:
-               return Typ[Int], typ.elem, "", false, true
+               return Typ[Int], typ.elem, "", true
        case *Slice:
-               return Typ[Int], typ.elem, "", false, true
+               return Typ[Int], typ.elem, "", true
        case *Map:
-               return typ.key, typ.elem, "", false, true
+               return typ.key, typ.elem, "", true
        case *Chan:
                if typ.dir == SendOnly {
                        return bad("receive from send-only channel")
                }
-               return typ.elem, nil, "", false, true
+               return typ.elem, nil, "", true
        case *Signature:
                if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
                        return bad("requires go1.23 or later")
@@ -1071,7 +1060,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
                if cb.Params().Len() >= 2 {
                        val = cb.Params().At(1).Type()
                }
-               return key, val, "", true, true
+               return key, val, "", true
        }
        return
 }
index f9a733fc3a3cb15bfaadd4bc3254126c75eb9640..f5cceb8e5f9244c6b50610552e9b8578087c3b2b 100644 (file)
@@ -857,7 +857,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
        var key, val Type
        if x.mode != invalid {
                // Ranging over a type parameter is permitted if it has a core type.
-               k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+               k, v, cause, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
                        return check.allowVersion(x.expr, v)
                })
                switch {
@@ -871,17 +871,6 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
                        check.softErrorf(sValue, InvalidIterVar, "range over %s permits only one iteration variable", &x)
                case sExtra != nil:
                        check.softErrorf(sExtra, InvalidIterVar, "range clause permits at most two iteration variables")
-               case isFunc && ((k == nil) != (sKey == nil) || (v == nil) != (sValue == nil)):
-                       var count string
-                       switch {
-                       case k == nil:
-                               count = "no iteration variables"
-                       case v == nil:
-                               count = "one iteration variable"
-                       default:
-                               count = "two iteration variables"
-                       }
-                       check.softErrorf(&x, InvalidIterVar, "range over %s must have %s", &x, count)
                }
                key, val = k, v
        }
@@ -1003,9 +992,9 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
 // 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, isFunc, ok bool) {
-       bad := func(cause string) (Type, Type, string, bool, bool) {
-               return Typ[Invalid], Typ[Invalid], cause, false, false
+func rangeKeyVal(typ 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
        }
        toSig := func(t Type) *Signature {
                sig, _ := coreType(t).(*Signature)
@@ -1018,26 +1007,25 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
                return bad("no core type")
        case *Basic:
                if isString(typ) {
-                       return Typ[Int], universeRune, "", false, true // use 'rune' name
+                       return Typ[Int], universeRune, "", true // use 'rune' name
                }
                if isInteger(typ) {
-                       // untyped numeric constants may be representable as integer values
                        if allowVersion != nil && !allowVersion(go1_22) {
                                return bad("requires go1.22 or later")
                        }
-                       return orig, nil, "", false, true
+                       return orig, nil, "", true
                }
        case *Array:
-               return Typ[Int], typ.elem, "", false, true
+               return Typ[Int], typ.elem, "", true
        case *Slice:
-               return Typ[Int], typ.elem, "", false, true
+               return Typ[Int], typ.elem, "", true
        case *Map:
-               return typ.key, typ.elem, "", false, true
+               return typ.key, typ.elem, "", true
        case *Chan:
                if typ.dir == SendOnly {
                        return bad("receive from send-only channel")
                }
-               return typ.elem, nil, "", false, true
+               return typ.elem, nil, "", true
        case *Signature:
                if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
                        return bad("requires go1.23 or later")
@@ -1065,7 +1053,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
                if cb.Params().Len() >= 2 {
                        val = cb.Params().At(1).Type()
                }
-               return key, val, "", true, true
+               return key, val, "", true
        }
        return
 }
index 07bd6b6769b6f7eb4441eda3b137ec208b6820c9..9e32256fb748887af2f37a273b6e7f55d938f034 100644 (file)
@@ -24,7 +24,7 @@ func f7(func(int) MyBool)             {}
 func f8(func(MyInt, MyString) MyBool) {}
 
 func test() {
-       // TODO: Would be nice to 'for range T.M' and 'for range (*T).PM' directly,
+       // TODO: Would be nice to test 'for range T.M' and 'for range (*T).PM' directly,
        // but there is no gofmt-friendly way to write the error pattern in the right place.
        m1 := T.M
        for range m1 /* ERROR "cannot range over m1 (variable of type func(T)): func must be func(yield func(...) bool): argument is not func" */ {
@@ -36,7 +36,7 @@ func test() {
        }
        for range f2 /* ERROR "cannot range over f2 (value of type func(func())): func must be func(yield func(...) bool): yield func does not return bool" */ {
        }
-       for range f4 /* ERROR "range over f4 (value of type func(func(int) bool)) must have one iteration variable" */ {
+       for range f4 {
        }
        for _ = range f4 {
        }
@@ -153,3 +153,33 @@ func _[T ~func(func(int) bool)](x T) {
        for _ = range x { // ok
        }
 }
+
+// go.dev/issue/65236
+
+func seq0(func() bool) {}
+func seq1(func(int) bool) {}
+func seq2(func(int, int) bool) {}
+
+func _() {
+       for range seq0 {
+       }
+       for _ /* ERROR "range over seq0 (value of type func(func() bool)) permits no iteration variables" */ = range seq0 {
+       }
+
+       for range seq1 {
+       }
+       for _ = range seq1 {
+       }
+       for _, _ /* ERROR "range over seq1 (value of type func(func(int) bool)) permits only one iteration variable" */ = range seq1 {
+       }
+
+       for range seq2 {
+       }
+       for _ = range seq2 {
+       }
+       for _, _ = range seq2 {
+       }
+       // Note: go/types reports a parser error in this case, hence the different error messages.
+       for _, _, _ /* ERRORx "(range clause permits at most two iteration variables|expected at most 2 expressions)" */ = range seq2 {
+       }
+}