Prologue
An user reported a weird behavior of go
command in golang-dev mailing list.
Here’s an equivalent program on playground, or inlined below, to reproduce the bug (with go 1.17 and below):
package main
func main() {
var s = []int{1, 2, 3}
_ = (*[2]int)(s[1:])
}
-- go.mod --
module example.com
go 1.16
Running about program give an error:
$ go1.17 run -gcflags=-lang=go1.17 main.go
# command-line-arguments
./main.go:5:15: cannot convert s[1:] (type []int) to type *[2]int:
conversion of slices to array pointers only supported as of -lang=go1.17
It’s weird, isn’t that we tell the compiler that we do want -lang=go1.17
?
I pointed out why go
behaves like that, and @ianlancetaylor suggested that it’s a bug.
The bug
Since then, an issue was opened on golang Github issues. And the conclusion is that cmd/go
is passing the wrong order of flags to cmd/compile
,
which causes the bug happens. This bug exists at least since go version 1.5, and it’s surprised to me that no one has reported before.
Note that this affects not only -lang
, but also any compiler flags, example:
$ cat main.go
package main
func main() {}
$ go1.17 build -a -x -work -gcflags=-buildid=foo main.go
WORK=/var/folders/rz/mxg9f82d2kl7jycb_rvrdp2r0000gn/T/go-build1278557172
...
<truncated output>
...
cd /Users/cuonglm
./sdk/go1.17/pkg/tool/darwin_arm64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -shared -buildid=foo -p main -lang=go1.17 -complete -buildid VdM2tsNIpf8Qhrtrmssj/VdM2tsNIpf8Qhrtrmssj -goversion go1.17 -D _/Users/cuonglm -importcfg $WORK/b001/importcfg -pack ./main.go $WORK/b001/_gomod_.go
/Users/cuonglm/sdk/go1.17/pkg/tool/darwin_arm64/buildid -w $WORK/b001/_pkg_.a # internal
...
$ WORK=/var/folders/rz/mxg9f82d2kl7jycb_rvrdp2r0000gn/T/go-build1278557172
$ go1.17 tool buildid $WORK/b001/_pkg_.a
VdM2tsNIpf8Qhrtrmssj/VdM2tsNIpf8Qhrtrmssj
The buildid
is not foo
as expected.
Taking a quick look at the cmd/go code, I wonder whether we can just swap the order of gcflags
and gcargs
.
@jayconrod said that (emphasis mine):
The
flag
package requires that flags appear before positional arguments.
It does not make sense, since when there’s no positional arguments involve in gcargs
, it contains all the compiler flags that cmd/go
wants to pass
to the compiler.
So it’s clear that cmd/go
is passing the wrong order of flags to the compiler.
Fixing
In CL 344909 that I sent to fix the bug, not only the wrong order was fixed, but also the misleading variable name gcargs
was removed. With the fix,
cmd/go
can now get it right:
$ go build -a -x -work -gcflags=-buildid=foo main.go
WORK=/var/folders/rz/mxg9f82d2kl7jycb_rvrdp2r0000gn/T/go-build2599132168
...
<truncated output>
...
cd /Users/cuonglm
./sources/go/pkg/tool/darwin_arm64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.18 -complete -buildid zjrn3d_w3mjLM_-u-69Q/zjrn3d_w3mjLM_-u-69Q -shared -buildid=foo -D _/Users/cuonglm -importcfg $WORK/b001/importcfg -pack ./main.go $WORK/b001/_gomod_.go
/Users/cuonglm/sources/go/pkg/tool/darwin_arm64/buildid -w $WORK/b001/_pkg_.a # internal
...
$ WORK=/var/folders/rz/mxg9f82d2kl7jycb_rvrdp2r0000gn/T/go-build2599132168
$ go tool buildid $WORK/b001/_pkg_.a
foo
NOTE
While fixing this bug, I learn a cool trick to use go build -n
for testing this kind of bug, instead of actually building the program.
That would speed up go test cmd/go
, which sometimes will give you timeout when you run in your local machine.
Epilogue
If you faced any issue after this fix (though, it’s very unlikely, my 2ยข), please file an issue or shoot me an email!
Thanks for reading so far.
Till next time!