Skip to content

Commit 61120c6

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modload: use "pruned" instead of "lazy" to describe pruned module graphs
The level of support for pruning — not the lazy/eager loading behavior — is the more fundamental property, and what matters in terms of what invariants we need to maintain. If the main module supports pruned module graphs we load its dependencies lazily, and if it does not support pruned module graphs we load its dependencies eagerly. However, in principle we could also load the module graph lazily even in modules that do not support graph pruning — we would just be more likely to overlook inconsistent requirements introduced by hand-edits or bad VCS merges to the go.mod file. (After this change, a “lazy” module is just one in which we happen not to have loaded the module graph, and an “eager” one is one in which we happen to load the module graph more aggressively.) Updates #36460 For #47397 Change-Id: I0d2ffd21acc913f72ff56b59a6bdc539ebc3d377 Reviewed-on: https://go-review.googlesource.com/c/go/+/345393 Trust: Bryan C. Mills <[email protected]> Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent af9009a commit 61120c6

File tree

8 files changed

+257
-230
lines changed

8 files changed

+257
-230
lines changed

src/cmd/go/internal/modload/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
8080
v string
8181
ok bool
8282
)
83-
if rs.depth == lazy {
83+
if rs.pruning == pruned {
8484
v, ok = rs.rootSelected(path)
8585
}
8686
if !ok {

src/cmd/go/internal/modload/buildlist.go

Lines changed: 112 additions & 105 deletions
Large diffs are not rendered by default.

src/cmd/go/internal/modload/edit.go

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
// 2. Each module version in tryUpgrade is upgraded toward the indicated
2222
// version as far as can be done without violating (1).
2323
//
24-
// 3. Each module version in rs.rootModules (or rs.graph, if rs.depth is eager)
24+
// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned)
2525
// is downgraded from its original version only to the extent needed to
2626
// satisfy (1), or upgraded only to the extent needed to satisfy (1) and
2727
// (2).
@@ -69,10 +69,11 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
6969
}
7070

7171
var roots []module.Version
72-
if rs.depth == eager {
73-
// In an eager module, modules that provide packages imported by the main
74-
// module may either be explicit roots or implicit transitive dependencies.
75-
// We promote the modules in mustSelect to be explicit requirements.
72+
if rs.pruning == unpruned {
73+
// In a module without graph pruning, modules that provide packages imported
74+
// by the main module may either be explicit roots or implicit transitive
75+
// dependencies. We promote the modules in mustSelect to be explicit
76+
// requirements.
7677
var rootPaths []string
7778
for _, m := range mustSelect {
7879
if !MainModules.Contains(m.Path) && m.Version != "none" {
@@ -102,8 +103,8 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
102103
return nil, false, err
103104
}
104105
} else {
105-
// In a lazy module, every module that provides a package imported by the
106-
// main module must be retained as a root.
106+
// In a module with a pruned graph, every module that provides a package
107+
// imported by the main module must be retained as a root.
107108
roots = mods
108109
if !changed {
109110
// Because the roots we just computed are unchanged, the entire graph must
@@ -126,7 +127,7 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
126127
direct[m.Path] = true
127128
}
128129
}
129-
return newRequirements(rs.depth, roots, direct), changed, nil
130+
return newRequirements(rs.pruning, roots, direct), changed, nil
130131
}
131132

132133
// limiterForEdit returns a versionLimiter with its max versions set such that
@@ -149,11 +150,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
149150
}
150151
}
151152

152-
if rs.depth == eager {
153-
// Eager go.mod files don't indicate which transitive dependencies are
154-
// actually relevant to the main module, so we have to assume that any module
155-
// that could have provided any package — that is, any module whose selected
156-
// version was not "none" — may be relevant.
153+
if rs.pruning == unpruned {
154+
// go.mod files that do not support graph pruning don't indicate which
155+
// transitive dependencies are actually relevant to the main module, so we
156+
// have to assume that any module that could have provided any package —
157+
// that is, any module whose selected version was not "none" — may be
158+
// relevant.
157159
for _, m := range mg.BuildList() {
158160
restrictTo(m)
159161
}
@@ -175,7 +177,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
175177
}
176178
}
177179

178-
if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.depth, tryUpgrade, mustSelect); err != nil {
180+
if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.pruning, tryUpgrade, mustSelect); err != nil {
179181
return nil, err
180182
}
181183

@@ -185,7 +187,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
185187
restrictTo(m)
186188
}
187189

188-
return newVersionLimiter(rs.depth, maxVersion), nil
190+
return newVersionLimiter(rs.pruning, maxVersion), nil
189191
}
190192

191193
// raiseLimitsForUpgrades increases the module versions in maxVersions to the
@@ -195,12 +197,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
195197
//
196198
// Versions not present in maxVersion are unrestricted, and it is assumed that
197199
// they will not be promoted to root requirements (and thus will not contribute
198-
// their own dependencies if the main module is lazy).
200+
// their own dependencies if the main module supports graph pruning).
199201
//
200202
// These limits provide an upper bound on how far a module may be upgraded as
201203
// part of an incidental downgrade, if downgrades are needed in order to select
202204
// the versions in mustSelect.
203-
func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, depth modDepth, tryUpgrade []module.Version, mustSelect []module.Version) error {
205+
func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, pruning modPruning, tryUpgrade []module.Version, mustSelect []module.Version) error {
204206
// allow raises the limit for m.Path to at least m.Version.
205207
// If m.Path was already unrestricted, it remains unrestricted.
206208
allow := func(m module.Version) {
@@ -213,9 +215,9 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
213215
}
214216
}
215217

216-
var eagerUpgrades []module.Version
217-
if depth == eager {
218-
eagerUpgrades = tryUpgrade
218+
var unprunedUpgrades []module.Version
219+
if pruning == unpruned {
220+
unprunedUpgrades = tryUpgrade
219221
} else {
220222
for _, m := range tryUpgrade {
221223
if MainModules.Contains(m.Path) {
@@ -229,11 +231,11 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
229231
if err != nil {
230232
return err
231233
}
232-
if summary.depth == eager {
233-
// For efficiency, we'll load all of the eager upgrades as one big
234+
if summary.pruning == unpruned {
235+
// For efficiency, we'll load all of the unpruned upgrades as one big
234236
// graph, rather than loading the (potentially-overlapping) subgraph for
235237
// each upgrade individually.
236-
eagerUpgrades = append(eagerUpgrades, m)
238+
unprunedUpgrades = append(unprunedUpgrades, m)
237239
continue
238240
}
239241

@@ -244,14 +246,14 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
244246
}
245247
}
246248

247-
if len(eagerUpgrades) > 0 {
248-
// Compute the max versions for eager upgrades all together.
249-
// Since these modules are eager, we'll end up scanning all of their
249+
if len(unprunedUpgrades) > 0 {
250+
// Compute the max versions for unpruned upgrades all together.
251+
// Since these modules are unpruned, we'll end up scanning all of their
250252
// transitive dependencies no matter which versions end up selected,
251253
// and since we have a large dependency graph to scan we might get
252254
// a significant benefit from not revisiting dependencies that are at
253255
// common versions among multiple upgrades.
254-
upgradeGraph, err := readModGraph(ctx, eager, eagerUpgrades)
256+
upgradeGraph, err := readModGraph(ctx, unpruned, unprunedUpgrades)
255257
if err != nil {
256258
// Compute the requirement path from a module path in tryUpgrade to the
257259
// error, and the requirement path (if any) from rs.rootModules to the
@@ -268,7 +270,7 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
268270
}
269271

270272
if len(mustSelect) > 0 {
271-
mustGraph, err := readModGraph(ctx, depth, mustSelect)
273+
mustGraph, err := readModGraph(ctx, pruning, mustSelect)
272274
if err != nil {
273275
return err
274276
}
@@ -300,7 +302,7 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
300302
}
301303

302304
var initial []module.Version
303-
if rs.depth == eager {
305+
if rs.pruning == unpruned {
304306
mg, err := rs.Graph(ctx)
305307
if err != nil {
306308
return nil, false, err
@@ -327,7 +329,7 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
327329
// downgraded module may require a higher (but still allowed) version of
328330
// another. The lower version may require extraneous dependencies that aren't
329331
// actually relevant, so we need to compute the actual selected versions.
330-
mg, err := readModGraph(ctx, rs.depth, mods)
332+
mg, err := readModGraph(ctx, rs.pruning, mods)
331333
if err != nil {
332334
return nil, false, err
333335
}
@@ -349,16 +351,16 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
349351
// A versionLimiter tracks the versions that may be selected for each module
350352
// subject to constraints on the maximum versions of transitive dependencies.
351353
type versionLimiter struct {
352-
// depth is the depth at which the dependencies of the modules passed to
354+
// pruning is the pruning at which the dependencies of the modules passed to
353355
// Select and UpgradeToward are loaded.
354-
depth modDepth
356+
pruning modPruning
355357

356358
// max maps each module path to the maximum version that may be selected for
357359
// that path.
358360
//
359361
// Paths with no entry are unrestricted, and we assume that they will not be
360362
// promoted to root dependencies (so will not contribute dependencies if the
361-
// main module is lazy).
363+
// main module supports graph pruning).
362364
max map[string]string
363365

364366
// selected maps each module path to a version of that path (if known) whose
@@ -410,16 +412,16 @@ func (dq dqState) isDisqualified() bool {
410412
// in the map are unrestricted. The limiter assumes that unrestricted paths will
411413
// not be promoted to root dependencies.
412414
//
413-
// If depth is lazy, then if a module passed to UpgradeToward or Select is
414-
// itself lazy, its unrestricted dependencies are skipped when scanning
415-
// requirements.
416-
func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter {
415+
// If module graph pruning is in effect, then if a module passed to
416+
// UpgradeToward or Select supports pruning, its unrestricted dependencies are
417+
// skipped when scanning requirements.
418+
func newVersionLimiter(pruning modPruning, max map[string]string) *versionLimiter {
417419
selected := make(map[string]string)
418420
for _, m := range MainModules.Versions() {
419421
selected[m.Path] = m.Version
420422
}
421423
return &versionLimiter{
422-
depth: depth,
424+
pruning: pruning,
423425
max: max,
424426
selected: selected,
425427
dqReason: map[module.Version]dqState{},
@@ -430,8 +432,8 @@ func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter {
430432
// UpgradeToward attempts to upgrade the selected version of m.Path as close as
431433
// possible to m.Version without violating l's maximum version limits.
432434
//
433-
// If depth is lazy and m itself is lazy, the the dependencies of unrestricted
434-
// dependencies of m will not be followed.
435+
// If module graph pruning is in effect and m itself supports pruning, the
436+
// dependencies of unrestricted dependencies of m will not be followed.
435437
func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) error {
436438
selected, ok := l.selected[m.Path]
437439
if ok {
@@ -443,7 +445,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
443445
selected = "none"
444446
}
445447

446-
if l.check(m, l.depth).isDisqualified() {
448+
if l.check(m, l.pruning).isDisqualified() {
447449
candidates, err := versions(ctx, m.Path, CheckAllowed)
448450
if err != nil {
449451
// This is likely a transient error reaching the repository,
@@ -460,7 +462,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
460462
})
461463
candidates = candidates[:i]
462464

463-
for l.check(m, l.depth).isDisqualified() {
465+
for l.check(m, l.pruning).isDisqualified() {
464466
n := len(candidates)
465467
if n == 0 || cmpVersion(selected, candidates[n-1]) >= 0 {
466468
// We couldn't find a suitable candidate above the already-selected version.
@@ -477,7 +479,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
477479

478480
// Select attempts to set the selected version of m.Path to exactly m.Version.
479481
func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err error) {
480-
dq := l.check(m, l.depth)
482+
dq := l.check(m, l.pruning)
481483
if !dq.isDisqualified() {
482484
l.selected[m.Path] = m.Version
483485
}
@@ -487,14 +489,14 @@ func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err
487489
// check determines whether m (or its transitive dependencies) would violate l's
488490
// maximum version limits if added to the module requirement graph.
489491
//
490-
// If depth is lazy and m itself is lazy, then the dependencies of unrestricted
491-
// dependencies of m will not be followed. If the lazy loading invariants hold
492-
// for the main module up to this point, the packages in those modules are at
493-
// best only imported by tests of dependencies that are themselves loaded from
494-
// outside modules. Although we would like to keep 'go test all' as reproducible
495-
// as is feasible, we don't want to retain test dependencies that are only
496-
// marginally relevant at best.
497-
func (l *versionLimiter) check(m module.Version, depth modDepth) dqState {
492+
// If pruning is in effect and m itself supports graph pruning, the dependencies
493+
// of unrestricted dependencies of m will not be followed. If the graph-pruning
494+
// invariants hold for the main module up to this point, the packages in those
495+
// modules are at best only imported by tests of dependencies that are
496+
// themselves loaded from outside modules. Although we would like to keep
497+
// 'go test all' as reproducible as is feasible, we don't want to retain test
498+
// dependencies that are only marginally relevant at best.
499+
func (l *versionLimiter) check(m module.Version, pruning modPruning) dqState {
498500
if m.Version == "none" || m == MainModules.mustGetSingleMainModule() {
499501
// version "none" has no requirements, and the dependencies of Target are
500502
// tautological.
@@ -525,20 +527,20 @@ func (l *versionLimiter) check(m module.Version, depth modDepth) dqState {
525527
return l.disqualify(m, dqState{err: err})
526528
}
527529

528-
if summary.depth == eager {
529-
depth = eager
530+
if summary.pruning == unpruned {
531+
pruning = unpruned
530532
}
531533
for _, r := range summary.require {
532-
if depth == lazy {
534+
if pruning == pruned {
533535
if _, restricted := l.max[r.Path]; !restricted {
534536
// r.Path is unrestricted, so we don't care at what version it is
535537
// selected. We assume that r.Path will not become a root dependency, so
536-
// since m is lazy, r's dependencies won't be followed.
538+
// since m supports pruning, r's dependencies won't be followed.
537539
continue
538540
}
539541
}
540542

541-
if dq := l.check(r, depth); dq.isDisqualified() {
543+
if dq := l.check(r, pruning); dq.isDisqualified() {
542544
return l.disqualify(m, dq)
543545
}
544546

src/cmd/go/internal/modload/import_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestQueryImport(t *testing.T) {
6969
RootMode = NoRoot
7070

7171
ctx := context.Background()
72-
rs := newRequirements(eager, nil, nil)
72+
rs := newRequirements(unpruned, nil, nil)
7373

7474
for _, tt := range importTests {
7575
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {

src/cmd/go/internal/modload/init.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
625625
MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "")
626626
goVersion := LatestGoVersion()
627627
rawGoVersion.Store(mainModule, goVersion)
628-
requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
628+
requirements = newRequirements(pruningForGoVersion(goVersion), nil, nil)
629629
return requirements, false
630630
}
631631

@@ -712,11 +712,11 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
712712

713713
// We need to add a 'go' version to the go.mod file, but we must assume
714714
// that its existing contents match something between Go 1.11 and 1.16.
715-
// Go 1.11 through 1.16 have eager requirements, but the latest Go
716-
// version uses lazy requirements instead — so we need to convert the
717-
// requirements to be lazy.
715+
// Go 1.11 through 1.16 do not support graph pruning, but the latest Go
716+
// version uses a pruned module graph — so we need to convert the
717+
// requirements to support pruning.
718718
var err error
719-
rs, err = convertDepth(ctx, rs, lazy)
719+
rs, err = convertPruning(ctx, rs, pruned)
720720
if err != nil {
721721
base.Fatalf("go: %v", err)
722722
}
@@ -978,7 +978,7 @@ func requirementsFromModFiles(ctx context.Context, modFiles []*modfile.File) *Re
978978
}
979979
}
980980
module.Sort(roots)
981-
rs := newRequirements(modDepthFromGoVersion(MainModules.GoVersion()), roots, direct)
981+
rs := newRequirements(pruningForGoVersion(MainModules.GoVersion()), roots, direct)
982982
return rs
983983
}
984984

@@ -1485,12 +1485,13 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
14851485
continue
14861486
}
14871487

1488-
if rs.depth == lazy && pkg.mod.Path != "" {
1488+
if rs.pruning == pruned && pkg.mod.Path != "" {
14891489
if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
1490-
// pkg was loaded from a root module, and because the main module is
1491-
// lazy we do not check non-root modules for conflicts for packages
1492-
// that can be found in roots. So we only need the checksums for the
1493-
// root modules that may contain pkg, not all possible modules.
1490+
// pkg was loaded from a root module, and because the main module has
1491+
// a pruned module graph we do not check non-root modules for
1492+
// conflicts for packages that can be found in roots. So we only need
1493+
// the checksums for the root modules that may contain pkg, not all
1494+
// possible modules.
14941495
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
14951496
if v, ok := rs.rootSelected(prefix); ok && v != "none" {
14961497
m := module.Version{Path: prefix, Version: v}
@@ -1514,8 +1515,7 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
15141515
}
15151516

15161517
if rs.graph.Load() == nil {
1517-
// The module graph was not loaded, possibly because the main module is lazy
1518-
// or possibly because we haven't needed to load the graph yet.
1518+
// We haven't needed to load the module graph so far.
15191519
// Save sums for the root modules (or their replacements), but don't
15201520
// incur the cost of loading the graph just to find and retain the sums.
15211521
for _, m := range rs.rootModules {

src/cmd/go/internal/modload/list.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List
105105
path := arg[:i]
106106
vers := arg[i+1:]
107107
if vers == "upgrade" || vers == "patch" {
108-
if _, ok := rs.rootSelected(path); !ok || rs.depth == eager {
108+
if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned {
109109
needFullGraph = true
110110
if !HasModRoot() {
111111
base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
@@ -114,7 +114,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List
114114
}
115115
continue
116116
}
117-
if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager {
117+
if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned {
118118
needFullGraph = true
119119
if mode&ListVersions == 0 && !HasModRoot() {
120120
base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)

0 commit comments

Comments
 (0)