While at it, slightly improve documentation and code.
Also, add additional test cases for #66561.
Updates #66561.
Fixes #67027.
Change-Id: I682b0e9227e065d6bbd199871c2e1ecff13edc66
Reviewed-on: https://go-review.googlesource.com/c/go/+/580937
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil
rhs := [2]Type{key, val} // key, val may be nil
- constIntRange := x.mode == constant_ && isInteger(x.typ)
+ rangeOverInt := isInteger(x.typ)
if isDef {
// short variable declaration
continue
}
- // initialize lhs variable
- if constIntRange {
+ if rangeOverInt {
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
check.initVar(obj, &x, "range clause")
} else {
- x.mode = value
- x.expr = lhs // we don't have a better rhs expression to use here
- x.typ = typ
- check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
+ var y operand
+ y.mode = value
+ y.expr = lhs // we don't have a better rhs expression to use here
+ y.typ = typ
+ check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
}
assert(obj.typ != nil)
}
continue
}
- if constIntRange {
+ if rangeOverInt {
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
check.assignVar(lhs, nil, &x, "range clause")
+ // If the assignment succeeded, if x was untyped before, it now
+ // has a type inferred via the assignment. It must be an integer.
+ // (go.dev/issues/67027)
+ if x.mode != invalid && !isInteger(x.typ) {
+ check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
+ }
} else {
- x.mode = value
- x.expr = lhs // we don't have a better rhs expression to use here
- x.typ = typ
- check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
+ var y operand
+ y.mode = value
+ y.expr = lhs // we don't have a better rhs expression to use here
+ y.typ = typ
+ check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
}
}
- } else if constIntRange {
+ } else if rangeOverInt {
// If we don't have any iteration variables, we still need to
// check that a (possibly untyped) integer range expression x
// is valid.
// We do this by checking the assignment _ = x. This ensures
- // that an untyped x can be converted to a value of type int.
+ // that an untyped x can be converted to a value of its default
+ // type (rune or int).
check.assignment(&x, nil, "range clause")
}
lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil
rhs := [2]Type{key, val} // key, val may be nil
- constIntRange := x.mode == constant_ && isInteger(x.typ)
+ rangeOverInt := isInteger(x.typ)
if isDef {
// short variable declaration
continue
}
- // initialize lhs variable
- if constIntRange {
+ if rangeOverInt {
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
check.initVar(obj, &x, "range clause")
} else {
- x.mode = value
- x.expr = lhs // we don't have a better rhs expression to use here
- x.typ = typ
- check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
+ var y operand
+ y.mode = value
+ y.expr = lhs // we don't have a better rhs expression to use here
+ y.typ = typ
+ check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
}
assert(obj.typ != nil)
}
continue
}
- if constIntRange {
+ if rangeOverInt {
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
check.assignVar(lhs, nil, &x, "range clause")
+ // If the assignment succeeded, if x was untyped before, it now
+ // has a type inferred via the assignment. It must be an integer.
+ // (go.dev/issues/67027)
+ if x.mode != invalid && !isInteger(x.typ) {
+ check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
+ }
} else {
- x.mode = value
- x.expr = lhs // we don't have a better rhs expression to use here
- x.typ = typ
- check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
+ var y operand
+ y.mode = value
+ y.expr = lhs // we don't have a better rhs expression to use here
+ y.typ = typ
+ check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
}
}
- } else if constIntRange {
+ } else if rangeOverInt {
// If we don't have any iteration variables, we still need to
// check that a (possibly untyped) integer range expression x
// is valid.
// We do this by checking the assignment _ = x. This ensures
- // that an untyped x can be converted to a value of type int.
+ // that an untyped x can be converted to a value of its default
+ // type (rune or int).
check.assignment(&x, nil, "range clause")
}
return Typ[Int], universeRune, "", false, 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")
}
for y /* ERROR "declared and not used" */ := range "" {
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
}
- for range 1.5 /* ERROR "cannot range over 1.5" */ {
+ for range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ {
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
}
- for y := range 1.5 /* ERROR "cannot range over 1.5" */ {
+ for y := range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ {
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
}
}
for u8 = range 256 /* ERROR "cannot use 256 (untyped int constant) as uint8 value in range clause (overflows)" */ {
}
}
+
+func issue64471() {
+ for i := range 'a' {
+ var _ *rune = &i // ensure i has type rune
+ }
+}
+
+func issue66561() {
+ for range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
+ }
+ for range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ {
+ }
+ for range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i {
+ }
+
+ for range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
+ }
+ for range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ {
+ }
+
+ for i := range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
+ _ = i
+ }
+ for i := range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ {
+ _ = i
+ }
+ for i := range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i {
+ _ = i
+ }
+
+ for i := range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
+ _ = i
+ }
+ for i := range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ {
+ _ = i
+ }
+
+ var j float64
+ _ = j
+ for j /* ERROR "cannot use iteration variable of type float64" */ = range 1 {
+ }
+ for j = range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
+ }
+ for j = range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
+ }
+
+ // There shouldn't be assignment errors if there are more iteration variables than permitted.
+ var i int
+ _ = i
+ for i, j /* ERROR "range over 10 (untyped int constant) permits only one iteration variable" */ = range 10 {
+ }
+}
+
+func issue67027() {
+ var i float64
+ _ = i
+ for i /* ERROR "cannot use iteration variable of type float64" */ = range 10 {
+ }
+}