Go version
go version go1.22.0 linux/amd64
Output of go env
in your module/workspace:
.
What did you do?
main.go:
//go:build go1.21
package main
func main() {
for i, p := 0, (*int)(nil); p == nil; println(p == &i) {
p = &i
}
}
go run main.go
What did you see happen?
true
What did you expect to see?
false
Note that when the -gcflags=-lang=go1.22
compiler flag is specified, then the "//go:build go1.xy" comment directive is respected (go run .
with a go.mod file also respects it).
$ gotv 1.22. run main.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run main.go
false
$ gotv 1.22. run -gcflags=-lang=go1.22 main.go
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -gcflags=-lang=go1.22 main.go
true
$ gotv 1.22. run .
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run .
true
$ gotv 1.22. run -gcflags=-lang=go1.22 .
[Run]: $HOME/.cache/gotv/tag_go1.22.0/bin/go run -gcflags=-lang=go1.22 .
true
Comment From: bcmills
This is closely related to
- #65612
and https://go.dev/cl/567435 (CC @samthanawalla @matloob), in that cmd/go
sometimes doesn't pass a language version for a main.go
file when it could.
However, in the absence of any -lang
flag, I expect cmd/compile
to still apply whatever version information it has inferred from the //go:build
directives, and it appears not to be doing so. This looks like a compiler bug.
Comment From: mknyszek
CC @golang/compiler
Comment From: zigo101
@bcmills
Just a btw question: it looks the -gcflags=-lang=go1.xy
compiler flag is only applied to the seed files being passed to the compiler. Other involved source files in building will not be affected by the flag. Is this the intended design?
Comment From: mknyszek
In runtime/compiler triage, we think this is a Go command issue? Is that right? Thanks.
Comment From: bcmills
I think it is a composite of a cmd/go
bug (not passing the -lang
flag) and a cmd/compile
bug (not using //go:build
version hints when -lang
is not explicitly set).
The cmd/go
bug should be fixed in #65612, so I suggest we focus the investigation in this issue on the cmd/compile
side.
Comment From: zigo101
Will this be fixed in 1.22.3+?
Comment From: griesemer
When no -lang is specified, in general we don't know how to interpret //go:build
lines in files because their interpretation may depend on the current -lang version.
cc: @rsc for input.
Comment From: zigo101
Is it a good idea to use the version of the go
command as the -lang
arg?
Comment From: rsc
@go101 Is the top comment wrong? It says got true, expected false for go run main.go
, but with Go 1.21 I get false and Go 1.22 I get true. If you think there is a bug in Go 1.22, then did you mean "got false, expected true"?
Comment From: zigo101
I mean the //go:build go1.21
line should matter, so that the results should be consistent with Go toolchain 1.21 and 1.22.
Comment From: rsc
I'm just trying to puzzle through this all since I don't know what "gotv 1.22." does either...
Comment From: zigo101
gotv
is a tool used to switch Go toolchain versions conveniently: https://go101.org/apps-and-libs/gotv.html
gotv 1.22.
means using the latest version prefixed by 1.22
. It will be expanded to $HOME/.cache/gotv/tag_go1.22.0/bin/go
. :)
Comment From: rsc
For future bug reports it would help to use reproduction instructions that are limited to standard tooling.
Comment From: rsc
Here is a clearer reproduction case:
% cd /tmp
% cat x.go
//go:build go1.21
package main
func main() {
for i, p := 0, (*int)(nil); p == nil; check(p == &i) {
p = &i
}
}
func check(b bool) {
if b {
println("using old semantics")
} else {
println("using new semantics")
}
}
% GOTOOLCHAIN=go1.21.0 go run x.go
using old semantics
% GOTOOLCHAIN=go1.22.0 go run x.go
using new semantics
% GOTOOLCHAIN=go1.22.0 go run -gcflags=-lang=go1.22 x.go
using old semantics
%
Comment From: rsc
CL 567435 in progress should fix this. The fix is to just make sure that cmd/go passes -lang to the compiler always.
I looked into changing what the compiler does when -lang is empty, but right now it intentionally ignores the //go:build lines to avoid downgrades, and for good reason (for example see GOROOT/src/internal/types/testdata/check/go1_xx_19.go). If we change the compiler, we risk breaking other uses of the compiler like Bazel. And the go command needs the logic anyway to help cmd/vet, so we might as well just make sure the go command always makes the decision.
Comment From: gopherbot
Change https://go.dev/cl/567435 mentions this issue: cmd/go: set the GoVersion for files listed on the commandline
Comment From: gopherbot
Change https://go.dev/cl/591136 mentions this issue: cmd/go: set the GoVersion for files listed on the commandline with vet
Comment From: gopherbot
Change https://go.dev/cl/593156 mentions this issue: cmd/go: set GoVersion for files on the command line with vet
Comment From: zigo101
Will this be backported to 1.22 and 1.23?
Comment From: samthanawalla
@gopherbot pretty please backport this to 1.21 and 1.22
Comment From: gopherbot
Backport issue(s) opened: #68048 (for 1.21), #68049 (for 1.22).
Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.
Comment From: gopherbot
Change https://go.dev/cl/593315 mentions this issue: cmd/go: set GoVersion for files on the command line with vet
Comment From: gopherbot
Change https://go.dev/cl/593295 mentions this issue: cmd/go: set GoVersion for files on the command line with vet
Comment From: gopherbot
Change https://go.dev/cl/593297 mentions this issue: go/analysis/passes/buildtag: retire Go 1.15 support
Comment From: gopherbot
Change https://go.dev/cl/593376 mentions this issue: all: update vendored golang.org/x/tools
Comment From: matloob
@zigo101 We're looking into backporting it, but we'd like more information to see if this meets the bar to be backported. The backports policy states that backports should be "fixes for security issues, serious problems with no workaround, and documentation fixes". This isn't a security issue or documentation fix, so the question is whether this is a serious problem with no workaround. Could you give us more information about whether this is a serious problem with no workaround?
Comment From: zigo101
Personally, I think it is a serious problem with no workaround. Without this fix, Go breaks Go 1 promise of compatibility with any standard.
Comment From: matloob
Hi @zigo101, could you provide more information about what your use case is and why there is no workaround?
Comment From: zigo101
@matloob
The example in the first comment of this thread is my use case.
I use some alike single Go files as scripts (use go run xyz.go
to run it).
I don't want to modify the source other than simple adding a //go:build go1.21
line at the start.
What is the workaround do you think?
Comment From: matloob
Hi, the workaround would depend on the environment your programs is running in, what the program does, why you can't modify the programs, etc.
The best workaround would be to put your each of your single file programs in a module and run the program as a package instead of passing in individual files to the go command. If your program depends on the semantics of the version of Go it's being run under, that's your best bet, and I would recommend doing that even after this change is in. What this change does is always pass -lang
to cmd/compile. But the primary way to set that -lang
is through go.mod
, and running your program in a module context.
There are other, less desirable, things you could do to get your builds to work: for example, you could pass in the -gcflags
flag as you suggested in your first comment or modify the source. I understand that those options do not result in a good user experience, so I'd recommend going the module route if you can.
Comment From: zigo101
Okay, let me explain it clearly. I have written many Go books and tutorials. I just want to show gophers that how to use //go:build
directives to control code behaviors. I hope the content works with Go toolchain v1.22 and v1.23. I hope gophers don't need the workarounds you provided, because it should work just by using //go:build
directives, as the Go docs says and as users expect.
Without the fix for v1.22 and v1.23, it is hard to say v1.22 and v1.23 don't break Go 1 promise of compatibility.
Comment From: zigo101
BTW, maybe it is meaningless to backport this to 1.21.
The behavior of panic(nil)
is not controlled by go versions.
Comment From: matloob
@zigo101 Thanks for your explanation. Unfortunately this doesn't meet the bar for backporting. It doesn't seem like there are user workflows that are broken by the incorrect behavior with //go:build
downgrades in Go 1.22. And although we acknowledge that it isn't ideal to have to use a workaround, a workaround does exist.
Comment From: zigo101
It doesn't seem like there are user workflows that are broken by the incorrect behavior with //go:build downgrades in Go 1.22.
Maybe, The //go:build
hack only become so important since Go 1.22. :D
It is used here just to keep some old code behavior unchanged with a minimal effort.
As for workarounds, I think most of other backported issues also have their own wordarounds.
Persnoally, I just hope this fix can be backported, to let your Go dev get a better reputation on code quality. But if you don't care about it much, I have nothing more to say. :D
This will not affect my production code, because I'm fully aware all the problems caused
by the new semantics of tranditional for;;
loops and related bugs in the old toolchain versions.
I just can't make sure whether or not these problems and bugs will affect other people's production code.
I will help you make this known to my readers on my website, books and social platforms.
Comment From: samthanawalla
@zigo101 I'm sorry, I know it's frustrating. Thanks for looking out for Go developers and sharing this with your audience. You're doing the community a solid.
Comment From: zigo101
Just found this fix has been merged into v1.23rc1. Interesting, so it is worth being merged into 1.23, but not 1.22.
Comment From: dmitshur
@zigo101 When fixes are made, they're included in the next major release. That is the default, and this resolved issue is in the Go1.23 milestone. Only in rare cases when it is deemed necessary they are also backported to currently supported major releases (1.22 and 1.21 at this time). See https://go.dev/s/release and https://go.dev/wiki/MinorReleases for more information on the Go release process.
Comment From: zigo101
Okay, glad to know it is deliberately.