]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: fix assertion failure when range over int is not permitted
authorTim King <taking@google.com>
Tue, 9 Jul 2024 21:01:56 +0000 (14:01 -0700)
committerTim King <taking@google.com>
Fri, 12 Jul 2024 18:39:52 +0000 (18:39 +0000)
Fixes an assertion failure in Checker.rangeStmt that range over int
only has a key type and no value type. When allowVersion failed,
rangeKeyVal returns Typ[Invalid] for the value instead of nil. When
Config.Error != nil, rangeStmt proceeded. The check for rhs[1]==nil
was not enough to catch this case. It must also check rhs[1]==

Updates #68334

Change-Id: Iffa1b2f7b6a94570ec50b8c6603e727a45ba3357
Reviewed-on: https://go-review.googlesource.com/c/go/+/597356
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/types2/issues_test.go
src/cmd/compile/internal/types2/stmt.go
src/go/types/issues_test.go
src/go/types/stmt.go

index b4da3c0b915befae020440c5250d9cb23c9d859e..20e3f52facd9deb4f3f38ca8aec21cf6fd53f728 100644 (file)
@@ -1092,3 +1092,32 @@ func _() {
        conf := Config{GoVersion: "go1.17"}
        mustTypecheck(src, &conf, nil)
 }
+
+func TestIssue68334(t *testing.T) {
+       const src = `
+package p
+
+func f(x int) {
+       for i, j := range x {
+               _, _ = i, j
+       }
+       var a, b int
+       for a, b = range x {
+               _, _ = a, b
+       }
+}
+`
+
+       got := ""
+       conf := Config{
+               GoVersion: "go1.21",                                      // #68334 requires GoVersion <= 1.21
+               Error:     func(err error) { got += err.Error() + "\n" }, // #68334 requires Error != nil
+       }
+       typecheck(src, &conf, nil) // do not crash
+
+       want := "p:5:20: cannot range over x (variable of type int): requires go1.22 or later\n" +
+               "p:9:19: cannot range over x (variable of type int): requires go1.22 or later\n"
+       if got != want {
+               t.Errorf("got: %s want: %s", got, want)
+       }
+}
index 58783f47c3cec5b2d6cec83e74089ab303ba45da..b598a4f068621db562f920dfbf37455db5e912df 100644 (file)
@@ -920,14 +920,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
 
                        // initialize lhs iteration variable, if any
                        typ := rhs[i]
-                       if typ == nil {
+                       if typ == nil || typ == Typ[Invalid] {
+                               // typ == Typ[Invalid] can happen if allowVersion fails.
                                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)
+                               assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt)
                                check.initVar(obj, &x, "range clause")
                        } else {
                                var y operand
@@ -957,12 +958,12 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
 
                        // assign to lhs iteration variable, if any
                        typ := rhs[i]
-                       if typ == nil {
+                       if typ == nil || typ == Typ[Invalid] {
                                continue
                        }
 
                        if rangeOverInt {
-                               assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
+                               assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] 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.
index c99c9a9550da24e6d938751ddd96a1cff303315c..3f459d3883017efcd4e78d482948588376d43e95 100644 (file)
@@ -1102,3 +1102,32 @@ func _() {
        conf := Config{GoVersion: "go1.17"}
        mustTypecheck(src, &conf, nil)
 }
+
+func TestIssue68334(t *testing.T) {
+       const src = `
+package p
+
+func f(x int) {
+       for i, j := range x {
+               _, _ = i, j
+       }
+       var a, b int
+       for a, b = range x {
+               _, _ = a, b
+       }
+}
+`
+
+       got := ""
+       conf := Config{
+               GoVersion: "go1.21",                                      // #68334 requires GoVersion <= 1.21
+               Error:     func(err error) { got += err.Error() + "\n" }, // #68334 requires Error != nil
+       }
+       typecheck(src, &conf, nil) // do not crash
+
+       want := "p:5:20: cannot range over x (variable of type int): requires go1.22 or later\n" +
+               "p:9:19: cannot range over x (variable of type int): requires go1.22 or later\n"
+       if got != want {
+               t.Errorf("got: %s want: %s", got, want)
+       }
+}
index 215b20160d8e11b1240a6beaf4f3532da8da91b8..c9f7a4f929ad50c14de4150411d50f3afe5df27f 100644 (file)
@@ -922,14 +922,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
 
                        // initialize lhs iteration variable, if any
                        typ := rhs[i]
-                       if typ == nil {
+                       if typ == nil || typ == Typ[Invalid] {
+                               // typ == Typ[Invalid] can happen if allowVersion fails.
                                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)
+                               assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt)
                                check.initVar(obj, &x, "range clause")
                        } else {
                                var y operand
@@ -959,12 +960,12 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
 
                        // assign to lhs iteration variable, if any
                        typ := rhs[i]
-                       if typ == nil {
+                       if typ == nil || typ == Typ[Invalid] {
                                continue
                        }
 
                        if rangeOverInt {
-                               assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
+                               assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] 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.