diff --git a/doc/go1.17.html b/doc/go1.17.html index 02cd18d0375..22896c8c273 100644 --- a/doc/go1.17.html +++ b/doc/go1.17.html @@ -187,6 +187,13 @@ Do not send CLs removing the interior tags from such phrases. features.
+
+ The go
mod
graph
subcommand also
+ supports the -go
flag, which causes it to report the graph as
+ seen by the indicated Go version, showing dependencies that may otherwise be
+ pruned out by lazy loading.
+
diff --git a/doc/go_spec.html b/doc/go_spec.html index 561d44271a2..b59b37fd552 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -4670,7 +4670,7 @@ The following built-in functions are not permitted in statement context:
append cap complex imag len make new real -unsafe.Alignof unsafe.Offsetof unsafe.Sizeof +unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 9212c5776e1..2bf34b0a84d 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -3106,7 +3106,7 @@ func (s *state) expr(n ir.Node) *ssa.Value { arrlen := s.constInt(types.Types[types.TINT], n.Type().Elem().NumElem()) cap := s.newValue1(ssa.OpSliceLen, types.Types[types.TINT], v) s.boundsCheck(arrlen, cap, ssa.BoundsConvert, false) - return s.newValue1(ssa.OpSlicePtrUnchecked, types.Types[types.TINT], v) + return s.newValue1(ssa.OpSlicePtrUnchecked, n.Type(), v) case ir.OCALLFUNC: n := n.(*ir.CallExpr) diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 3febe880cdd..fd95da23eb0 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1186,13 +1186,17 @@ // // Usage: // -// go mod graph +// go mod graph [-go=version] // // Graph prints the module requirement graph (with replacements applied) // in text form. Each line in the output has two space-separated fields: a module // and one of its requirements. Each module is identified as a string of the form // path@version, except for the main module, which has no @version suffix. // +// The -go flag causes graph to report the module graph as loaded by by the +// given Go version, instead of the version indicated by the 'go' directive +// in the go.mod file. +// // See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'. // // @@ -1887,6 +1891,9 @@ // GOMIPS64 // For GOARCH=mips64{,le}, whether to use floating point instructions. // Valid values are hardfloat (default), softfloat. +// GOPPC64 +// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture). +// Valid values are power8 (default), power9. // GOWASM // For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. // Valid values are satconv, signext. diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index b47eb812b59..fc6989097ee 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -77,6 +77,14 @@ func defaultContext() build.Context { ctxt.GOOS = envOr("GOOS", ctxt.GOOS) ctxt.GOARCH = envOr("GOARCH", ctxt.GOARCH) + // The experiments flags are based on GOARCH, so they may + // need to change. TODO: This should be cleaned up. + buildcfg.UpdateExperiments(ctxt.GOARCH) + ctxt.ToolTags = nil + for _, exp := range buildcfg.EnabledExperiments() { + ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp) + } + // The go/build rule for whether cgo is enabled is: // 1. If $CGO_ENABLED is set, respect it. // 2. Otherwise, if this is a cross-compile, disable cgo. diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 9ec65018926..b552777e3e0 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -598,6 +598,9 @@ Architecture-specific environment variables: GOMIPS64 For GOARCH=mips64{,le}, whether to use floating point instructions. Valid values are hardfloat (default), softfloat. + GOPPC64 + For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture). + Valid values are power8 (default), power9. GOWASM For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. Valid values are satconv, signext. diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go index 6baa1db14f0..c8282965669 100644 --- a/src/cmd/go/internal/load/test.go +++ b/src/cmd/go/internal/load/test.go @@ -116,7 +116,7 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co // Can't change that code, because that code is only for loading the // non-test copy of a package. ptestErr = &PackageError{ - ImportStack: testImportStack(stk[0], p1, p.ImportPath), + ImportStack: importCycleStack(p1, p.ImportPath), Err: errors.New("import cycle not allowed in test"), IsImportCycle: true, } @@ -375,22 +375,44 @@ func TestPackagesAndErrors(ctx context.Context, opts PackageOpts, p *Package, co return pmain, ptest, pxtest } -func testImportStack(top string, p *Package, target string) []string { - stk := []string{top, p.ImportPath} -Search: - for p.ImportPath != target { - for _, p1 := range p.Internal.Imports { - if p1.ImportPath == target || str.Contains(p1.Deps, target) { - stk = append(stk, p1.ImportPath) - p = p1 - continue Search +// importCycleStack returns an import stack from p to the package whose import +// path is target. +func importCycleStack(p *Package, target string) []string { + // importerOf maps each import path to its importer nearest to p. + importerOf := map[string]string{p.ImportPath: ""} + + // q is a breadth-first queue of packages to search for target. + // Every package added to q has a corresponding entry in pathTo. + // + // We search breadth-first for two reasons: + // + // 1. We want to report the shortest cycle. + // + // 2. If p contains multiple cycles, the first cycle we encounter might not + // contain target. To ensure termination, we have to break all cycles + // other than the first. + q := []*Package{p} + + for len(q) > 0 { + p := q[0] + q = q[1:] + if path := p.ImportPath; path == target { + var stk []string + for path != "" { + stk = append(stk, path) + path = importerOf[path] + } + return stk + } + for _, dep := range p.Internal.Imports { + if _, ok := importerOf[dep.ImportPath]; !ok { + importerOf[dep.ImportPath] = p.ImportPath + q = append(q, dep) } } - // Can't happen, but in case it does... - stk = append(stk, "") - break } - return stk + + panic("lost path to cycle") } // recompileForTest copies and replaces certain packages in pmain's dependency diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go index 77853304e9d..903bd9970f1 100644 --- a/src/cmd/go/internal/modcmd/graph.go +++ b/src/cmd/go/internal/modcmd/graph.go @@ -18,7 +18,7 @@ import ( ) var cmdGraph = &base.Command{ - UsageLine: "go mod graph", + UsageLine: "go mod graph [-go=version]", Short: "print module requirement graph", Long: ` Graph prints the module requirement graph (with replacements applied) @@ -26,12 +26,21 @@ in text form. Each line in the output has two space-separated fields: a module and one of its requirements. Each module is identified as a string of the form path@version, except for the main module, which has no @version suffix. +The -go flag causes graph to report the module graph as loaded by by the +given Go version, instead of the version indicated by the 'go' directive +in the go.mod file. + See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'. `, Run: runGraph, } +var ( + graphGo goVersionFlag +) + func init() { + cmdGraph.Flag.Var(&graphGo, "go", "") base.AddModCommonFlags(&cmdGraph.Flag) } @@ -41,7 +50,7 @@ func runGraph(ctx context.Context, cmd *base.Command, args []string) { } modload.ForceUseModules = true modload.RootMode = modload.NeedRoot - mg := modload.LoadModGraph(ctx) + mg := modload.LoadModGraph(ctx, graphGo.String()) w := bufio.NewWriter(os.Stdout) defer w.Flush() diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go index 5c321c783ae..5a6eca32cfb 100644 --- a/src/cmd/go/internal/modcmd/verify.go +++ b/src/cmd/go/internal/modcmd/verify.go @@ -54,7 +54,8 @@ func runVerify(ctx context.Context, cmd *base.Command, args []string) { sem := make(chan token, runtime.GOMAXPROCS(0)) // Use a slice of result channels, so that the output is deterministic. - mods := modload.LoadModGraph(ctx).BuildList()[1:] + const defaultGoVersion = "" + mods := modload.LoadModGraph(ctx, defaultGoVersion).BuildList()[1:] errsChans := make([]<-chan []error, len(mods)) for i, mod := range mods { diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index ea5c4e229a5..9672e5598e0 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -506,7 +506,8 @@ type versionReason struct { func newResolver(ctx context.Context, queries []*query) *resolver { // LoadModGraph also sets modload.Target, which is needed by various resolver // methods. - mg := modload.LoadModGraph(ctx) + const defaultGoVersion = "" + mg := modload.LoadModGraph(ctx, defaultGoVersion) buildList := mg.BuildList() initialVersion := make(map[string]string, len(buildList)) @@ -1803,7 +1804,8 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi return false } - r.buildList = modload.LoadModGraph(ctx).BuildList() + const defaultGoVersion = "" + r.buildList = modload.LoadModGraph(ctx, defaultGoVersion).BuildList() r.buildListVersion = make(map[string]string, len(r.buildList)) for _, m := range r.buildList { r.buildListVersion[m.Path] = m.Version diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index 64eaa16e8b1..604a57b4373 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -403,11 +403,33 @@ func (mg *ModuleGraph) allRootsSelected() bool { // LoadModGraph loads and returns the graph of module dependencies of the main module, // without loading any packages. // +// If the goVersion string is non-empty, the returned graph is the graph +// as interpreted by the given Go version (instead of the version indicated +// in the go.mod file). +// // Modules are loaded automatically (and lazily) in LoadPackages: // LoadModGraph need only be called if LoadPackages is not, // typically in commands that care about modules but no particular package. -func LoadModGraph(ctx context.Context) *ModuleGraph { - rs, mg, err := expandGraph(ctx, LoadModFile(ctx)) +func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph { + rs := LoadModFile(ctx) + + if goVersion != "" { + depth := modDepthFromGoVersion(goVersion) + if depth == eager && rs.depth != eager { + // Use newRequirements instead of convertDepth because convertDepth + // also updates roots; here, we want to report the unmodified roots + // even though they may seem inconsistent. + rs = newRequirements(eager, rs.rootModules, rs.direct) + } + + mg, err := rs.Graph(ctx) + if err != nil { + base.Fatalf("go: %v", err) + } + return mg + } + + rs, mg, err := expandGraph(ctx, rs) if err != nil { base.Fatalf("go: %v", err) } diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go index 60bd26fb22f..d2bbe5cbe0b 100644 --- a/src/cmd/go/internal/modload/import.go +++ b/src/cmd/go/internal/modload/import.go @@ -428,6 +428,15 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver mv = module.ZeroPseudoVersion("v0") } } + mg, err := rs.Graph(ctx) + if err != nil { + return module.Version{}, err + } + if cmpVersion(mg.Selected(mp), mv) >= 0 { + // We can't resolve the import by adding mp@mv to the module graph, + // because the selected version of mp is already at least mv. + continue + } mods = append(mods, module.Version{Path: mp, Version: mv}) } diff --git a/src/cmd/go/testdata/script/env_cross_build.txt b/src/cmd/go/testdata/script/env_cross_build.txt new file mode 100644 index 00000000000..3feeba6b141 --- /dev/null +++ b/src/cmd/go/testdata/script/env_cross_build.txt @@ -0,0 +1,29 @@ +# Test that the corect default GOEXPERIMENT is used when cross +# building with GOENV (#46815). + +# Unset variables set by the TestScript harness. Users typically won't +# explicitly configure these, and #46815 doesn't repro if they are. +env GOOS= +env GOARCH= +env GOEXPERIMENT= + +env GOENV=windows-amd64 +go build internal/abi + +env GOENV=ios-arm64 +go build internal/abi + +env GOENV=linux-mips +go build internal/abi + +-- windows-amd64 -- +GOOS=windows +GOARCH=amd64 + +-- ios-arm64 -- +GOOS=ios +GOARCH=arm64 + +-- linux-mips -- +GOOS=linux +GOARCH=mips diff --git a/src/cmd/go/testdata/script/mod_graph_version.txt b/src/cmd/go/testdata/script/mod_graph_version.txt new file mode 100644 index 00000000000..f9a73f4617b --- /dev/null +++ b/src/cmd/go/testdata/script/mod_graph_version.txt @@ -0,0 +1,101 @@ +# For this module, Go 1.17 prunes out a (transitive and otherwise-irrelevant) +# requirement on a retracted higher version of a dependency. +# However, when Go 1.16 reads the same requirements from the go.mod file, +# it does not prune out that requirement, and selects the retracted version. +# +# The Go 1.16 module graph looks like: +# +# m ---- lazy v0.1.0 ---- requireincompatible v0.1.0 ---- incompatible v2.0.0+incompatible +# | | +# + -------+------------- incompatible v1.0.0 +# +# The Go 1.17 module graph is the same except that the dependencies of +# requireincompatible are pruned out (because the module that requires +# it — lazy v0.1.0 — specifies 'go 1.17', and it is not otherwise relevant to +# the main module). + +cp go.mod go.mod.orig + +go mod graph +cp stdout graph-1.17.txt +stdout '^example\.com/m example\.com/retract/incompatible@v1\.0\.0$' +stdout '^example\.net/lazy@v0\.1\.0 example\.com/retract/incompatible@v1\.0\.0$' +! stdout 'example\.com/retract/incompatible@v2\.0\.0\+incompatible' + +go mod graph -go=1.17 +cmp stdout graph-1.17.txt + +cmp go.mod go.mod.orig + + +# Setting -go=1.16 should report the graph as viewed by Go 1.16, +# but should not edit the go.mod file. + +go mod graph -go=1.16 +cp stdout graph-1.16.txt +stdout '^example\.com/m example\.com/retract/incompatible@v1\.0\.0$' +stdout '^example\.net/lazy@v0\.1\.0 example.com/retract/incompatible@v1\.0\.0$' +stdout '^example.net/requireincompatible@v0.1.0 example.com/retract/incompatible@v2\.0\.0\+incompatible$' + +cmp go.mod go.mod.orig + + +# If we actually update the go.mod file to the requested go version, +# we should get the same selected versions, but the roots of the graph +# may be updated. +# +# TODO(#45551): The roots should not be updated. + +go mod edit -go=1.16 +go mod graph +! stdout '^example\.com/m example\.com/retract/incompatible@v1\.0\.0$' +stdout '^example\.net/lazy@v0.1.0 example.com/retract/incompatible@v1\.0\.0$' +stdout '^example.net/requireincompatible@v0.1.0 example.com/retract/incompatible@v2\.0\.0\+incompatible$' + # TODO(#45551): cmp stdout graph-1.16.txt + + +# Unsupported go versions should be rejected, since we don't know +# what versions they would report. +! go mod graph -go=1.99999999999 +stderr '^invalid value "1\.99999999999" for flag -go: maximum supported Go version is '$goversion'\nusage: go mod graph \[-go=version\]\nRun ''go help mod graph'' for details.$' + + +-- go.mod -- +// Module m indirectly imports a package from +// example.com/retract/incompatible. Its selected version of +// that module is lower under Go 1.17 semantics than under Go 1.16. +module example.com/m + +go 1.17 + +replace ( + example.net/lazy v0.1.0 => ./lazy + example.net/requireincompatible v0.1.0 => ./requireincompatible +) + +require ( + example.com/retract/incompatible v1.0.0 // indirect + example.net/lazy v0.1.0 +) +-- lazy/go.mod -- +// Module lazy requires example.com/retract/incompatible v1.0.0. +// +// When viewed from the outside it also has a transitive dependency +// on v2.0.0+incompatible, but in lazy mode that transitive dependency +// is pruned out. +module example.net/lazy + +go 1.17 + +exclude example.com/retract/incompatible v2.0.0+incompatible + +require ( + example.com/retract/incompatible v1.0.0 + example.net/requireincompatible v0.1.0 +) +-- requireincompatible/go.mod -- +module example.net/requireincompatible + +go 1.15 + +require example.com/retract/incompatible v2.0.0+incompatible diff --git a/src/cmd/go/testdata/script/mod_list_test_cycle.txt b/src/cmd/go/testdata/script/mod_list_test_cycle.txt new file mode 100644 index 00000000000..755e50b0767 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_list_test_cycle.txt @@ -0,0 +1,23 @@ +# https://golang.org/issue/45863: a typo in a test package leading to an +# import cycle should be diagnosed, instead of causing an infinite loop. +# The failure mode of this test prior to the fix was a timeout or OOM crash. + +go list -e -test -deps ./datastore/sql + +-- go.mod -- +module golang.org/issue45863 + +go 1.17 +-- datastore/datastore_health.go -- +package datastore + +import ( + "golang.org/issue45863/datastore" + "golang.org/issue45863/datastore/sql" +) +-- datastore/sql/sql.go -- +package sql +-- datastore/sql/sql_test.go -- +package sql + +import _ "golang.org/issue45863/datastore" diff --git a/src/cmd/go/testdata/script/mod_tidy_replace_old.txt b/src/cmd/go/testdata/script/mod_tidy_replace_old.txt new file mode 100644 index 00000000000..cfd3792600c --- /dev/null +++ b/src/cmd/go/testdata/script/mod_tidy_replace_old.txt @@ -0,0 +1,34 @@ +# Regression test for https://golang.org/issue/46659. +# +# If a 'replace' directive specifies an older-than-selected version of a module, +# 'go mod tidy' shouldn't try to add that version to the build list to resolve a +# missing package: it won't be selected, and would cause the module loader to +# loop indefinitely trying to resolve the package. + +cp go.mod go.mod.orig + +! go mod tidy +! stderr panic +stderr '^golang\.org/issue46659 imports\n\texample\.com/missingpkg/deprecated: package example\.com/missingpkg/deprecated provided by example\.com/missingpkg at latest version v1\.0\.0 but not at required version v1\.0\.1-beta$' + +go mod tidy -e + +cmp go.mod go.mod.orig + +-- go.mod -- +module golang.org/issue46659 + +go 1.17 + +replace example.com/missingpkg v1.0.1-alpha => example.com/missingpkg v1.0.0 + +require example.com/usemissingpre v1.0.0 + +require example.com/missingpkg v1.0.1-beta // indirect +-- m.go -- +package m + +import ( + _ "example.com/missingpkg/deprecated" + _ "example.com/usemissingpre" +) diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index 7723250468e..56c3b2585c7 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -5,6 +5,7 @@ package moddeps_test import ( + "bytes" "encoding/json" "fmt" "internal/testenv" @@ -68,7 +69,7 @@ func TestAllDependencies(t *testing.T) { // There is no vendor directory, so the module must have no dependencies. // Check that the list of active modules contains only the main module. - cmd := exec.Command(goBin, "list", "-mod=mod", "-m", "all") + cmd := exec.Command(goBin, "list", "-mod=readonly", "-m", "all") cmd.Env = append(os.Environ(), "GO111MODULE=on") cmd.Dir = m.Dir cmd.Stderr = new(strings.Builder) @@ -123,10 +124,38 @@ func TestAllDependencies(t *testing.T) { t.Skip("skipping because a diff command with support for --recursive and --unified flags is unavailable") } + // We're going to check the standard modules for tidiness, so we need a usable + // GOMODCACHE. If the default directory doesn't exist, use a temporary + // directory instead. (That can occur, for example, when running under + // run.bash with GO_TEST_SHORT=0: run.bash sets GOPATH=/nonexist-gopath, and + // GO_TEST_SHORT=0 causes it to run this portion of the test.) + var modcacheEnv []string + { + out, err := exec.Command(goBin, "env", "GOMODCACHE").Output() + if err != nil { + t.Fatalf("%s env GOMODCACHE: %v", goBin, err) + } + modcacheOk := false + if gomodcache := string(bytes.TrimSpace(out)); gomodcache != "" { + if _, err := os.Stat(gomodcache); err == nil { + modcacheOk = true + } + } + if !modcacheOk { + modcacheEnv = []string{ + "GOMODCACHE=" + t.TempDir(), + "GOFLAGS=" + os.Getenv("GOFLAGS") + " -modcacherw", // Allow t.TempDir() to clean up subdirectories. + } + } + } + // Build the bundle binary at the golang.org/x/tools // module version specified in GOROOT/src/cmd/go.mod. bundleDir := t.TempDir() - r := runner{Dir: filepath.Join(runtime.GOROOT(), "src/cmd")} + r := runner{ + Dir: filepath.Join(runtime.GOROOT(), "src/cmd"), + Env: append(os.Environ(), modcacheEnv...), + } r.run(t, goBin, "build", "-mod=readonly", "-o", bundleDir, "golang.org/x/tools/cmd/bundle") var gorootCopyDir string @@ -160,7 +189,7 @@ func TestAllDependencies(t *testing.T) { } r := runner{ Dir: filepath.Join(gorootCopyDir, rel), - Env: append(os.Environ(), + Env: append(append(os.Environ(), modcacheEnv...), // Set GOROOT. "GOROOT="+gorootCopyDir, // Explicitly override PWD and clear GOROOT_FINAL so that GOROOT=gorootCopyDir is definitely used. diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 7d1cb9b85a5..f771dee4a97 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -2841,7 +2841,6 @@ func TestTxStmtDeadlock(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() tx, err := db.BeginTx(ctx, nil) - cancel() if err != nil { t.Fatal(err) } @@ -2850,6 +2849,7 @@ func TestTxStmtDeadlock(t *testing.T) { if err != nil { t.Fatal(err) } + cancel() // Run number of stmt queries to reproduce deadlock from context cancel for i := 0; i < 1e3; i++ { // Encounter any close related errors (e.g. ErrTxDone, stmt is closed) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 869d14c2c13..c0a3cc66fed 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1379,7 +1379,12 @@ func (p *parser) parseIndexOrSliceOrInstance(x ast.Expr) ast.Expr { p.errorExpected(p.pos, "operand") rbrack := p.pos p.next() - return &ast.BadExpr{From: x.Pos(), To: rbrack} + return &ast.IndexExpr{ + X: x, + Lbrack: lbrack, + Index: &ast.BadExpr{From: rbrack, To: rbrack}, + Rbrack: rbrack, + } } p.exprLev++ diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go index f8bcf4d8642..ca4b5264cfe 100644 --- a/src/go/scanner/scanner.go +++ b/src/go/scanner/scanner.go @@ -373,7 +373,7 @@ func (s *Scanner) scanIdentifier() string { continue } s.rdOffset += rdOffset - if b < utf8.RuneSelf { + if 0 < b && b < utf8.RuneSelf { // Optimization: we've encountered an ASCII character that's not a letter // or number. Avoid the call into s.next() and corresponding set up. // diff --git a/src/go/scanner/scanner_test.go b/src/go/scanner/scanner_test.go index dd3c7cf8385..de45e166060 100644 --- a/src/go/scanner/scanner_test.go +++ b/src/go/scanner/scanner_test.go @@ -813,6 +813,8 @@ var errors = []struct { {"//\ufeff", token.COMMENT, 2, "//\ufeff", "illegal byte order mark"}, // only first BOM is ignored {"'\ufeff" + `'`, token.CHAR, 1, "'\ufeff" + `'`, "illegal byte order mark"}, // only first BOM is ignored {`"` + "abc\ufeffdef" + `"`, token.STRING, 4, `"` + "abc\ufeffdef" + `"`, "illegal byte order mark"}, // only first BOM is ignored + {"abc\x00def", token.IDENT, 3, "abc", "illegal character NUL"}, + {"abc\x00", token.IDENT, 3, "abc", "illegal character NUL"}, } func TestScanErrors(t *testing.T) { diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 0926ac74319..a5720f992e9 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -202,17 +202,20 @@ func asGoVersion(s string) string { return "" } -func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, manual bool) { +func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, manual bool, imp Importer) { if len(filenames) == 0 { t.Fatal("no source files") } + if strings.HasSuffix(filenames[0], ".go2") && !typeparams.Enabled { + t.Skip("type params are not enabled") + } + if strings.HasSuffix(filenames[0], ".go1") && typeparams.Enabled { + t.Skip("type params are enabled") + } + mode := parser.AllErrors - if strings.HasSuffix(filenames[0], ".go2") { - if !typeparams.Enabled { - t.Skip("type params are not enabled") - } - } else { + if !strings.HasSuffix(filenames[0], ".go2") { mode |= typeparams.DisallowParsing } @@ -250,7 +253,10 @@ func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, man } } - conf.Importer = importer.Default() + conf.Importer = imp + if imp == nil { + conf.Importer = importer.Default() + } conf.Error = func(err error) { if *haltOnError { defer panic(err) @@ -339,7 +345,7 @@ func TestManual(t *testing.T) { func TestLongConstants(t *testing.T) { format := "package longconst\n\nconst _ = %s\nconst _ = %s // ERROR excessively long constant" src := fmt.Sprintf(format, strings.Repeat("1", 9999), strings.Repeat("1", 10001)) - testFiles(t, nil, []string{"longconst.go"}, [][]byte{[]byte(src)}, false) + testFiles(t, nil, []string{"longconst.go"}, [][]byte{[]byte(src)}, false, nil) } // TestIndexRepresentability tests that constant index operands must @@ -347,7 +353,7 @@ func TestLongConstants(t *testing.T) { // represent larger values. func TestIndexRepresentability(t *testing.T) { const src = "package index\n\nvar s []byte\nvar _ = s[int64 /* ERROR \"int64\\(1\\) << 40 \\(.*\\) overflows int\" */ (1) << 40]" - testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false) + testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false, nil) } func TestIssue46453(t *testing.T) { @@ -355,7 +361,7 @@ func TestIssue46453(t *testing.T) { t.Skip("type params are enabled") } const src = "package p\ntype _ comparable // ERROR \"undeclared name: comparable\"" - testFiles(t, nil, []string{"issue46453.go"}, [][]byte{[]byte(src)}, false) + testFiles(t, nil, []string{"issue46453.go"}, [][]byte{[]byte(src)}, false, nil) } func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) } @@ -415,5 +421,5 @@ func testPkg(t *testing.T, filenames []string, manual bool) { } srcs[i] = src } - testFiles(t, nil, filenames, srcs, manual) + testFiles(t, nil, filenames, srcs, manual, nil) } diff --git a/src/go/types/errors.go b/src/go/types/errors.go index 19e9ae8d44a..22631064173 100644 --- a/src/go/types/errors.go +++ b/src/go/types/errors.go @@ -31,7 +31,7 @@ func (check *Checker) qualifier(pkg *Package) string { if check.pkgPathMap == nil { check.pkgPathMap = make(map[string]map[string]bool) check.seenPkgMap = make(map[*Package]bool) - check.markImports(pkg) + check.markImports(check.pkg) } // If the same package name was used by multiple packages, display the full path. if len(check.pkgPathMap[pkg.name]) > 1 { diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 44926919efe..51995af30af 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -577,42 +577,64 @@ func TestIssue44515(t *testing.T) { } func TestIssue43124(t *testing.T) { - // TODO(rFindley) enhance the testdata tests to be able to express this type - // of setup. + // TODO(rFindley) move this to testdata by enhancing support for importing. // All involved packages have the same name (template). Error messages should // disambiguate between text/template and html/template by printing the full // path. const ( asrc = `package a; import "text/template"; func F(template.Template) {}; func G(int) {}` - bsrc = `package b; import ("a"; "html/template"); func _() { a.F(template.Template{}) }` - csrc = `package c; import ("a"; "html/template"); func _() { a.G(template.Template{}) }` + bsrc = ` +package b + +import ( + "a" + "html/template" +) + +func _() { + // Packages should be fully qualified when there is ambiguity within the + // error string itself. + a.F(template /* ERROR cannot use.*html/template.* as .*text/template */ .Template{}) +} +` + csrc = ` +package c + +import ( + "a" + "fmt" + "html/template" +) + +// Issue #46905: make sure template is not the first package qualified. +var _ fmt.Stringer = 1 // ERROR cannot use 1.*as fmt\.Stringer + +// Packages should be fully qualified when there is ambiguity in reachable +// packages. In this case both a (and for that matter html/template) import +// text/template. +func _() { a.G(template /* ERROR cannot use .*html/template.*Template */ .Template{}) } +` + + tsrc = ` +package template + +import "text/template" + +type T int + +// Verify that the current package name also causes disambiguation. +var _ T = template /* ERROR cannot use.*text/template.* as T value */.Template{} +` ) a, err := pkgFor("a", asrc, nil) if err != nil { t.Fatalf("package a failed to typecheck: %v", err) } - conf := Config{Importer: importHelper{pkg: a, fallback: importer.Default()}} + imp := importHelper{pkg: a, fallback: importer.Default()} - // Packages should be fully qualified when there is ambiguity within the - // error string itself. - bast := mustParse(t, bsrc) - _, err = conf.Check(bast.Name.Name, fset, []*ast.File{bast}, nil) - if err == nil { - t.Fatal("package b had no errors") - } - if !strings.Contains(err.Error(), "text/template") || !strings.Contains(err.Error(), "html/template") { - t.Errorf("type checking error for b does not disambiguate package template: %q", err) - } - - // ...and also when there is any ambiguity in reachable packages. - cast := mustParse(t, csrc) - _, err = conf.Check(cast.Name.Name, fset, []*ast.File{cast}, nil) - if err == nil { - t.Fatal("package c had no errors") - } - if !strings.Contains(err.Error(), "html/template") { - t.Errorf("type checking error for c does not disambiguate package template: %q", err) - } + testFiles(t, nil, []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp) + testFiles(t, nil, []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp) + testFiles(t, nil, []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp) } diff --git a/src/go/types/testdata/check/issues.src b/src/go/types/testdata/check/issues.src index 9d9fc7862f1..b0231548a7c 100644 --- a/src/go/types/testdata/check/issues.src +++ b/src/go/types/testdata/check/issues.src @@ -332,7 +332,7 @@ func issue28281g() (... /* ERROR can only use ... with final parameter */ TT) func issue26234a(f *syn.File) { // The error message below should refer to the actual package name (syntax) // not the local package name (syn). - f.foo /* ERROR f.foo undefined \(type \*syntax.File has no field or method foo\) */ + f.foo /* ERROR f\.foo undefined \(type \*syntax\.File has no field or method foo\) */ } type T struct { @@ -361,7 +361,7 @@ func issue35895() { // Because both t1 and t2 have the same global package name (template), // qualify packages with full path name in this case. - var _ t1.Template = t2 /* ERROR cannot use .* \(value of type "html/template".Template\) as "text/template".Template */ .Template{} + var _ t1.Template = t2 /* ERROR cannot use .* \(value of type .html/template.\.Template\) as .text/template.\.Template */ .Template{} } func issue42989(s uint) { diff --git a/src/go/types/testdata/examples/functions.go2 b/src/go/types/testdata/examples/functions.go2 index f15c709ce44..81b9d3456c8 100644 --- a/src/go/types/testdata/examples/functions.go2 +++ b/src/go/types/testdata/examples/functions.go2 @@ -210,5 +210,5 @@ func _() { func h[] /* ERROR empty type parameter list */ () func _() { - h[] /* ERROR operand */ () + h /* ERROR cannot index */ [] /* ERROR operand */ () } diff --git a/src/go/types/testdata/fixedbugs/issue46403.src b/src/go/types/testdata/fixedbugs/issue46403.src new file mode 100644 index 00000000000..9d475222adb --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue46403.src @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue46403 + +func _() { + // a should be used, despite the parser error below. + var a []int + var _ = a[] // ERROR expected operand +} diff --git a/src/go/types/testdata/fixedbugs/issue46404.go1 b/src/go/types/testdata/fixedbugs/issue46404.go1 new file mode 100644 index 00000000000..db604bc1ac3 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue46404.go1 @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue46404 + +// Check that we don't type check t[_] as an instantiation. +type t [t /* ERROR not a type */ [_]]_ // ERROR cannot use diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 97df908ae93..e6be7b72e46 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -262,8 +262,12 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { } case *ast.IndexExpr: - exprs := typeparams.UnpackExpr(e.Index) - return check.instantiatedType(e.X, exprs, def) + if typeparams.Enabled { + exprs := typeparams.UnpackExpr(e.Index) + return check.instantiatedType(e.X, exprs, def) + } + check.errorf(e0, _NotAType, "%s is not a type", e0) + check.use(e.X) case *ast.ParenExpr: // Generic types must be instantiated before they can be used in any form. diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index e78f9879991..e87b6221f1e 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -18,7 +18,7 @@ import ( // // (This is not necessarily the set of experiments the compiler itself // was built with.) -var Experiment goexperiment.Flags = parseExperiments() +var Experiment goexperiment.Flags = parseExperiments(GOARCH) var regabiSupported = GOARCH == "amd64" || GOARCH == "arm64" var regabiDeveloping = false @@ -41,7 +41,7 @@ var experimentBaseline = goexperiment.Flags{ // Note: must agree with runtime.framepointer_enabled. var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64" -func parseExperiments() goexperiment.Flags { +func parseExperiments(goarch string) goexperiment.Flags { // Start with the statically enabled set of experiments. flags := experimentBaseline @@ -96,11 +96,11 @@ func parseExperiments() goexperiment.Flags { } // regabiwrappers is always enabled on amd64. - if GOARCH == "amd64" { + if goarch == "amd64" { flags.RegabiWrappers = true } // regabi is only supported on amd64 and arm64. - if GOARCH != "amd64" && GOARCH != "arm64" { + if goarch != "amd64" && goarch != "arm64" { flags.RegabiWrappers = false flags.RegabiReflect = false flags.RegabiArgs = false @@ -161,3 +161,10 @@ func EnabledExperiments() []string { func AllExperiments() []string { return expList(&Experiment, nil, true) } + +// UpdateExperiments updates the Experiment global based on a new GOARCH value. +// This is only required for cmd/go, which can change GOARCH after +// program startup due to use of "go env -w". +func UpdateExperiments(goarch string) { + Experiment = parseExperiments(goarch) +} diff --git a/src/net/dial_test.go b/src/net/dial_test.go index f899da10cf8..723038c7a22 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -155,40 +155,27 @@ func slowDialTCP(ctx context.Context, network string, laddr, raddr *TCPAddr) (*T return c, err } -func dialClosedPort(t *testing.T) (actual, expected time.Duration) { - // Estimate the expected time for this platform. - // On Windows, dialing a closed port takes roughly 1 second, - // but other platforms should be instantaneous. - if runtime.GOOS == "windows" { - expected = 1500 * time.Millisecond - } else if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { - expected = 150 * time.Millisecond - } else { - expected = 95 * time.Millisecond - } +func dialClosedPort(t *testing.T) (dialLatency time.Duration) { + // On most platforms, dialing a closed port should be nearly instantaneous — + // less than a few hundred milliseconds. However, on some platforms it may be + // much slower: on Windows and OpenBSD, it has been observed to take up to a + // few seconds. l, err := Listen("tcp", "127.0.0.1:0") if err != nil { - t.Logf("dialClosedPort: Listen failed: %v", err) - return 999 * time.Hour, expected + t.Fatalf("dialClosedPort: Listen failed: %v", err) } addr := l.Addr().String() l.Close() - // On OpenBSD, interference from TestTCPSelfConnect is mysteriously - // causing the first attempt to hang for a few seconds, so we throw - // away the first result and keep the second. - for i := 1; ; i++ { - startTime := time.Now() - c, err := Dial("tcp", addr) - if err == nil { - c.Close() - } - elapsed := time.Now().Sub(startTime) - if i == 2 { - t.Logf("dialClosedPort: measured delay %v", elapsed) - return elapsed, expected - } + + startTime := time.Now() + c, err := Dial("tcp", addr) + if err == nil { + c.Close() } + elapsed := time.Now().Sub(startTime) + t.Logf("dialClosedPort: measured delay %v", elapsed) + return elapsed } func TestDialParallel(t *testing.T) { @@ -198,10 +185,7 @@ func TestDialParallel(t *testing.T) { t.Skip("both IPv4 and IPv6 are required") } - closedPortDelay, expectClosedPortDelay := dialClosedPort(t) - if closedPortDelay > expectClosedPortDelay { - t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay) - } + closedPortDelay := dialClosedPort(t) const instant time.Duration = 0 const fallbackDelay = 200 * time.Millisecond @@ -675,10 +659,7 @@ func TestDialerDualStack(t *testing.T) { t.Skip("both IPv4 and IPv6 are required") } - closedPortDelay, expectClosedPortDelay := dialClosedPort(t) - if closedPortDelay > expectClosedPortDelay { - t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay) - } + closedPortDelay := dialClosedPort(t) origTestHookLookupIP := testHookLookupIP defer func() { testHookLookupIP = origTestHookLookupIP }() diff --git a/src/net/lookup_windows_test.go b/src/net/lookup_windows_test.go index 62b61ed6c2f..aa95501d023 100644 --- a/src/net/lookup_windows_test.go +++ b/src/net/lookup_windows_test.go @@ -299,7 +299,7 @@ func lookupPTR(name string) (ptr []string, err error) { ptr = make([]string, 0, 10) rx := regexp.MustCompile(`(?m)^Pinging\s+([a-zA-Z0-9.\-]+)\s+\[.*$`) for _, ans := range rx.FindAllStringSubmatch(r, -1) { - ptr = append(ptr, ans[1]+".") + ptr = append(ptr, absDomainName([]byte(ans[1]))) } return } diff --git a/src/os/fifo_test.go b/src/os/fifo_test.go index 9b262f8205a..007ed291293 100644 --- a/src/os/fifo_test.go +++ b/src/os/fifo_test.go @@ -26,9 +26,6 @@ func TestFifoEOF(t *testing.T) { switch runtime.GOOS { case "android": t.Skip("skipping on Android; mkfifo syscall not available") - case "openbsd": - // On OpenBSD 6.2 this test just hangs for some reason. - t.Skip("skipping on OpenBSD; issue 25877") } dir := t.TempDir() diff --git a/src/reflect/value.go b/src/reflect/value.go index 4341fd3f901..7589966e222 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -1377,10 +1377,16 @@ func valueInterface(v Value, safe bool) interface{} { return packEface(v) } -// InterfaceData returns the interface v's value as a uintptr pair. +// InterfaceData returns a pair of unspecified uintptr values. // It panics if v's Kind is not Interface. +// +// In earlier versions of Go, this function returned the interface's +// value as a uintptr pair. As of Go 1.4, the implementation of +// interface values precludes any defined use of InterfaceData. +// +// Deprecated: The memory representation of interface values is not +// compatible with InterfaceData. func (v Value) InterfaceData() [2]uintptr { - // TODO: deprecate this v.mustBe(Interface) // We treat this as a read operation, so we allow // it even for unexported data, because the caller diff --git a/src/runtime/internal/atomic/atomic_386.s b/src/runtime/internal/atomic/atomic_386.s index 37318e0ad76..724d5152313 100644 --- a/src/runtime/internal/atomic/atomic_386.s +++ b/src/runtime/internal/atomic/atomic_386.s @@ -65,7 +65,7 @@ TEXT ·Xaddint64(SB), NOSPLIT, $0-20 // bool ·Cas64(uint64 *val, uint64 old, uint64 new) // Atomically: -// if(*val == *old){ +// if(*val == old){ // *val = new; // return 1; // } else { diff --git a/src/time/format.go b/src/time/format.go index 6040ed5aebc..bb173a21c2d 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -751,8 +751,11 @@ type ParseError struct { // These are borrowed from unicode/utf8 and strconv and replicate behavior in // that package, since we can't take a dependency on either. -const runeSelf = 0x80 -const lowerhex = "0123456789abcdef" +const ( + lowerhex = "0123456789abcdef" + runeSelf = 0x80 + runeError = '\uFFFD' +) func quote(s string) string { buf := make([]byte, 1, len(s)+2) // slice will be at least len(s) + quotes @@ -765,7 +768,16 @@ func quote(s string) string { // reproduce strconv.Quote's behavior with full fidelity but // given how rarely we expect to hit these edge cases, speed and // conciseness are better. - for j := 0; j < len(string(c)) && j < len(s); j++ { + var width int + if c == runeError { + width = 1 + if i+2 < len(s) && s[i:i+3] == string(runeError) { + width = 3 + } + } else { + width = len(string(c)) + } + for j := 0; j < width; j++ { buf = append(buf, `\x`...) buf = append(buf, lowerhex[s[i+j]>>4]) buf = append(buf, lowerhex[s[i+j]&0xF]) diff --git a/src/time/time_test.go b/src/time/time_test.go index f272bbd5587..cea5f2d3f5a 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -917,6 +917,11 @@ var parseDurationErrorTests = []struct { {".s", `".s"`}, {"+.s", `"+.s"`}, {"1d", `"1d"`}, + {"\x85\x85", `"\x85\x85"`}, + {"\xffff", `"\xffff"`}, + {"hello \xffff world", `"hello \xffff world"`}, + {"\uFFFD", `"\xef\xbf\xbd"`}, // utf8.RuneError + {"\uFFFD hello \uFFFD world", `"\xef\xbf\xbd hello \xef\xbf\xbd world"`}, // utf8.RuneError // overflow {"9223372036854775810ns", `"9223372036854775810ns"`}, {"9223372036854775808ns", `"9223372036854775808ns"`}, diff --git a/test/fixedbugs/issue46907.go b/test/fixedbugs/issue46907.go new file mode 100644 index 00000000000..bd82f4f2b10 --- /dev/null +++ b/test/fixedbugs/issue46907.go @@ -0,0 +1,11 @@ +// compile + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func f(b []byte) []byte { + return (*[32]byte)(b[:32])[:] +}