Skip to content

Commit 3b0d288

Browse files
author
Bryan C. Mills
committed
cmd/go: assume Go 1.16 instead of Go 1.11 for dependencies that lack explicit 'go' directives
Fixes #45109 Updates #44976 Updates #36876 Change-Id: Icb00f8b6e0d4e076d82da1697e7058b9e7603916 Reviewed-on: https://go-review.googlesource.com/c/go/+/303229 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 1c59066 commit 3b0d288

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

doc/go1.17.html

+12
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ <h4 id="missing-go-directive"><code>go.mod</code> files missing <code>go</code>
6767
Go 1.12</a>.)
6868
</p>
6969

70+
<p><!-- golang.org/issue/44976 -->
71+
If a module dependency lacks an explicit <code>go.mod</code> file, or
72+
its <code>go.mod</code> file does not contain
73+
a <a href="/doc/modules/gomod-ref#go"><code>go</code> directive</a>,
74+
the <code>go</code> command now assumes <code>go 1.16</code> for that
75+
dependency instead of the current release. (Dependencies developed in GOPATH
76+
mode may lack a <code>go.mod</code> file, and
77+
the <code>vendor/modules.txt</code> has to date never recorded
78+
the <code>go</code> versions indicated by dependencies' <code>go.mod</code>
79+
files.)
80+
</p>
81+
7082
<h2 id="runtime">Runtime</h2>
7183

7284
<p>

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

+35-7
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,38 @@ func LoadModFile(ctx context.Context) {
405405
readVendorList()
406406
checkVendorConsistency()
407407
}
408-
if index.goVersionV == "" && cfg.BuildMod == "mod" {
409-
addGoStmt()
410-
WriteGoMod()
408+
if index.goVersionV == "" {
409+
// The main module necessarily has a go.mod file, and that file lacks a
410+
// 'go' directive. The 'go' command has been adding that directive
411+
// automatically since Go 1.12, so this module either dates to Go 1.11 or
412+
// has been erroneously hand-edited.
413+
//
414+
// If we are able to modify the go.mod file, we will add a 'go' directive
415+
// to at least make the situation explicit going forward.
416+
if cfg.BuildMod == "mod" {
417+
// TODO(#44976): If we implicitly upgrade to the latest Go version once
418+
// lazy loading is implemented, we could accidentally prune out
419+
// dependencies from what was formerly a Go 1.11 module, resulting in
420+
// downgrades (if only lower requirements on that module remain) and/or
421+
// upgrades (if no requirement remains and we end up re-resolving to
422+
// latest).
423+
//
424+
// We should probably instead load the dependencies using Go 1.11
425+
// semantics to ensure that we capture everything that is relevant, or
426+
// perhaps error out and let the user tell us which version they intend.
427+
//
428+
// If we are running 'go mod tidy' in particular, we will have enough
429+
// information to upgrade the 'go' version after loading is complete.
430+
addGoStmt(latestGoVersion())
431+
WriteGoMod()
432+
} else {
433+
// Reproducibility requires that if we change the semantics of a module,
434+
// we write some explicit change to its go.mod file. We cannot write to
435+
// the go.mod file (because we are in readonly or vendor mode), so we must
436+
// not change its semantics either. The go.mod file looks as if it were
437+
// created by Go 1.11, so assume Go 1.11 semantics.
438+
rawGoVersion.Store(Target, "1.11")
439+
}
411440
}
412441
}
413442

@@ -442,7 +471,7 @@ func CreateModFile(ctx context.Context, modPath string) {
442471
modFile = new(modfile.File)
443472
modFile.AddModuleStmt(modPath)
444473
initTarget(modFile.Module.Mod)
445-
addGoStmt() // Add the go directive before converted module requirements.
474+
addGoStmt(latestGoVersion()) // Add the go directive before converted module requirements.
446475

447476
convertedFrom, err := convertLegacyConfig(modPath)
448477
if convertedFrom != "" {
@@ -680,19 +709,18 @@ func convertLegacyConfig(modPath string) (from string, err error) {
680709
// addGoStmt adds a go directive to the go.mod file if it does not already
681710
// include one. The 'go' version added, if any, is the latest version supported
682711
// by this toolchain.
683-
func addGoStmt() {
712+
func addGoStmt(v string) {
684713
if modFile.Go != nil && modFile.Go.Version != "" {
685714
return
686715
}
687-
v := latestGoVersion()
688716
if err := modFile.AddGoStmt(v); err != nil {
689717
base.Fatalf("go: internal error: %v", err)
690718
}
691719
rawGoVersion.Store(Target, v)
692720
}
693721

694722
// latestGoVersion returns the latest version of the Go language supported by
695-
// this toolchain.
723+
// this toolchain, like "1.17".
696724
func latestGoVersion() string {
697725
tags := build.Default.ReleaseTags
698726
version := tags[len(tags)-1]

src/cmd/go/internal/work/gc.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,15 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
7070
// as of Go 1.12, so any module that still lacks such a directive must
7171
// either have been authored before then, or have a hand-edited go.mod
7272
// file that hasn't been updated by cmd/go since that edit.
73-
v = "1.11"
73+
//
74+
// Unfortunately, through at least Go 1.16 we didn't add versions to
75+
// vendor/modules.txt. So this could also be a vendored 1.16 dependency.
76+
//
77+
// Fortunately, there were no breaking changes to the language between Go
78+
// 1.11 and 1.16, so if we assume Go 1.16 semantics we will not introduce
79+
// any spurious errors — we will only mask errors, and not particularly
80+
// important ones at that.
81+
v = "1.16"
7482
}
7583
if allowedVersion(v) {
7684
gcargs = append(gcargs, "-lang=go"+v)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Regression test for https://golang.org/issue/45109:
2+
# Dependencies that use post-1.11 Go features should build
3+
# when compiled as vendored dependencies of Go 1.16 modules.
4+
5+
[short] skip
6+
7+
go mod init example.com/foo
8+
go mod edit -replace=example.com/[email protected]=./use113
9+
10+
go mod vendor
11+
! grep 1.13 vendor/modules.txt # TODO(#36876): record dependency versions.
12+
go build .
13+
14+
15+
# In Go 1.16 and earlier, 'go mod vendor' did not record dependency versions.
16+
# That still should not cause a build failure.
17+
18+
go mod edit -go=1.16
19+
go mod vendor
20+
! grep 1.13 vendor/modules.txt
21+
go build .
22+
23+
24+
-- foo.go --
25+
package foo
26+
27+
import _ "example.com/use113"
28+
29+
-- use113/go.mod --
30+
module example.com/use113
31+
32+
go 1.13
33+
-- use113/use113.go --
34+
package use113
35+
36+
const x = 1_000

0 commit comments

Comments
 (0)