]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.22] go/types, types2: report error for floating-point iteration...
authorRobert Griesemer <gri@golang.org>
Mon, 22 Apr 2024 22:52:59 +0000 (15:52 -0700)
committerJoedian Reid <joedian@google.com>
Mon, 24 Jun 2024 17:14:05 +0000 (17:14 +0000)
While at it, slightly improve documentation and code.
Also, add additional test cases for #66561.

Updates #66561.
Fixes #67798.

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>
Reviewed-on: https://go-review.googlesource.com/c/go/+/590056

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 c9713dac6fe7ff5a387c13b08e30269fc216dafe..d519657b6b153ebe9111478fd6323a5b90e7f9a5 100644 (file)
@@ -902,7 +902,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
@@ -927,19 +927,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
@@ -958,21 +966,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")
        }
 
index 80f3ac75da2cd8255383015dba6d6c6ff03af8d8..bb203f130c192a43ac8e4184d0e444a7cbed0b9d 100644 (file)
@@ -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")
                        }
index b61f1c72320f79cf22388a078ca85f3747378c8b..d7ae8f8a02bfc36f0febe22c5b8adc55b94cd734 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 {
+       }
+}