diff options
author | Bryan C. Mills <bcmills@google.com> | 2021-01-28 09:10:57 -0500 |
---|---|---|
committer | Bryan C. Mills <bcmills@google.com> | 2021-02-18 21:09:46 +0000 |
commit | eb982727e33263c0bb67de607beb44c5e0bd2bea (patch) | |
tree | e052fc775be98c4661657747d3a778a1850ce574 /src/cmd/go/internal/mvs/mvs.go | |
parent | 3b7277d3651b5c5856c5b0879ba3fb7a5f279508 (diff) | |
download | go-eb982727e33263c0bb67de607beb44c5e0bd2bea.tar.gz go-eb982727e33263c0bb67de607beb44c5e0bd2bea.zip |
cmd/go/internal/mvs: fix Downgrade to match Algorithm 4
mvs.Downgrade is pretty clearly intended to match Algorithm 4 from the
MVS blog post (https://research.swtch.com/vgo-mvs#algorithm_4).
Per the blog post:
“Downgrading one module may require downgrading other modules, but we
want to downgrade as few other modules as possible. … To avoid an
unnecessary downgrade to E 1.1, we must also add a new requirement on
E 1.2. We can apply Algorithm R to find the minimal set of new
requirements to write to go.mod.”
mvs.Downgrade does not match that behavior today: it fails to retain
the selected versions of transitive dependencies that are not implied
by downgraded direct dependencies of the target (module E in the
post). This bug is currently masked by the fact that we only call
Downgrade today with a *modload.mvsReqs, for which the Required method
happens to return the complete build list — rather than only the
direct dependencies as documented for the mvs.Reqs interface.
For #36460
Change-Id: If9c8f413b156b5f67c02787d9359394e169951b0
Reviewed-on: https://go-review.googlesource.com/c/go/+/287633
Trust: Bryan C. Mills <bcmills@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Diffstat (limited to 'src/cmd/go/internal/mvs/mvs.go')
-rw-r--r-- | src/cmd/go/internal/mvs/mvs.go | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go index f016d8ff15..bed4d5c1ba 100644 --- a/src/cmd/go/internal/mvs/mvs.go +++ b/src/cmd/go/internal/mvs/mvs.go @@ -375,10 +375,19 @@ func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]mod // reqs.Previous, but the methods of reqs must otherwise handle such versions // correctly. func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) { - list, err := reqs.Required(target) + // Per https://research.swtch.com/vgo-mvs#algorithm_4: + // “To avoid an unnecessary downgrade to E 1.1, we must also add a new + // requirement on E 1.2. We can apply Algorithm R to find the minimal set of + // new requirements to write to go.mod.” + // + // In order to generate those new requirements, we need to identify versions + // for every module in the build list — not just reqs.Required(target). + list, err := BuildList(target, reqs) if err != nil { return nil, err } + list = list[1:] // remove target + max := make(map[string]string) for _, r := range list { max[r.Path] = r.Version @@ -411,6 +420,9 @@ func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([ } added[m] = true if v, ok := max[m.Path]; ok && reqs.Max(m.Version, v) != v { + // m would upgrade an existing dependency — it is not a strict downgrade, + // and because it was already present as a dependency, it could affect the + // behavior of other relevant packages. exclude(m) return } @@ -427,6 +439,7 @@ func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([ // is transient (we couldn't download go.mod), return the error from // Downgrade. Currently, we can't tell what kind of error it is. exclude(m) + return } for _, r := range list { add(r) @@ -438,8 +451,8 @@ func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([ } } - var out []module.Version - out = append(out, target) + downgraded := make([]module.Version, 0, len(list)+1) + downgraded = append(downgraded, target) List: for _, r := range list { add(r) @@ -466,10 +479,14 @@ List: add(p) r = p } - out = append(out, r) + downgraded = append(downgraded, r) } - return out, nil + return BuildList(target, &override{ + target: target, + list: downgraded, + Reqs: reqs, + }) } type override struct { |