Skip to content

Commit 7afe17b

Browse files
matloobprattmic
authored andcommitted
[release-branch.go1.23] go/types, types2: use max(fileVersion, go1.21) if fileVersion present
Change the rules for how //go:build "file versions" are applied: instead of considering whether a file version is an upgrade or downgrade from the -lang version, always use max(fileVersion, go1.21). This prevents file versions from downgrading the version below go1.21. Before Go 1.21 the //go:build version did not have the meaning of setting the file's langage version. This fixes an issue that was appearing in GOPATH builds: Go 1.23.0 started providing -lang versions to the compiler in GOPATH mode (among other places) which it wasn't doing before, and it set -lang to the toolchain version (1.23). Because the -lang version was greater than go1.21, language version used to compile the file would be set to the //go:build file version. //go:build file versions below 1.21 could cause files that could previously build to stop building. For example, take a Go file with a //go:build line specifying go1.10. If that file used a 1.18 feature, that use would compile fine with a Go 1.22 toolchain. But it would produce an error when compiling with the 1.23.0 toolchain because it set the language version to 1.10 and disallowed the 1.18 feature. This breaks backwards compatibility: when the build tag was added, it did not have the meaning of restricting the language version. For #68658 Fixes #69094 Change-Id: I6cedda81a55bcccffaa3501eef9e2be6541b6ece Reviewed-on: https://go-review.googlesource.com/c/go/+/607955 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> (cherry picked from commit aeac0b6) Reviewed-on: https://go-review.googlesource.com/c/go/+/608935
1 parent 8002845 commit 7afe17b

File tree

11 files changed

+174
-99
lines changed

11 files changed

+174
-99
lines changed

src/cmd/compile/internal/types2/api_test.go

+38-12
Original file line numberDiff line numberDiff line change
@@ -2898,22 +2898,48 @@ func TestFileVersions(t *testing.T) {
28982898
fileVersion string
28992899
wantVersion string
29002900
}{
2901-
{"", "", ""}, // no versions specified
2902-
{"go1.19", "", "go1.19"}, // module version specified
2903-
{"", "go1.20", ""}, // file upgrade ignored
2904-
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
2905-
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
2906-
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
2901+
{"", "", ""}, // no versions specified
2902+
{"go1.19", "", "go1.19"}, // module version specified
2903+
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2904+
{"go1", "", "go1"}, // no file version specified
2905+
{"go1", "goo1.22", "go1"}, // invalid file version specified
2906+
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2907+
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2908+
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
2909+
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
2910+
{"go1.19", "", "go1.19"}, // no file version specified
2911+
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
2912+
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2913+
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
2914+
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
2915+
{"go1.20", "", "go1.20"}, // no file version specified
2916+
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
2917+
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2918+
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2919+
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
2920+
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
2921+
{"go1.21", "", "go1.21"}, // no file version specified
2922+
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
2923+
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2924+
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2925+
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
2926+
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
2927+
{"go1.22", "", "go1.22"}, // no file version specified
2928+
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
2929+
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2930+
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2931+
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
2932+
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
29072933

29082934
// versions containing release numbers
29092935
// (file versions containing release numbers are considered invalid)
29102936
{"go1.19.0", "", "go1.19.0"}, // no file version specified
2911-
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
2912-
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
2913-
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
2914-
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
2915-
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
2916-
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
2937+
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
2938+
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
2939+
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
2940+
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
2941+
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
2942+
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
29172943
} {
29182944
var src string
29192945
if test.fileVersion != "" {

src/cmd/compile/internal/types2/check.go

+19-28
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@ func (check *Checker) initFiles(files []*syntax.File) {
327327
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
328328
check.version, go_current)
329329
}
330-
downgradeOk := check.version.cmp(go1_21) >= 0
331330

332331
// determine Go version for each file
333332
for _, file := range check.files {
@@ -336,33 +335,18 @@ func (check *Checker) initFiles(files []*syntax.File) {
336335
// unlike file versions which are Go language versions only, if valid.)
337336
v := check.conf.GoVersion
338337

339-
fileVersion := asGoVersion(file.GoVersion)
340-
if fileVersion.isValid() {
341-
// use the file version, if applicable
342-
// (file versions are either the empty string or of the form go1.dd)
343-
if pkgVersionOk {
344-
cmp := fileVersion.cmp(check.version)
345-
// Go 1.21 introduced the feature of setting the go.mod
346-
// go line to an early version of Go and allowing //go:build lines
347-
// to “upgrade” (cmp > 0) the Go version in a given file.
348-
// We can do that backwards compatibly.
349-
//
350-
// Go 1.21 also introduced the feature of allowing //go:build lines
351-
// to “downgrade” (cmp < 0) the Go version in a given file.
352-
// That can't be done compatibly in general, since before the
353-
// build lines were ignored and code got the module's Go version.
354-
// To work around this, downgrades are only allowed when the
355-
// module's Go version is Go 1.21 or later.
356-
//
357-
// If there is no valid check.version, then we don't really know what
358-
// Go version to apply.
359-
// Legacy tools may do this, and they historically have accepted everything.
360-
// Preserve that behavior by ignoring //go:build constraints entirely in that
361-
// case (!pkgVersionOk).
362-
if cmp > 0 || cmp < 0 && downgradeOk {
363-
v = file.GoVersion
364-
}
365-
}
338+
// If the file specifies a version, use max(fileVersion, go1.21).
339+
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
340+
// Go 1.21 introduced the feature of allowing //go:build lines
341+
// to sometimes set the Go version in a given file. Versions Go 1.21 and later
342+
// can be set backwards compatibly as that was the first version
343+
// files with go1.21 or later build tags could be built with.
344+
//
345+
// Set the version to max(fileVersion, go1.21): That will allow a
346+
// downgrade to a version before go1.22, where the for loop semantics
347+
// change was made, while being backwards compatible with versions of
348+
// go before the new //go:build semantics were introduced.
349+
v = string(versionMax(fileVersion, go1_21))
366350

367351
// Report a specific error for each tagged file that's too new.
368352
// (Normally the build system will have filtered files by version,
@@ -377,6 +361,13 @@ func (check *Checker) initFiles(files []*syntax.File) {
377361
}
378362
}
379363

364+
func versionMax(a, b goVersion) goVersion {
365+
if a.cmp(b) > 0 {
366+
return a
367+
}
368+
return b
369+
}
370+
380371
// A bailout panic is used for early termination.
381372
type bailout struct{}
382373

src/go/types/api_test.go

+38-12
Original file line numberDiff line numberDiff line change
@@ -2904,22 +2904,48 @@ func TestFileVersions(t *testing.T) {
29042904
fileVersion string
29052905
wantVersion string
29062906
}{
2907-
{"", "", ""}, // no versions specified
2908-
{"go1.19", "", "go1.19"}, // module version specified
2909-
{"", "go1.20", ""}, // file upgrade ignored
2910-
{"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
2911-
{"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
2912-
{"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
2907+
{"", "", ""}, // no versions specified
2908+
{"go1.19", "", "go1.19"}, // module version specified
2909+
{"", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2910+
{"go1", "", "go1"}, // no file version specified
2911+
{"go1", "goo1.22", "go1"}, // invalid file version specified
2912+
{"go1", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2913+
{"go1", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2914+
{"go1", "go1.21", "go1.21"}, // file version specified at 1.21
2915+
{"go1", "go1.22", "go1.22"}, // file version specified above 1.21
2916+
{"go1.19", "", "go1.19"}, // no file version specified
2917+
{"go1.19", "goo1.22", "go1.19"}, // invalid file version specified
2918+
{"go1.19", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2919+
{"go1.19", "go1.21", "go1.21"}, // file version specified at 1.21
2920+
{"go1.19", "go1.22", "go1.22"}, // file version specified above 1.21
2921+
{"go1.20", "", "go1.20"}, // no file version specified
2922+
{"go1.20", "goo1.22", "go1.20"}, // invalid file version specified
2923+
{"go1.20", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2924+
{"go1.20", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2925+
{"go1.20", "go1.21", "go1.21"}, // file version specified at 1.21
2926+
{"go1.20", "go1.22", "go1.22"}, // file version specified above 1.21
2927+
{"go1.21", "", "go1.21"}, // no file version specified
2928+
{"go1.21", "goo1.22", "go1.21"}, // invalid file version specified
2929+
{"go1.21", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2930+
{"go1.21", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2931+
{"go1.21", "go1.21", "go1.21"}, // file version specified at 1.21
2932+
{"go1.21", "go1.22", "go1.22"}, // file version specified above 1.21
2933+
{"go1.22", "", "go1.22"}, // no file version specified
2934+
{"go1.22", "goo1.22", "go1.22"}, // invalid file version specified
2935+
{"go1.22", "go1.19", "go1.21"}, // file version specified below minimum of 1.21
2936+
{"go1.22", "go1.20", "go1.21"}, // file version specified below minimum of 1.21
2937+
{"go1.22", "go1.21", "go1.21"}, // file version specified at 1.21
2938+
{"go1.22", "go1.22", "go1.22"}, // file version specified above 1.21
29132939

29142940
// versions containing release numbers
29152941
// (file versions containing release numbers are considered invalid)
29162942
{"go1.19.0", "", "go1.19.0"}, // no file version specified
2917-
{"go1.20", "go1.20.1", "go1.20"}, // file upgrade ignored
2918-
{"go1.20.1", "go1.20", "go1.20.1"}, // file upgrade ignored
2919-
{"go1.20.1", "go1.21", "go1.21"}, // file upgrade permitted
2920-
{"go1.20.1", "go1.19", "go1.20.1"}, // file downgrade not permitted
2921-
{"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
2922-
{"go1.21.1", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
2943+
{"go1.20.1", "go1.19.1", "go1.20.1"}, // invalid file version
2944+
{"go1.20.1", "go1.21.1", "go1.20.1"}, // invalid file version
2945+
{"go1.21.1", "go1.19.1", "go1.21.1"}, // invalid file version
2946+
{"go1.21.1", "go1.21.1", "go1.21.1"}, // invalid file version
2947+
{"go1.22.1", "go1.19.1", "go1.22.1"}, // invalid file version
2948+
{"go1.22.1", "go1.21.1", "go1.22.1"}, // invalid file version
29232949
} {
29242950
var src string
29252951
if test.fileVersion != "" {

src/go/types/check.go

+20-28
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,6 @@ func (check *Checker) initFiles(files []*ast.File) {
349349
check.errorf(files[0], TooNew, "package requires newer Go version %v (application built with %v)",
350350
check.version, go_current)
351351
}
352-
downgradeOk := check.version.cmp(go1_21) >= 0
353352

354353
// determine Go version for each file
355354
for _, file := range check.files {
@@ -358,33 +357,19 @@ func (check *Checker) initFiles(files []*ast.File) {
358357
// unlike file versions which are Go language versions only, if valid.)
359358
v := check.conf.GoVersion
360359

361-
fileVersion := asGoVersion(file.GoVersion)
362-
if fileVersion.isValid() {
363-
// use the file version, if applicable
364-
// (file versions are either the empty string or of the form go1.dd)
365-
if pkgVersionOk {
366-
cmp := fileVersion.cmp(check.version)
367-
// Go 1.21 introduced the feature of setting the go.mod
368-
// go line to an early version of Go and allowing //go:build lines
369-
// to “upgrade” (cmp > 0) the Go version in a given file.
370-
// We can do that backwards compatibly.
371-
//
372-
// Go 1.21 also introduced the feature of allowing //go:build lines
373-
// to “downgrade” (cmp < 0) the Go version in a given file.
374-
// That can't be done compatibly in general, since before the
375-
// build lines were ignored and code got the module's Go version.
376-
// To work around this, downgrades are only allowed when the
377-
// module's Go version is Go 1.21 or later.
378-
//
379-
// If there is no valid check.version, then we don't really know what
380-
// Go version to apply.
381-
// Legacy tools may do this, and they historically have accepted everything.
382-
// Preserve that behavior by ignoring //go:build constraints entirely in that
383-
// case (!pkgVersionOk).
384-
if cmp > 0 || cmp < 0 && downgradeOk {
385-
v = file.GoVersion
386-
}
387-
}
360+
// If the file specifies a version, use max(fileVersion, go1.21).
361+
if fileVersion := asGoVersion(file.GoVersion); fileVersion.isValid() {
362+
// Go 1.21 introduced the feature of setting the go.mod
363+
// go line to an early version of Go and allowing //go:build lines
364+
// to set the Go version in a given file. Versions Go 1.21 and later
365+
// can be set backwards compatibly as that was the first version
366+
// files with go1.21 or later build tags could be built with.
367+
//
368+
// Set the version to max(fileVersion, go1.21): That will allow a
369+
// downgrade to a version before go1.22, where the for loop semantics
370+
// change was made, while being backwards compatible with versions of
371+
// go before the new //go:build semantics were introduced.
372+
v = string(versionMax(fileVersion, go1_21))
388373

389374
// Report a specific error for each tagged file that's too new.
390375
// (Normally the build system will have filtered files by version,
@@ -399,6 +384,13 @@ func (check *Checker) initFiles(files []*ast.File) {
399384
}
400385
}
401386

387+
func versionMax(a, b goVersion) goVersion {
388+
if a.cmp(b) < 0 {
389+
return b
390+
}
391+
return a
392+
}
393+
402394
// A bailout panic is used for early termination.
403395
type bailout struct{}
404396

src/internal/types/testdata/check/go1_20_19.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ type Slice []byte
1414
type Array [8]byte
1515

1616
var s Slice
17-
var p = (Array)(s /* ok because Go 1.20 ignored the //go:build go1.19 */)
17+
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)

src/internal/types/testdata/check/go1_21_19.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ type Slice []byte
1414
type Array [8]byte
1515

1616
var s Slice
17-
var p = (Array)(s /* ERROR "requires go1.20 or later" */)
17+
var p = (Array)(s /* ok because file versions below go1.21 set the langage version to go1.21 */)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// -lang=go1.21
2+
3+
// Copyright 2022 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Check Go language version-specific errors.
8+
9+
//go:build go1.22
10+
11+
package p
12+
13+
func f() {
14+
for _ = range /* ok because of upgrade to 1.22 */ 10 {
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// -lang=go1.22
2+
3+
// Copyright 2024 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Check Go language version-specific errors.
8+
9+
//go:build go1.21
10+
11+
package p
12+
13+
func f() {
14+
for _ = range 10 /* ERROR "requires go1.22 or later" */ {
15+
}
16+
}

src/internal/types/testdata/fixedbugs/issue66285.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
// -lang=go1.21
1+
// -lang=go1.13
22

33
// Copyright 2024 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
55
// license that can be found in the LICENSE file.
66

7-
// Note: Downgrading to go1.13 requires at least go1.21,
8-
// hence the need for -lang=go1.21 at the top.
9-
10-
//go:build go1.13
11-
127
package p
138

149
import "io"

test/fixedbugs/issue63489a.go

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1-
// errorcheck -lang=go1.21
1+
// errorcheck -lang=go1.22
22

33
// Copyright 2023 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
55
// license that can be found in the LICENSE file.
66

7-
//go:build go1.4
7+
// This file has been changed from its original version as
8+
// //go:build file versions below 1.21 set the language version to 1.21.
9+
// The original tested a -lang version of 1.21 with a file version of
10+
// go1.4 while this new version tests a -lang version of go1.22
11+
// with a file version of go1.21.
812

9-
package p
10-
11-
const c = 0o123 // ERROR "file declares //go:build go1.4"
13+
//go:build go1.21
1214

13-
// ERROR "file declares //go:build go1.4"
15+
package p
1416

15-
//line issue63489a.go:13:1
16-
const d = 0o124
17+
func f() {
18+
for _ = range 10 { // ERROR "file declares //go:build go1.21"
19+
}
20+
}

test/fixedbugs/issue63489b.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
// errorcheck -lang=go1.4
1+
// errorcheck -lang=go1.21
22

33
// Copyright 2023 The Go Authors. All rights reserved.
44
// Use of this source code is governed by a BSD-style
55
// license that can be found in the LICENSE file.
66

7-
//go:build go1.4
7+
// This file has been changed from its original version as
8+
// //go:build file versions below 1.21 set the language version to 1.21.
9+
// The original tested a -lang version of 1.4 with a file version of
10+
// go1.4 while this new version tests a -lang version of go1.1
11+
// with a file version of go1.21.
12+
13+
//go:build go1.21
814

915
package p
1016

11-
const c = 0o123 // ERROR "file declares //go:build go1.4"
17+
func f() {
18+
for _ = range 10 { // ERROR "file declares //go:build go1.21"
19+
}
20+
}

0 commit comments

Comments
 (0)