]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: report error for floating-point iteration variable
authorRobert Griesemer <gri@golang.org>
Mon, 22 Apr 2024 22:52:59 +0000 (15:52 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 16 May 2024 18:56:00 +0000 (18:56 +0000)
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>

src/cmd/compile/internal/types2/stmt.go
src/go/types/stmt.go
src/internal/types/testdata/check/stmt0.go
src/internal/types/testdata/spec/range_int.go

index 1984777008916b10f0e3f28a118af9eb4e6558b9..655d072171cb49e365d16c90ff404977dd7d1ebc 100644 (file)
@@ -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
@@ -933,14 +933,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
                                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)
                }
@@ -967,21 +968,30 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
                                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")
        }
 
index bfb51fd2e51aca0b823588ea8b73d2e2c6cca0b2..258ad1d32759e9b55941a38907b35193eab77e4c 100644 (file)
@@ -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
@@ -933,14 +933,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
                                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)
                }
@@ -967,21 +968,30 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
                                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")
        }
 
@@ -1011,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")
                        }
index a6c47cb48301a2ad8535ebbafafb22f8359c6c8f..ea161279c65cf7937f4be41333811bafe54b3197 100644 (file)
@@ -953,10 +953,10 @@ func issue10148() {
        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
        }
 }
index 7f722e2d997c03c204a4efb0a05c12787e956661..766736cc1556efc904f7a7355286f42e860bb296 100644 (file)
@@ -129,3 +129,62 @@ func issue65133() {
        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 {
+       }
+}