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>
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)
}
// 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
}
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:
}
return typ.elem, nil, "", false, true
case *Signature:
+ // TODO(gri) when this becomes enabled permanently, add version check
if !buildcfg.Experiment.RangeFunc {
break
}
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))
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)
}
// 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
}
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:
}
return typ.elem, nil, "", false, true
case *Signature:
+ // TODO(gri) when this becomes enabled permanently, add version check
if !buildcfg.Experiment.RangeFunc {
break
}
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))
--- /dev/null
+// -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" */ {
+ }
+}