]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: report error for range over int if Go version < 1.22
authorRobert Griesemer <gri@golang.org>
Wed, 13 Dec 2023 22:15:53 +0000 (14:15 -0800)
committerGopher Robot <gobot@golang.org>
Wed, 13 Dec 2023 22:57:57 +0000 (22:57 +0000)
Fixes #64704.

Change-Id: Ied3af46ab534343cdafba5ee27680b9c6ef3d37a
Reviewed-on: https://go-review.googlesource.com/c/go/+/549459
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/version.go
src/go/types/stmt.go
src/go/types/version.go
src/internal/types/testdata/fixedbugs/issue64704.go [new file with mode: 0644]

index 7956bf3033674a4119e2a673ad16efb068202b0d..a07bc9370a6096242669cdff2ef26f7d0c4da56d 100644 (file)
@@ -861,7 +861,9 @@ 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)
+               k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+                       return check.allowVersion(check.pkg, x.expr, v)
+               })
                switch {
                case !ok && cause != "":
                        check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause)
@@ -964,16 +966,18 @@ 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)
+       key, val, _, _, _ := rangeKeyVal(typ, nil)
        return key, val
 }
 
 // rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ. 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) (key, val Type, cause string, isFunc, ok bool) {
+// over an expression of type typ.
+// 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
        }
@@ -991,6 +995,9 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) {
                        return Typ[Int], universeRune, "", false, 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
                }
        case *Array:
@@ -1005,6 +1012,7 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) {
                }
                return typ.elem, nil, "", false, true
        case *Signature:
+               // TODO(gri) when this becomes enabled permanently, add version check
                if !buildcfg.Experiment.RangeFunc {
                        break
                }
index 12c86ef9fe6219716fcd649fa146aaceee6f6b48..5aa3c803b54996b345f11c881c28d522b7821956 100644 (file)
@@ -43,6 +43,7 @@ var (
        go1_18 = asGoVersion("go1.18")
        go1_20 = asGoVersion("go1.20")
        go1_21 = asGoVersion("go1.21")
+       go1_22 = asGoVersion("go1.22")
 
        // current (deployed) Go version
        go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
index 288d74b95a4fac8f143c5dcccabe1a02f67b58e1..35c485827d23f3bff2515c18895161399bd9abb5 100644 (file)
@@ -852,7 +852,9 @@ 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)
+               k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
+                       return check.allowVersion(check.pkg, x.expr, v)
+               })
                switch {
                case !ok && cause != "":
                        check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause)
@@ -955,10 +957,11 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
 }
 
 // rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ. 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) (key, val Type, cause string, isFunc, ok bool) {
+// over an expression of type typ.
+// 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
        }
@@ -976,6 +979,9 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) {
                        return Typ[Int], universeRune, "", false, 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
                }
        case *Array:
@@ -990,6 +996,7 @@ func rangeKeyVal(typ Type) (key, val Type, cause string, isFunc, ok bool) {
                }
                return typ.elem, nil, "", false, true
        case *Signature:
+               // TODO(gri) when this becomes enabled permanently, add version check
                if !buildcfg.Experiment.RangeFunc {
                        break
                }
index cfbab0f2a8b954ae722862f21e6214f94aac4768..f2466edc1fa1cee296d60d64afedd31686fde090 100644 (file)
@@ -44,6 +44,7 @@ var (
        go1_18 = asGoVersion("go1.18")
        go1_20 = asGoVersion("go1.20")
        go1_21 = asGoVersion("go1.21")
+       go1_22 = asGoVersion("go1.22")
 
        // current (deployed) Go version
        go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
diff --git a/src/internal/types/testdata/fixedbugs/issue64704.go b/src/internal/types/testdata/fixedbugs/issue64704.go
new file mode 100644 (file)
index 0000000..c8e9056
--- /dev/null
@@ -0,0 +1,12 @@
+// -lang=go1.21
+
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func _() {
+       for range 10 /* ERROR "cannot range over 10 (untyped int constant): requires go1.22 or later" */ {
+       }
+}