cmd/go: inject State parameter into toolchain.Select

This command modifies the call tree starting at `toolchain.Select` to
inject a `State` parameter to every function that is currently using
the global `modload.LoaderState` variable.  By explicilty passing a
`State` parameter, we can begin to eliminate the usage of the global
`modload.LoaderState`.

This commit is part of the overall effort to eliminate global
modloader state.

[git-generate]
cd src/cmd/go/internal/toolchain
rf '
  inject modload.LoaderState Select
  add select.go var moduleLoaderState *modload.State
  ex {
    import "cmd/go/internal/modload";
    modload.LoaderState -> moduleLoaderState
  }
  add Select://+0 moduleLoaderState := modload.NewState()
  rm select.go:/var moduleLoaderState \*modload.State/
'
cd ..
./rf-cleanup.zsh

Change-Id: I759439a47e2b1aaa01a0a800bc18596dd7ce4983
Reviewed-on: https://go-review.googlesource.com/c/go/+/709988
Reviewed-by: Michael Matloob <matloob@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Ian Alexander 2025-10-02 21:45:53 -04:00
parent 4dc3dd9a86
commit e7c66a58d5
9 changed files with 30 additions and 29 deletions

View file

@ -158,7 +158,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
// TODO(#64008): call base.Fatalf instead of toolchain.SwitchOrFatal
// here, since we can only reach this point with an outdated toolchain
// if the go.mod file is inconsistent.
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
for _, m := range modFile.Require {

View file

@ -62,7 +62,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) {
goVersion := graphGo.String()
if goVersion != "" && gover.Compare(gover.Local(), goVersion) < 0 {
toolchain.SwitchOrFatal(ctx, &gover.TooNewError{
toolchain.SwitchOrFatal(modload.LoaderState, ctx, &gover.TooNewError{
What: "-go flag",
GoVersion: goVersion,
})

View file

@ -124,7 +124,7 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
goVersion := tidyGo.String()
if goVersion != "" && gover.Compare(gover.Local(), goVersion) < 0 {
toolchain.SwitchOrFatal(ctx, &gover.TooNewError{
toolchain.SwitchOrFatal(modload.LoaderState, ctx, &gover.TooNewError{
What: "-go flag",
GoVersion: goVersion,
})

View file

@ -418,7 +418,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
// but the command line is not.
// TODO(bcmills): modload.EditBuildList should catch this instead,
// and then this can be changed to base.Fatal(err).
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
newReqs := reqsFromGoMod(modload.ModFile())
@ -558,7 +558,7 @@ func newResolver(ctx context.Context, queries []*query) *resolver {
// methods.
mg, err := modload.LoadModGraph(ctx, "")
if err != nil {
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
buildList := mg.BuildList()
@ -1619,7 +1619,7 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
// are old enough but the go command itself is not new
// enough. See the related comment on the SwitchOrFatal
// in runGet when WriteGoMod returns an error.
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
}
@ -1989,7 +1989,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
changed, err := modload.EditBuildList(modload.LoaderState, ctx, additions, resolved)
if err != nil {
if errors.Is(err, gover.ErrTooNew) {
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
constraint, ok := errors.AsType[*modload.ConstraintError](err)
@ -2035,7 +2035,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
mg, err := modload.LoadModGraph(ctx, "")
if err != nil {
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
r.buildList = mg.BuildList()

View file

@ -570,12 +570,12 @@ func Init(loaderstate *State) {
// of 'go get', but Init reads the -modfile flag in 'go get', so it shouldn't
// be called until the command is installed and flags are parsed. Instead of
// calling Init and Enabled, the main package can call this function.
func WillBeEnabled() bool {
if LoaderState.modRoots != nil || cfg.ModulesEnabled {
func WillBeEnabled(loaderstate *State) bool {
if loaderstate.modRoots != nil || cfg.ModulesEnabled {
// Already enabled.
return true
}
if LoaderState.initialized {
if loaderstate.initialized {
// Initialized, not enabled.
return false
}

View file

@ -22,7 +22,7 @@ func Increment() {
// incrementConfig increments counters for the configuration
// the command is running in.
func incrementConfig() {
if !modload.WillBeEnabled() {
if !modload.WillBeEnabled(modload.LoaderState) {
counter.Inc("go/mode:gopath")
} else if workfile := modload.FindGoWork(modload.LoaderState, base.Cwd()); workfile != "" {
counter.Inc("go/mode:workspace")

View file

@ -95,10 +95,11 @@ var toolchainTrace = godebug.New("#toolchaintrace").Value() == "1"
// It must be called early in startup.
// See https://go.dev/doc/toolchain#select.
func Select() {
moduleLoaderState := modload.NewState()
log.SetPrefix("go: ")
defer log.SetPrefix("")
if !modload.WillBeEnabled() {
if !modload.WillBeEnabled(moduleLoaderState) {
return
}
@ -171,7 +172,7 @@ func Select() {
gotoolchain = minToolchain
if mode == "auto" || mode == "path" {
// Read go.mod to find new minimum and suggested toolchain.
file, goVers, toolchain := modGoToolchain()
file, goVers, toolchain := modGoToolchain(moduleLoaderState)
gover.Startup.AutoFile = file
if toolchain == "default" {
// "default" means always use the default toolchain,
@ -231,7 +232,7 @@ func Select() {
}
}
}
maybeSwitchForGoInstallVersion(minVers)
maybeSwitchForGoInstallVersion(moduleLoaderState, minVers)
}
// If we are invoked as a target toolchain, confirm that
@ -283,7 +284,7 @@ func Select() {
}
counterSelectExec.Inc()
Exec(modload.LoaderState, gotoolchain)
Exec(moduleLoaderState, gotoolchain)
}
var counterSelectExec = counter.New("go/toolchain/select-exec")
@ -527,9 +528,9 @@ func raceSafeCopy(old, new string) error {
// modGoToolchain finds the enclosing go.work or go.mod file
// and returns the go version and toolchain lines from the file.
// The toolchain line overrides the version line
func modGoToolchain() (file, goVers, toolchain string) {
func modGoToolchain(loaderstate *modload.State) (file, goVers, toolchain string) {
wd := base.UncachedCwd()
file = modload.FindGoWork(modload.LoaderState, wd)
file = modload.FindGoWork(loaderstate, wd)
// $GOWORK can be set to a file that does not yet exist, if we are running 'go work init'.
// Do not try to load the file in that case
if _, err := os.Stat(file); err != nil {
@ -551,7 +552,7 @@ func modGoToolchain() (file, goVers, toolchain string) {
// maybeSwitchForGoInstallVersion reports whether the command line is go install m@v or go run m@v.
// If so, switch to the go version required to build m@v if it's higher than minVers.
func maybeSwitchForGoInstallVersion(minVers string) {
func maybeSwitchForGoInstallVersion(loaderstate *modload.State, minVers string) {
// Note: We assume there are no flags between 'go' and 'install' or 'run'.
// During testing there are some debugging flags that are accepted
// in that position, but in production go binaries there are not.
@ -692,10 +693,10 @@ func maybeSwitchForGoInstallVersion(minVers string) {
// command lines if we add new flags in the future.
// Set up modules without an explicit go.mod, to download go.mod.
modload.LoaderState.ForceUseModules = true
modload.LoaderState.RootMode = modload.NoRoot
modload.Init(modload.LoaderState)
defer modload.LoaderState.Reset()
loaderstate.ForceUseModules = true
loaderstate.RootMode = modload.NoRoot
modload.Init(loaderstate)
defer loaderstate.Reset()
// See internal/load.PackagesAndErrorsOutsideModule
ctx := context.Background()
@ -705,14 +706,14 @@ func maybeSwitchForGoInstallVersion(minVers string) {
allowed = nil
}
noneSelected := func(path string) (version string) { return "none" }
_, err := modload.QueryPackages(modload.LoaderState, ctx, path, version, noneSelected, allowed)
_, err := modload.QueryPackages(loaderstate, ctx, path, version, noneSelected, allowed)
if errors.Is(err, gover.ErrTooNew) {
// Run early switch, same one go install or go run would eventually do,
// if it understood all the command-line flags.
s := NewSwitcher(modload.LoaderState)
s := NewSwitcher(loaderstate)
s.Error(err)
if s.TooNew != nil && gover.Compare(s.TooNew.GoVersion, minVers) > 0 {
SwitchOrFatal(ctx, err)
SwitchOrFatal(loaderstate, ctx, err)
}
}
}

View file

@ -116,8 +116,8 @@ var counterSwitchExec = counter.New("go/toolchain/switch-exec")
// SwitchOrFatal attempts a toolchain switch based on the information in err
// and otherwise falls back to base.Fatal(err).
func SwitchOrFatal(ctx context.Context, err error) {
s := NewSwitcher(modload.LoaderState)
func SwitchOrFatal(loaderstate *modload.State, ctx context.Context, err error) {
s := NewSwitcher(loaderstate)
s.Error(err)
s.Switch(ctx)
base.Exit()

View file

@ -56,7 +56,7 @@ func runSync(ctx context.Context, cmd *base.Command, args []string) {
_, err := modload.LoadModGraph(ctx, "")
if err != nil {
toolchain.SwitchOrFatal(ctx, err)
toolchain.SwitchOrFatal(modload.LoaderState, ctx, err)
}
mustSelectFor := map[module.Version][]module.Version{}