diff --git a/src/cmd/go/internal/gover/toolchain.go b/src/cmd/go/internal/gover/toolchain.go index a24df98168056b..a614bd7f06f680 100644 --- a/src/cmd/go/internal/gover/toolchain.go +++ b/src/cmd/go/internal/gover/toolchain.go @@ -45,6 +45,15 @@ func FromToolchain(name string) string { return v } +// ToolchainForGoVersion returns the implied toolchain name for the Go version v. +func ToolchainForGoVersion(v string) string { + name := "go" + v + if IsLang(v) && Compare(v, GoStrictVersion) >= 0 { + name += ".0" + } + return name +} + func maybeToolchainVersion(name string) string { if IsValid(name) { return name diff --git a/src/cmd/go/internal/gover/toolchain_test.go b/src/cmd/go/internal/gover/toolchain_test.go index d1c22fbc37cb9c..9baf378e3d4ffb 100644 --- a/src/cmd/go/internal/gover/toolchain_test.go +++ b/src/cmd/go/internal/gover/toolchain_test.go @@ -17,3 +17,16 @@ var fromToolchainTests = []testCase1[string, string]{ {"gccgo-go1.23rc4", ""}, {"gccgo-go1.23rc4-bigdwarf", ""}, } + +func TestToolchainForGoVersion(t *testing.T) { + test1(t, toolchainForGoVersionTests, "ToolchainForGoVersion", ToolchainForGoVersion) +} + +var toolchainForGoVersionTests = []testCase1[string, string]{ + {"1.20", "go1.20"}, + {"1.21", "go1.21.0"}, + {"1.22", "go1.22.0"}, + {"1.23", "go1.23.0"}, + {"1.23.4", "go1.23.4"}, + {"1.24rc1", "go1.24rc1"}, +} diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 8ae2b8bd22eaac..9ab6530794236b 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -918,8 +918,9 @@ func UpdateWorkGoVersion(wf *modfile.WorkFile, goVers string) (changed bool) { // This behavior seemed to cause user confusion without much // benefit so it was removed. See #65847. toolchain := wf.Toolchain.Name + impliedToolchain := gover.ToolchainForGoVersion(goVers) toolVers := gover.FromToolchain(toolchain) - if toolchain == "go"+goVers || gover.Compare(toolVers, goVers) < 0 || gover.Compare(toolVers, gover.GoStrictVersion) < 0 { + if toolchain == "go"+goVers || toolchain == impliedToolchain || gover.Compare(toolVers, goVers) < 0 || gover.Compare(toolVers, gover.GoStrictVersion) < 0 { wf.DropToolchainStmt() } @@ -2012,11 +2013,12 @@ func UpdateGoModFromReqs(ld *Loader, ctx context.Context, opts WriteOpts) (befor forceGoStmt(modFile, mainModule, gover.GoModToolVersion) } + impliedToolchain := gover.ToolchainForGoVersion(goVersion) if toolchain == "" { - toolchain = "go" + goVersion + toolchain = impliedToolchain } toolVers := gover.FromToolchain(toolchain) - if opts.DropToolchain || toolchain == "go"+goVersion || (gover.Compare(toolVers, gover.GoStrictVersion) < 0 && !opts.ExplicitToolchain) { + if opts.DropToolchain || toolchain == "go"+goVersion || toolchain == impliedToolchain || (gover.Compare(toolVers, gover.GoStrictVersion) < 0 && !opts.ExplicitToolchain) { // go get toolchain@none or toolchain matches go line or isn't valid; drop it. // TODO(#57001): 'go get' should reject explicit toolchains below GoStrictVersion. modFile.DropToolchainStmt() diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index abbbc8b4794231..3d2129bd7eccd3 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -682,7 +682,7 @@ func rawGoModSummary(ld *Loader, m module.Version) (*modFileSummary, error) { // Declare that go 1.21.3 requires toolchain 1.21.3, // so that go get knows that downgrading toolchain implies downgrading go // and similarly upgrading go requires upgrading the toolchain. - return &modFileSummary{module: m, require: []module.Version{{Path: "toolchain", Version: "go" + m.Version}}}, nil + return &modFileSummary{module: m, require: []module.Version{{Path: "toolchain", Version: gover.ToolchainForGoVersion(m.Version)}}}, nil } return &modFileSummary{module: m}, nil } diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go index f446f70f294b97..f78b8242aaacbc 100644 --- a/src/cmd/go/internal/toolchain/select.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -214,15 +214,12 @@ func Select() { } } if gover.Compare(goVers, minVers) > 0 { - gotoolchain = "go" + goVers - minVers = goVers // Starting with Go 1.21, the first released version has a .0 patch version suffix. // Don't try to download a language version (sans patch component), such as go1.22. // Instead, use the first toolchain of that language version, such as 1.22.0. // See golang.org/issue/62278. - if gover.IsLang(goVers) && gover.Compare(goVers, "1.21") >= 0 { - gotoolchain += ".0" - } + gotoolchain = gover.ToolchainForGoVersion(goVers) + minVers = goVers gover.Startup.AutoGoVersion = goVers gover.Startup.AutoToolchain = "" // in case we are overriding it for being too old if toolchainTrace { diff --git a/src/cmd/go/testdata/script/mod_get_toolchain.txt b/src/cmd/go/testdata/script/mod_get_toolchain.txt index 83cef4a0fd0c5e..43653bcb21ed88 100644 --- a/src/cmd/go/testdata/script/mod_get_toolchain.txt +++ b/src/cmd/go/testdata/script/mod_get_toolchain.txt @@ -105,6 +105,17 @@ grep 'toolchain go1.23.9' go.mod # Bug fixes. +# go get go@version should not write a language version as the toolchain when +# preserving the original go line's implied toolchain. +cp go.mod.oldlang go.mod +go get go@1.22.9 +stderr '^go: downgraded go 1.23 => 1.22.9$' +stderr '^go: upgraded toolchain go1.22.9 => go1.23.0$' +grep '^go 1.22.9$' go.mod +grep '^toolchain go1.23.0$' go.mod +! grep '^toolchain go1.23$' go.mod +cp go.mod.latest go.mod + # go get go@garbage should fail but not crash ! go get go@garbage ! stderr panic @@ -134,3 +145,17 @@ grep 'go 1.21$' go.mod module m go 1.21 + +-- go.mod.oldlang -- +module m + +go 1.23 + +toolchain go1.22.9 + +-- go.mod.latest -- +module m + +go 1.21 + +toolchain go1.23.9