diff options
Diffstat (limited to 'src/go/types/stmt.go')
-rw-r--r-- | src/go/types/stmt.go | 60 |
1 files changed, 42 insertions, 18 deletions
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 30b4948216..258ad1d327 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -898,7 +898,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 @@ -923,19 +923,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 @@ -954,21 +962,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") } @@ -998,6 +1021,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") } |