aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/stmt.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/types2/stmt.go')
-rw-r--r--src/cmd/compile/internal/types2/stmt.go59
1 files changed, 41 insertions, 18 deletions
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index 7fd7009e13..655d072171 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -898,7 +898,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
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 *syntax.ForStmt, rclause *s
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 *syntax.ForStmt, rclause *s
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")
}