Skip to content

cmd/fix,x/tools/go/analysis/passes/modernize: rangeint uses target platform's type in the range expression, breaking other platforms #77691

@dagood

Description

@dagood

Go version

go version go1.26.0 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/dagood/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/dagood/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2735933558=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/dagood/go/pkg/mod'
GONOPROXY=[...]
GONOSUMDB=[...]
GOOS='linux'
GOPATH='/home/dagood/go'
GOPRIVATE=[...]
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/dagood/sdk/go1.26.0'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/dagood/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/home/dagood/sdk/go1.26.0/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.26.0'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

This main.go file gives us a for loop over int64 on linux-amd64 but int32 on linux-arm:

package main

import "syscall"

func main() {
	var ts syscall.Timespec
	// tries needs to be the same type as syscall.Timespec.Nsec
	// but the fields are int32 on 32bit and int64 on 64bit.
	// tries is assigned to syscall.Timespec.Nsec in order to match its type.
	tries := ts.Nsec
	for tries = 0; tries < 20; tries++ {
		println(tries)
	}
}

I'm trying to use go fix on a larger repo that includes some code like this, and it broke that code and caused PR testing to fail. (Just to say that it happened in a real project, not that it's necessarily severe. 😄 This is admittedly a bit of an odd case.)

What did you see happen?

The go fix output depends on the current platform, and produces code that isn't compatible between platforms:

$ go fix -rangeint -diff ./main.go
--- /home/dagood/try/fix/main.go (old)
+++ /home/dagood/try/fix/main.go (new)
@@ -8,7 +8,7 @@
        // but the fields are int32 on 32bit and int64 on 64bit.
        // tries is assigned to syscall.Timespec.Nsec in order to match its type.
        tries := ts.Nsec
-       for tries = 0; tries < 20; tries++ {
+       for tries = range int64(20) {
                println(tries)
        }
 }

$ GOARCH=arm go fix -rangeint -diff ./main.go
--- /home/dagood/try/fix/main.go (old)
+++ /home/dagood/try/fix/main.go (new)
@@ -8,7 +8,7 @@
        // but the fields are int32 on 32bit and int64 on 64bit.
        // tries is assigned to syscall.Timespec.Nsec in order to match its type.
        tries := ts.Nsec
-       for tries = 0; tries < 20; tries++ {
+       for tries = range int32(20) {
                println(tries)
        }
 }

Specifically, if I try to use the fixed code in a linux-arm build, I get a build error:

$ go fix -rangeint ./main.go
$ GOARCH=arm go build ./main.go
# command-line-arguments
./main.go:11:20: cannot use int64(20) (constant 20 of type int64) as int32 value in range clause

What did you expect to see?

The result should compile.

I would expect this code result, but I don't know if there are subtleties that make it infeasible:

	for tries = range 20 {
		println(tries)
	}

Metadata

Metadata

Assignees

Labels

BugReportIssues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.ToolsThis label describes issues relating to any tools in the x/tools repository.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions