|
@@ -893,7 +893,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|
|
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
|
|
@@ -918,19 +918,27 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|
|
check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
|
|
|
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
|
|
|
}
|
|
|
+ assert(obj.typ == nil)
|
|
|
|
|
|
- // initialize lhs variable
|
|
|
- if constIntRange {
|
|
|
- check.initVar(obj, &x, "range clause")
|
|
|
- } else if typ := rhs[i]; typ != nil {
|
|
|
- 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"
|
|
|
- } else {
|
|
|
+ // initialize lhs iteration variable, if any
|
|
|
+ typ := rhs[i]
|
|
|
+ if typ == nil {
|
|
|
obj.typ = Typ[Invalid]
|
|
|
obj.used = true // don't complain about unused variable
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if rangeOverInt {
|
|
|
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
|
|
+ check.initVar(obj, &x, "range clause")
|
|
|
+ } else {
|
|
|
+ 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)
|
|
|
}
|
|
|
|
|
|
// declare variables
|
|
@@ -949,21 +957,36 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- if constIntRange {
|
|
|
+ // assign to lhs iteration variable, if any
|
|
|
+ typ := rhs[i]
|
|
|
+ if typ == nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if rangeOverInt {
|
|
|
+ assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
|
|
check.assignVar(lhs, nil, &x, "range clause")
|
|
|
- } else if typ := rhs[i]; typ != nil {
|
|
|
- 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"
|
|
|
+ // 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 {
|
|
|
+ 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")
|
|
|
}
|
|
|
|
|
@@ -993,6 +1016,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
|
|
|
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")
|
|
|
}
|