go run ./tag_lang
go run ./tag_lang_experiment
-- go.mod --
module test
go 1.22
-- tag_lang/tag.go --
//go:build go1.23
package main
func count(n int) func(yield func(i int) bool) {
return func(yield func(i int) bool) {
i := 0
for i < n && yield(i) {
i++
}
}
}
func main() {
for i := range count(5) {
println(i)
}
}
-- tag_lang_experiment/tag.go --
//go:build go1.23 || goexperiment.rangefunc
package main
func count(n int) func(yield func(i int) bool) {
return func(yield func(i int) bool) {
i := 0
for i < n && yield(i) {
i++
}
}
}
func main() {
for i := range count(5) {
println(i)
}
}
What did you see happen?
$ testscript -v repro.txtar
[...]
> go run ./tag_lang
[stderr]
0
1
2
3
4
> go run ./tag_lang_experiment
[stderr]
# test/tag_lang_experiment
tag_lang_experiment/tag.go:15:17: cannot range over count(5) (value of type func(yield func(i int) bool)): requires go1.23 or later (-lang was set to go1.22; check go.mod)
[exit status 1]
FAIL: /tmp/testscript552503763/repro.txtar/script.txtar:3: unexpected go command failure
error running repro.txtar in /tmp/testscript552503763/repro.txtar
I reckon this is a sharp edge that we should polish; cmd/go should be able to figure out that, since either build tag must be true with go1.23 || goexperiment.rangefunc, the range-over-func language feature should be enabled. I think the simplest way to solve this would be to have the go1.23 build tag imply the goexperiment.rangefunc build tag, alongside a default of GOEXPERIMENT=rangefunc being set. In the same setup, using //go:build goexperiment.rangefunc with GOEXPERIMENT=rangefunc go run works, after all.
The reason I ran into this is because we have a library that was written in a way to be compatible with iterator functions, i.e. Go funcs and methods return yield funcs today, even though we only support Go 1.21 and 1.22, where range-over-func isn't available yet. But we want to double check that range-over-func will work with those APIs once it's live, so we use a build-tagged file with go1.23 || goexperiment.rangefunc to briefly test that the range-over-func would compile and run as expected. Having the experiment build tag there is useful so it can be tested on Go 1.22 by enabling the experiment, and not just by installing Go 1.23 from master.
I reckon this is fairly niche and perhaps low priority, but given that there likely will be multiple more language changes implemented in a similar way with GOEXPERIMENT (https://github.com/golang/go/issues/57001), I think polishing this sharp edge would make it a lot easier to trial the new features before they are stabilized.
I have this same issue. I want to put my iterator methods in a file guarded by a build tag and have the module work on Go 1.22 (with or without rangefunc) or Go 1.23, but that doesn't seem to be possible.
Go version
go version devel go1.23-8496060870 2024-03-19 07:29:47 +0000 linux/amd64
Output of
go env
in your module/workspace:What did you do?
Testscript below, run via https://pkg.go.dev/github.com/rogpeppe/go-internal@v1.12.0/cmd/testscript:
What did you see happen?
What did you expect to see?
Both should work the same.
Note that this is technically working as expected given https://github.com/golang/go/issues/59033. https://pkg.go.dev/go/build/constraint#GoVersion on
go1.23
givesgo1.23
, but forgo1.23 || goexperiment.rangefunc
gives no result, so cmd/go falls back togo 1.22
from go.mod.I reckon this is a sharp edge that we should polish; cmd/go should be able to figure out that, since either build tag must be true with
go1.23 || goexperiment.rangefunc
, the range-over-func language feature should be enabled. I think the simplest way to solve this would be to have thego1.23
build tag imply thegoexperiment.rangefunc
build tag, alongside a default ofGOEXPERIMENT=rangefunc
being set. In the same setup, using//go:build goexperiment.rangefunc
withGOEXPERIMENT=rangefunc go run
works, after all.The reason I ran into this is because we have a library that was written in a way to be compatible with iterator functions, i.e. Go funcs and methods return yield funcs today, even though we only support Go 1.21 and 1.22, where range-over-func isn't available yet. But we want to double check that range-over-func will work with those APIs once it's live, so we use a build-tagged file with
go1.23 || goexperiment.rangefunc
to briefly test that the range-over-func would compile and run as expected. Having the experiment build tag there is useful so it can be tested on Go 1.22 by enabling the experiment, and not just by installing Go 1.23 from master.I reckon this is fairly niche and perhaps low priority, but given that there likely will be multiple more language changes implemented in a similar way with GOEXPERIMENT (https://github.com/golang/go/issues/57001), I think polishing this sharp edge would make it a lot easier to trial the new features before they are stabilized.