diff options
author | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-08-12 20:22:27 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-08-12 20:22:27 +0000 |
commit | 4d3cc84774549d26e52cbba3a0ffc50d3ede80d5 (patch) | |
tree | de3466e3b835e2c18fa03548ddf0619f5555575b /src | |
parent | 7e9f911ec4fd08ce9b4296f0aea4864b53064573 (diff) | |
parent | a64ab8d3ecb38e10007e136edc9dc3abde873e1e (diff) | |
download | go-git-dev.typeparams.tar.gz |
Merge "[dev.typeparams] all: merge master (46fd547) into dev.typeparams" into dev.typeparamsdev.typeparams
Diffstat (limited to 'src')
46 files changed, 541 insertions, 227 deletions
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 1abb03bcc5..bec17696f3 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1263,14 +1263,19 @@ func cmdbootstrap() { timelog("start", "dist bootstrap") defer timelog("end", "dist bootstrap") - var noBanner bool + var noBanner, noClean bool var debug bool flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all") flag.BoolVar(&debug, "d", debug, "enable debugging of bootstrap process") flag.BoolVar(&noBanner, "no-banner", noBanner, "do not print banner") + flag.BoolVar(&noClean, "no-clean", noClean, "print deprecation warning") xflagparse(0) + if noClean { + xprintf("warning: --no-clean is deprecated and has no effect; use 'go install std cmd' instead\n") + } + // Set GOPATH to an internal directory. We shouldn't actually // need to store files here, since the toolchain won't // depend on modules outside of vendor directories, but if diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 954caae9fb..7f88d3216c 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -1078,7 +1078,7 @@ // // Usage: // -// go mod edit [editing flags] [go.mod] +// go mod edit [editing flags] [-fmt|-print|-json] [go.mod] // // Edit provides a command-line interface for editing go.mod, // for use primarily by tools or scripts. It reads only go.mod; @@ -1204,7 +1204,7 @@ // // Usage: // -// go mod init [module] +// go mod init [module-path] // // Init initializes and writes a new go.mod file in the current directory, in // effect creating a new module rooted at the current directory. The go.mod file diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go index e856e7c630..bb3d521092 100644 --- a/src/cmd/go/internal/modcmd/edit.go +++ b/src/cmd/go/internal/modcmd/edit.go @@ -25,7 +25,7 @@ import ( ) var cmdEdit = &base.Command{ - UsageLine: "go mod edit [editing flags] [go.mod]", + UsageLine: "go mod edit [editing flags] [-fmt|-print|-json] [go.mod]", Short: "edit go.mod from tools or scripts", Long: ` Edit provides a command-line interface for editing go.mod, diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go index 73cc282d81..958c3066ac 100644 --- a/src/cmd/go/internal/modcmd/init.go +++ b/src/cmd/go/internal/modcmd/init.go @@ -13,7 +13,7 @@ import ( ) var cmdInit = &base.Command{ - UsageLine: "go mod init [module]", + UsageLine: "go mod init [module-path]", Short: "initialize new module in current directory", Long: ` Init initializes and writes a new go.mod file in the current directory, in diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index f817a04583..dfef9f73c2 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -864,22 +864,25 @@ func (r *codeRepo) GoMod(version string) (data []byte, err error) { data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod) if err != nil { if os.IsNotExist(err) { - return r.legacyGoMod(rev, dir), nil + return LegacyGoMod(r.modPath), nil } return nil, err } return data, nil } -func (r *codeRepo) legacyGoMod(rev, dir string) []byte { - // We used to try to build a go.mod reflecting pre-existing - // package management metadata files, but the conversion - // was inherently imperfect (because those files don't have - // exactly the same semantics as go.mod) and, when done - // for dependencies in the middle of a build, impossible to - // correct. So we stopped. - // Return a fake go.mod that simply declares the module path. - return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(r.modPath))) +// LegacyGoMod generates a fake go.mod file for a module that doesn't have one. +// The go.mod file contains a module directive and nothing else: no go version, +// no requirements. +// +// We used to try to build a go.mod reflecting pre-existing +// package management metadata files, but the conversion +// was inherently imperfect (because those files don't have +// exactly the same semantics as go.mod) and, when done +// for dependencies in the middle of a build, impossible to +// correct. So we stopped. +func LegacyGoMod(modPath string) []byte { + return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(modPath))) } func (r *codeRepo) modPrefix(rev string) string { diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go index 604a57b437..bf69567316 100644 --- a/src/cmd/go/internal/modload/buildlist.go +++ b/src/cmd/go/internal/modload/buildlist.go @@ -191,6 +191,19 @@ func (rs *Requirements) rootSelected(path string) (version string, ok bool) { return "", false } +// hasRedundantRoot returns true if the root list contains multiple requirements +// of the same module or a requirement on any version of the main module. +// Redundant requirements should be pruned, but they may influence version +// selection. +func (rs *Requirements) hasRedundantRoot() bool { + for i, m := range rs.rootModules { + if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) { + return true + } + } + return false +} + // Graph returns the graph of module requirements loaded from the current // root modules (as reported by RootModules). // @@ -882,6 +895,12 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen // and (trivially) version. if !rootsUpgraded { + if cfg.BuildMod != "mod" { + // The only changes to the root set (if any) were to remove duplicates. + // The requirements are consistent (if perhaps redundant), so keep the + // original rs to preserve its ModuleGraph. + return rs, nil + } // The root set has converged: every root going into this iteration was // already at its selected version, although we have have removed other // (redundant) roots for the same path. diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index a8cbd9fe16..45f724d5e3 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -449,13 +449,22 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) { } setDefaultBuildMod() // possibly enable automatic vendoring - rs = requirementsFromModFile(ctx) - + rs = requirementsFromModFile() if cfg.BuildMod == "vendor" { readVendorList() checkVendorConsistency() rs.initVendor(vendorList) } + if rs.hasRedundantRoot() { + // If any module path appears more than once in the roots, we know that the + // go.mod file needs to be updated even though we have not yet loaded any + // transitive dependencies. + rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) + if err != nil { + base.Fatalf("go: %v", err) + } + } + if index.goVersionV == "" { // TODO(#45551): Do something more principled instead of checking // cfg.CmdName directly here. @@ -530,7 +539,12 @@ func CreateModFile(ctx context.Context, modPath string) { base.Fatalf("go: %v", err) } - commitRequirements(ctx, modFileGoVersion(), requirementsFromModFile(ctx)) + rs := requirementsFromModFile() + rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) + if err != nil { + base.Fatalf("go: %v", err) + } + commitRequirements(ctx, modFileGoVersion(), rs) // Suggest running 'go mod tidy' unless the project is empty. Even if we // imported all the correct requirements above, we're probably missing @@ -641,9 +655,8 @@ func initTarget(m module.Version) { // requirementsFromModFile returns the set of non-excluded requirements from // the global modFile. -func requirementsFromModFile(ctx context.Context) *Requirements { +func requirementsFromModFile() *Requirements { roots := make([]module.Version, 0, len(modFile.Require)) - mPathCount := map[string]int{Target.Path: 1} direct := map[string]bool{} for _, r := range modFile.Require { if index != nil && index.exclude[r.Mod] { @@ -656,28 +669,12 @@ func requirementsFromModFile(ctx context.Context) *Requirements { } roots = append(roots, r.Mod) - mPathCount[r.Mod.Path]++ if !r.Indirect { direct[r.Mod.Path] = true } } module.Sort(roots) rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct) - - // If any module path appears more than once in the roots, we know that the - // go.mod file needs to be updated even though we have not yet loaded any - // transitive dependencies. - for _, n := range mPathCount { - if n > 1 { - var err error - rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false) - if err != nil { - base.Fatalf("go: %v", err) - } - break - } - } - return rs } diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index d280945ea6..03e02e73b6 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -595,47 +595,14 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { } c := rawGoModSummaryCache.Do(m, func() interface{} { summary := new(modFileSummary) - var f *modfile.File - if m.Version == "" { - // m is a replacement module with only a file path. - dir := m.Path - if !filepath.IsAbs(dir) { - dir = filepath.Join(ModRoot(), dir) - } - gomod := filepath.Join(dir, "go.mod") - var data []byte - var err error - if gomodActual, ok := fsys.OverlayPath(gomod); ok { - // Don't lock go.mod if it's part of the overlay. - // On Plan 9, locking requires chmod, and we don't want to modify any file - // in the overlay. See #44700. - data, err = os.ReadFile(gomodActual) - } else { - data, err = lockedfile.Read(gomodActual) - } - if err != nil { - return cached{nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(gomod), err))} - } - f, err = modfile.ParseLax(gomod, data, nil) - if err != nil { - return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(gomod), err))} - } - } else { - if !semver.IsValid(m.Version) { - // Disallow the broader queries supported by fetch.Lookup. - base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version) - } - - data, err := modfetch.GoMod(m.Path, m.Version) - if err != nil { - return cached{nil, err} - } - f, err = modfile.ParseLax("go.mod", data, nil) - if err != nil { - return cached{nil, module.VersionError(m, fmt.Errorf("parsing go.mod: %v", err))} - } + name, data, err := rawGoModData(m) + if err != nil { + return cached{nil, err} + } + f, err := modfile.ParseLax(name, data, nil) + if err != nil { + return cached{nil, module.VersionError(m, fmt.Errorf("parsing %s: %v", base.ShortPath(name), err))} } - if f.Module != nil { summary.module = f.Module.Mod summary.deprecated = f.Module.Deprecated @@ -671,6 +638,43 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) { var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result +// rawGoModData returns the content of the go.mod file for module m, ignoring +// all replacements that may apply to m. +// +// rawGoModData cannot be used on the Target module. +// +// Unlike rawGoModSummary, rawGoModData does not cache its results in memory. +// Use rawGoModSummary instead unless you specifically need these bytes. +func rawGoModData(m module.Version) (name string, data []byte, err error) { + if m.Version == "" { + // m is a replacement module with only a file path. + dir := m.Path + if !filepath.IsAbs(dir) { + dir = filepath.Join(ModRoot(), dir) + } + name = filepath.Join(dir, "go.mod") + if gomodActual, ok := fsys.OverlayPath(name); ok { + // Don't lock go.mod if it's part of the overlay. + // On Plan 9, locking requires chmod, and we don't want to modify any file + // in the overlay. See #44700. + data, err = os.ReadFile(gomodActual) + } else { + data, err = lockedfile.Read(gomodActual) + } + if err != nil { + return "", nil, module.VersionError(m, fmt.Errorf("reading %s: %v", base.ShortPath(name), err)) + } + } else { + if !semver.IsValid(m.Version) { + // Disallow the broader queries supported by fetch.Lookup. + base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", m.Path, m.Version) + } + name = "go.mod" + data, err = modfetch.GoMod(m.Path, m.Version) + } + return name, data, err +} + // queryLatestVersionIgnoringRetractions looks up the latest version of the // module with the given path without considering retracted or excluded // versions. diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index dda9004a9f..e737ca90fc 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -5,13 +5,13 @@ package modload import ( + "bytes" "context" "errors" "fmt" "io/fs" "os" pathpkg "path" - "path/filepath" "sort" "strings" "sync" @@ -931,14 +931,32 @@ func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) { return ok, err } -func versionHasGoMod(ctx context.Context, m module.Version) (bool, error) { - needSum := false - root, _, err := fetch(ctx, m, needSum) +// versionHasGoMod returns whether a version has a go.mod file. +// +// versionHasGoMod fetches the go.mod file (possibly a fake) and true if it +// contains anything other than a module directive with the same path. When a +// module does not have a real go.mod file, the go command acts as if it had one +// that only contained a module directive. Normal go.mod files created after +// 1.12 at least have a go directive. +// +// This function is a heuristic, since it's possible to commit a file that would +// pass this test. However, we only need a heurstic for determining whether +// +incompatible versions may be "latest", which is what this function is used +// for. +// +// This heuristic is useful for two reasons: first, when using a proxy, +// this lets us fetch from the .mod endpoint which is much faster than the .zip +// endpoint. The .mod file is used anyway, even if the .zip file contains a +// go.mod with different content. Second, if we don't fetch the .zip, then +// we don't need to verify it in go.sum. This makes 'go list -m -u' faster +// and simpler. +func versionHasGoMod(_ context.Context, m module.Version) (bool, error) { + _, data, err := rawGoModData(m) if err != nil { return false, err } - fi, err := os.Stat(filepath.Join(root, "go.mod")) - return err == nil && !fi.IsDir(), nil + isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path)) + return !isFake, nil } // A versionRepo is a subset of modfetch.Repo that can report information about diff --git a/src/cmd/go/testdata/script/mod_tidy_lazy_self.txt b/src/cmd/go/testdata/script/mod_tidy_lazy_self.txt index ffcea18603..9abbabd2eb 100644 --- a/src/cmd/go/testdata/script/mod_tidy_lazy_self.txt +++ b/src/cmd/go/testdata/script/mod_tidy_lazy_self.txt @@ -2,18 +2,13 @@ # 'go mod tidy' should not panic if the main module initially # requires an older version of itself. +# A module may require an older version of itself without error. This is +# inconsistent (the required version is never selected), but we still get +# a reproducible build list. +go list -m all +stdout '^golang.org/issue/46078$' -# A module that explicitly requires an older version of itself should be -# rejected as inconsistent: we enforce that every explicit requirement is the -# selected version of its module path, but the selected version of the main -# module is always itself — not some explicit version. - -! go list -m all -stderr '^go: updates to go\.mod needed; to update it:\n\tgo mod tidy$' - - -# The suggested 'go mod tidy' command should succeed (not crash). - +# 'go mod tidy' should fix this (and not crash). go mod tidy diff --git a/src/cmd/go/testdata/script/mod_update_sum_readonly.txt b/src/cmd/go/testdata/script/mod_update_sum_readonly.txt new file mode 100644 index 0000000000..41f12e4084 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_update_sum_readonly.txt @@ -0,0 +1,34 @@ +# When finding the latest version of a module, we should not download version +# contents. Previously, we downloaded .zip files to determine whether a real +# .mod file was present in order to decide whether +incompatible versions +# could be "latest". +# +# Verifies #47377. + +# rsc.io/breaker has two versions, neither of which has a .mod file. +go list -m -versions rsc.io/breaker +stdout '^rsc.io/breaker v1.0.0 v2.0.0\+incompatible$' +go mod download rsc.io/breaker@v1.0.0 +! grep '^go' $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v1.0.0.mod +go mod download rsc.io/breaker@v2.0.0+incompatible +! grep '^go' $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v2.0.0+incompatible.mod + +# Delete downloaded .zip files. +go clean -modcache + +# Check for updates. +go list -m -u rsc.io/breaker +stdout '^rsc.io/breaker v1.0.0 \[v2.0.0\+incompatible\]$' + +# We should not have downloaded zips. +! exists $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v1.0.0.zip +! exists $GOPATH/pkg/mod/cache/download/rsc.io/breaker/@v/v2.0.0+incompatible.zip + +-- go.mod -- +module m + +go 1.16 + +require rsc.io/breaker v1.0.0 +-- go.sum -- +rsc.io/breaker v1.0.0/go.mod h1:s5yxDXvD88U1/ESC23I2FK3Lkv4YIKaB1ij/Hbm805g= diff --git a/src/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt b/src/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt new file mode 100644 index 0000000000..3f6f5c5276 --- /dev/null +++ b/src/cmd/go/testdata/script/mod_vendor_redundant_requirement.txt @@ -0,0 +1,29 @@ +# 'go list -mod=vendor' should succeed even when go.mod contains redundant +# requirements. Verifies #47565. +go list -mod=vendor + +-- go.mod -- +module m + +go 1.17 + +require example.com/m v0.0.0 +require example.com/m v0.0.0 + +replace example.com/m v0.0.0 => ./m +-- m/go.mod -- +module example.com/m + +go 1.17 +-- m/m.go -- +package m +-- use.go -- +package use + +import _ "example.com/m" +-- vendor/example.com/m/m.go -- +package m +-- vendor/modules.txt -- +# example.com/m v0.0.0 => ./m +## explicit; go 1.17 +example.com/m diff --git a/src/cmd/internal/obj/textflag.go b/src/cmd/internal/obj/textflag.go index 881e192203..5ae75027c2 100644 --- a/src/cmd/internal/obj/textflag.go +++ b/src/cmd/internal/obj/textflag.go @@ -49,8 +49,8 @@ const ( // Function can call reflect.Type.Method or reflect.Type.MethodByName. REFLECTMETHOD = 1024 - // Function is the top of the call stack. Call stack unwinders should stop - // at this function. + // Function is the outermost frame of the call stack. Call stack unwinders + // should stop at this function. TOPFRAME = 2048 // Function is an ABI wrapper. diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go index a33bba2466..7da8606ece 100644 --- a/src/cmd/vet/main.go +++ b/src/cmd/vet/main.go @@ -1,3 +1,7 @@ +// Copyright 2012 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 main import ( diff --git a/src/cmd/vet/testdata/copylock/copylock.go b/src/cmd/vet/testdata/copylock/copylock.go index 8079cf3248..7cfafe6408 100644 --- a/src/cmd/vet/testdata/copylock/copylock.go +++ b/src/cmd/vet/testdata/copylock/copylock.go @@ -1,3 +1,7 @@ +// Copyright 2018 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 copylock import "sync" diff --git a/src/cmd/vet/testdata/httpresponse/httpresponse.go b/src/cmd/vet/testdata/httpresponse/httpresponse.go index 6141f6e06d..98e394a271 100644 --- a/src/cmd/vet/testdata/httpresponse/httpresponse.go +++ b/src/cmd/vet/testdata/httpresponse/httpresponse.go @@ -1,3 +1,7 @@ +// Copyright 2018 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 httpresponse import ( diff --git a/src/cmd/vet/testdata/testingpkg/tests.go b/src/cmd/vet/testdata/testingpkg/tests.go index 69d29d3c6c..8f4674d33c 100644 --- a/src/cmd/vet/testdata/testingpkg/tests.go +++ b/src/cmd/vet/testdata/testingpkg/tests.go @@ -1 +1,5 @@ +// Copyright 2018 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 testdata diff --git a/src/cmd/vet/testdata/testingpkg/tests_test.go b/src/cmd/vet/testdata/testingpkg/tests_test.go index 09bb98d980..815dcc8a95 100644 --- a/src/cmd/vet/testdata/testingpkg/tests_test.go +++ b/src/cmd/vet/testdata/testingpkg/tests_test.go @@ -1,3 +1,7 @@ +// Copyright 2018 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 testdata func Example_BadSuffix() {} // ERROR "Example_BadSuffix has malformed example suffix: BadSuffix" diff --git a/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go b/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go index 44dc8e8caf..8fe583939f 100644 --- a/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go +++ b/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go @@ -1,5 +1,6 @@ // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. +//go:build amd64 && gc && !purego // +build amd64,gc,!purego package field diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index b2d532c4c8..f138af5fbf 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -322,6 +322,18 @@ func TestTypesInfo(t *testing.T) { `[][]struct{}`, }, + // issue 47243 + {`package issue47243_a; var x int32; var _ = x << 3`, `3`, `untyped int`}, + {`package issue47243_b; var x int32; var _ = x << 3.`, `3.`, `uint`}, // issue 47410: should be untyped float + {`package issue47243_c; var x int32; var _ = 1 << x`, `1 << x`, `int`}, + {`package issue47243_d; var x int32; var _ = 1 << x`, `1`, `int`}, + {`package issue47243_e; var x int32; var _ = 1 << 2`, `1`, `untyped int`}, + {`package issue47243_f; var x int32; var _ = 1 << 2`, `2`, `untyped int`}, + {`package issue47243_g; var x int32; var _ = int(1) << 2`, `2`, `untyped int`}, + {`package issue47243_h; var x int32; var _ = 1 << (2 << x)`, `1`, `int`}, + {`package issue47243_i; var x int32; var _ = 1 << (2 << x)`, `(2 << x)`, `untyped int`}, + {`package issue47243_j; var x int32; var _ = 1 << (2 << x)`, `2`, `untyped int`}, + // tests for broken code that doesn't parse or type-check {broken + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`}, {broken + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`}, diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 692004facf..8c8452c9c6 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -354,6 +354,13 @@ func TestIndexRepresentability(t *testing.T) { testFiles(t, &StdSizes{4, 4}, []string{"index.go"}, [][]byte{[]byte(src)}, false, nil) } +func TestIssue47243_TypedRHS(t *testing.T) { + // The RHS of the shift expression below overflows uint on 32bit platforms, + // but this is OK as it is explicitly typed. + const src = "package issue47243\n\nvar a uint64; var _ = a << uint64(4294967296)" // uint64(1<<32) + testFiles(t, &StdSizes{4, 4}, []string{"p.go"}, [][]byte{[]byte(src)}, false, nil) +} + func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) } func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) } func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", false) } diff --git a/src/go/types/expr.go b/src/go/types/expr.go index b55f51185f..c9a55aa871 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -795,32 +795,48 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) { // spec: "The right operand in a shift expression must have integer type // or be an untyped constant representable by a value of type uint." - // Provide a good error message for negative shift counts. + // Check that constants are representable by uint, but do not convert them + // (see also issue #47243). if y.mode == constant_ { + // Provide a good error message for negative shift counts. yval := constant.ToInt(y.val) // consider -1, 1.0, but not -1.1 if yval.Kind() == constant.Int && constant.Sign(yval) < 0 { check.invalidOp(y, _InvalidShiftCount, "negative shift count %s", y) x.mode = invalid return } + + if isUntyped(y.typ) { + // Caution: Check for representability here, rather than in the switch + // below, because isInteger includes untyped integers (was bug #43697). + check.representable(y, Typ[Uint]) + if y.mode == invalid { + x.mode = invalid + return + } + } } - // Caution: Check for isUntyped first because isInteger includes untyped - // integers (was bug #43697). - if isUntyped(y.typ) { + // Check that RHS is otherwise at least of integer type. + switch { + case isInteger(y.typ): + if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { + check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y) + x.mode = invalid + return + } + case isUntyped(y.typ): + // This is incorrect, but preserves pre-existing behavior. + // See also bug #47410. check.convertUntyped(y, Typ[Uint]) if y.mode == invalid { x.mode = invalid return } - } else if !isInteger(y.typ) { + default: check.invalidOp(y, _InvalidShiftCount, "shift count %s must be integer", y) x.mode = invalid return - } else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) { - check.invalidOp(y, _InvalidShiftCount, "signed shift count %s requires go1.13 or later", y) - x.mode = invalid - return } if x.mode == constant_ { diff --git a/src/io/fs/fs.go b/src/io/fs/fs.go index e1be32478e..e603afadb0 100644 --- a/src/io/fs/fs.go +++ b/src/io/fs/fs.go @@ -86,7 +86,7 @@ type File interface { type DirEntry interface { // Name returns the name of the file (or subdirectory) described by the entry. // This name is only the final element of the path (the base name), not the entire path. - // For example, Name would return "hello.go" not "/home/gopher/hello.go". + // For example, Name would return "hello.go" not "home/gopher/hello.go". Name() string // IsDir reports whether the entry describes a directory. diff --git a/src/make.bash b/src/make.bash index 4fb13f6275..7986125a06 100755 --- a/src/make.bash +++ b/src/make.bash @@ -130,8 +130,8 @@ if [ "$(uname -s)" = "GNU/kFreeBSD" ]; then export CGO_ENABLED=0 fi -# Test which linker/loader our system is using -if type readelf >/dev/null 2>&1; then +# Test which linker/loader our system is using, if GO_LDSO is not set. +if [ -z "$GO_LDSO" ] && type readelf >/dev/null 2>&1; then if echo "int main() { return 0; }" | ${CC:-cc} -o ./test-musl-ldso -x c - >/dev/null 2>&1; then LDSO=$(readelf -l ./test-musl-ldso | grep 'interpreter:' | sed -e 's/^.*interpreter: \(.*\)[]]/\1/') >/dev/null 2>&1 [ -z "$LDSO" ] || export GO_LDSO="$LDSO" @@ -203,16 +203,10 @@ if [ "$1" = "--dist-tool" ]; then exit 0 fi -buildall="-a" -if [ "$1" = "--no-clean" ]; then - buildall="" - shift -fi - # Run dist bootstrap to complete make.bash. # Bootstrap installs a proper cmd/dist, built with the new toolchain. # Throw ours, built with Go 1.4, away after bootstrap. -./cmd/dist/dist bootstrap $buildall $vflag $GO_DISTFLAGS "$@" +./cmd/dist/dist bootstrap -a $vflag $GO_DISTFLAGS "$@" rm -f ./cmd/dist/dist # DO NOT ADD ANY NEW CODE HERE. diff --git a/src/make.bat b/src/make.bat index b4a8e70849..8f2825b09a 100644 --- a/src/make.bat +++ b/src/make.bat @@ -112,20 +112,20 @@ if x%2==x--dist-tool goto copydist if x%3==x--dist-tool goto copydist
if x%4==x--dist-tool goto copydist
-set buildall=-a
-if x%1==x--no-clean set buildall=
-if x%2==x--no-clean set buildall=
-if x%3==x--no-clean set buildall=
-if x%4==x--no-clean set buildall=
-if x%1==x--no-banner set buildall=%buildall% --no-banner
-if x%2==x--no-banner set buildall=%buildall% --no-banner
-if x%3==x--no-banner set buildall=%buildall% --no-banner
-if x%4==x--no-banner set buildall=%buildall% --no-banner
+set bootstrapflags=
+if x%1==x--no-clean set bootstrapflags=--no-clean
+if x%2==x--no-clean set bootstrapflags=--no-clean
+if x%3==x--no-clean set bootstrapflags=--no-clean
+if x%4==x--no-clean set bootstrapflags=--no-clean
+if x%1==x--no-banner set bootstrapflags=%bootstrapflags% --no-banner
+if x%2==x--no-banner set bootstrapflags=%bootstrapflags% --no-banner
+if x%3==x--no-banner set bootstrapflags=%bootstrapflags% --no-banner
+if x%4==x--no-banner set bootstrapflags=%bootstrapflags% --no-banner
:: Run dist bootstrap to complete make.bash.
:: Bootstrap installs a proper cmd/dist, built with the new toolchain.
:: Throw ours, built with Go 1.4, away after bootstrap.
-.\cmd\dist\dist.exe bootstrap %vflag% %buildall%
+.\cmd\dist\dist.exe bootstrap -a %vflag% %bootstrapflags%
if errorlevel 1 goto fail
del .\cmd\dist\dist.exe
goto end
diff --git a/src/make.rc b/src/make.rc index f5e57e9755..7bdc7dea1c 100755 --- a/src/make.rc +++ b/src/make.rc @@ -92,15 +92,10 @@ if(~ $1 --dist-tool){ exit } -buildall = -a -if(~ $1 --no-clean) { - buildall = () - shift -} # Run dist bootstrap to complete make.bash. # Bootstrap installs a proper cmd/dist, built with the new toolchain. # Throw ours, built with Go 1.4, away after bootstrap. -./cmd/dist/dist bootstrap $vflag $buildall $* +./cmd/dist/dist bootstrap -a $vflag $* rm -f ./cmd/dist/dist # DO NOT ADD ANY NEW CODE HERE. diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index 5d39955d62..8b63368386 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -235,6 +235,15 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req.ContentLength == 0 { outreq.Body = nil // Issue 16036: nil Body for http.Transport retries } + if outreq.Body != nil { + // Reading from the request body after returning from a handler is not + // allowed, and the RoundTrip goroutine that reads the Body can outlive + // this handler. This can lead to a crash if the handler panics (see + // Issue 46866). Although calling Close doesn't guarantee there isn't + // any Read in flight after the handle returns, in practice it's safe to + // read after closing it. + defer outreq.Body.Close() + } if outreq.Header == nil { outreq.Header = make(http.Header) // Issue 33142: historical behavior was to always allocate } diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index 1898ed8b8a..4b6ad77a29 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -1122,6 +1122,45 @@ func TestReverseProxy_PanicBodyError(t *testing.T) { rproxy.ServeHTTP(httptest.NewRecorder(), req) } +// Issue #46866: panic without closing incoming request body causes a panic +func TestReverseProxy_PanicClosesIncomingBody(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + out := "this call was relayed by the reverse proxy" + // Coerce a wrong content length to induce io.ErrUnexpectedEOF + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(out)*2)) + fmt.Fprintln(w, out) + })) + defer backend.Close() + backendURL, err := url.Parse(backend.URL) + if err != nil { + t.Fatal(err) + } + proxyHandler := NewSingleHostReverseProxy(backendURL) + proxyHandler.ErrorLog = log.New(io.Discard, "", 0) // quiet for tests + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + frontendClient := frontend.Client() + + var wg sync.WaitGroup + for i := 0; i < 2; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for j := 0; j < 10; j++ { + const reqLen = 6 * 1024 * 1024 + req, _ := http.NewRequest("POST", frontend.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen}) + req.ContentLength = reqLen + resp, _ := frontendClient.Transport.RoundTrip(req) + if resp != nil { + io.Copy(io.Discard, resp.Body) + resp.Body.Close() + } + } + }() + } + wg.Wait() +} + func TestSelectFlushInterval(t *testing.T) { tests := []struct { name string diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 690e0c299d..eeaa492644 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -6441,10 +6441,11 @@ func TestErrorWriteLoopRace(t *testing.T) { // Test that a new request which uses the connection of an active request // cannot cause it to be canceled as well. func TestCancelRequestWhenSharingConnection(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } + reqc := make(chan chan struct{}, 2) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, req *Request) { + ch := make(chan struct{}, 1) + reqc <- ch + <-ch w.Header().Add("Content-Length", "0") })) defer ts.Close() @@ -6456,34 +6457,58 @@ func TestCancelRequestWhenSharingConnection(t *testing.T) { var wg sync.WaitGroup - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + wg.Add(1) + putidlec := make(chan chan struct{}) + go func() { + defer wg.Done() + ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ + PutIdleConn: func(error) { + // Signal that the idle conn has been returned to the pool, + // and wait for the order to proceed. + ch := make(chan struct{}) + putidlec <- ch + <-ch + }, + }) + req, _ := NewRequestWithContext(ctx, "GET", ts.URL, nil) + res, err := client.Do(req) + if err == nil { + res.Body.Close() + } + if err != nil { + t.Errorf("request 1: got err %v, want nil", err) + } + }() - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - for ctx.Err() == nil { - reqctx, reqcancel := context.WithCancel(ctx) - go reqcancel() - req, _ := NewRequestWithContext(reqctx, "GET", ts.URL, nil) - res, err := client.Do(req) - if err == nil { - res.Body.Close() - } - } - }() - } + // Wait for the first request to receive a response and return the + // connection to the idle pool. + r1c := <-reqc + close(r1c) + idlec := <-putidlec - for ctx.Err() == nil { - req, _ := NewRequest("GET", ts.URL, nil) - if res, err := client.Do(req); err != nil { - t.Errorf("unexpected: %p %v", req, err) - break - } else { + wg.Add(1) + cancelctx, cancel := context.WithCancel(context.Background()) + go func() { + defer wg.Done() + req, _ := NewRequestWithContext(cancelctx, "GET", ts.URL, nil) + res, err := client.Do(req) + if err == nil { res.Body.Close() } - } + if !errors.Is(err, context.Canceled) { + t.Errorf("request 2: got err %v, want Canceled", err) + } + }() + // Wait for the second request to arrive at the server, and then cancel + // the request context. + r2c := <-reqc cancel() + + // Give the cancelation a moment to take effect, and then unblock the first request. + time.Sleep(1 * time.Millisecond) + close(idlec) + + close(r2c) wg.Wait() } diff --git a/src/os/exec/lp_windows_test.go b/src/os/exec/lp_windows_test.go index f834ffede0..bbf6a9b7f1 100644 --- a/src/os/exec/lp_windows_test.go +++ b/src/os/exec/lp_windows_test.go @@ -312,9 +312,6 @@ func TestLookPath(t *testing.T) { // Run all tests. for i, test := range lookPathTests { t.Run(fmt.Sprint(i), func(t *testing.T) { - if i == 16 { - t.Skip("golang.org/issue/44379") - } dir := filepath.Join(tmp, "d"+strconv.Itoa(i)) err := os.Mkdir(dir, 0700) if err != nil { diff --git a/src/runtime/cgo/gcc_traceback.c b/src/runtime/cgo/gcc_traceback.c index d86331c583..6e9470c43c 100644 --- a/src/runtime/cgo/gcc_traceback.c +++ b/src/runtime/cgo/gcc_traceback.c @@ -7,6 +7,14 @@ #include <stdint.h> #include "libcgo.h" +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(memory_sanitizer) +#include <sanitizer/msan_interface.h> +#endif + // Call the user's traceback function and then call sigtramp. // The runtime signal handler will jump to this code. // We do it this way so that the user's traceback function will be called @@ -19,6 +27,18 @@ x_cgo_callers(uintptr_t sig, void *info, void *context, void (*cgoTraceback)(str arg.SigContext = (uintptr_t)(context); arg.Buf = cgoCallers; arg.Max = 32; // must match len(runtime.cgoCallers) + +#if __has_feature(memory_sanitizer) + // This function is called directly from the signal handler. + // The arguments are passed in registers, so whether msan + // considers cgoCallers to be initialized depends on whether + // it considers the appropriate register to be initialized. + // That can cause false reports in rare cases. + // Explicitly unpoison the memory to avoid that. + // See issue #47543 for more details. + __msan_unpoison(&arg, sizeof arg); +#endif + (*cgoTraceback)(&arg); sigtramp(sig, info, context); } diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 479878e6d2..2f3c609907 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -213,6 +213,8 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) { // a different M. The call to unlockOSThread is in unwindm. lockOSThread() + checkm := gp.m + // Save current syscall parameters, so m.syscall can be // used again if callback decide to make syscall. syscall := gp.m.syscall @@ -228,15 +230,20 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) { osPreemptExtExit(gp.m) - cgocallbackg1(fn, frame, ctxt) + cgocallbackg1(fn, frame, ctxt) // will call unlockOSThread // At this point unlockOSThread has been called. // The following code must not change to a different m. // This is enforced by checking incgo in the schedule function. + gp.m.incgo = true + + if gp.m != checkm { + throw("m changed unexpectedly in cgocallbackg") + } + osPreemptExtEnter(gp.m) - gp.m.incgo = true // going back to cgo call reentersyscall(savedpc, uintptr(savedsp)) @@ -245,6 +252,11 @@ func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) { func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) { gp := getg() + + // When we return, undo the call to lockOSThread in cgocallbackg. + // We must still stay on the same m. + defer unlockOSThread() + if gp.m.needextram || atomic.Load(&extraMWaiters) > 0 { gp.m.needextram = false systemstack(newextram) @@ -324,10 +336,6 @@ func unwindm(restore *bool) { releasem(mp) } - - // Undo the call to lockOSThread in cgocallbackg. - // We must still stay on the same m. - unlockOSThread() } // called from assembly diff --git a/src/runtime/checkptr.go b/src/runtime/checkptr.go index d42950844b..2d4afd5cf6 100644 --- a/src/runtime/checkptr.go +++ b/src/runtime/checkptr.go @@ -7,6 +7,11 @@ package runtime import "unsafe" func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) { + // nil pointer is always suitably aligned (#47430). + if p == nil { + return + } + // Check that (*[n]elem)(p) is appropriately aligned. // Note that we allow unaligned pointers if the types they point to contain // no pointers themselves. See issue 37298. @@ -29,10 +34,12 @@ func checkptrStraddles(ptr unsafe.Pointer, size uintptr) bool { return false } - end := add(ptr, size-1) - if uintptr(end) < uintptr(ptr) { + // Check that add(ptr, size-1) won't overflow. This avoids the risk + // of producing an illegal pointer value (assuming ptr is legal). + if uintptr(ptr) >= -(size - 1) { return true } + end := add(ptr, size-1) // TODO(mdempsky): Detect when [ptr, end] contains Go allocations, // but neither ptr nor end point into one themselves. diff --git a/src/runtime/checkptr_test.go b/src/runtime/checkptr_test.go index 2a5c364e97..d5dd101adb 100644 --- a/src/runtime/checkptr_test.go +++ b/src/runtime/checkptr_test.go @@ -26,6 +26,7 @@ func TestCheckPtr(t *testing.T) { }{ {"CheckPtrAlignmentPtr", "fatal error: checkptr: misaligned pointer conversion\n"}, {"CheckPtrAlignmentNoPtr", ""}, + {"CheckPtrAlignmentNilPtr", ""}, {"CheckPtrArithmetic", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"}, {"CheckPtrArithmetic2", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"}, {"CheckPtrSize", "fatal error: checkptr: converted pointer straddles multiple allocations\n"}, diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index 7d25c51aa2..5729942cee 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -282,6 +282,15 @@ func TestCgoTracebackContext(t *testing.T) { } } +func TestCgoTracebackContextPreemption(t *testing.T) { + t.Parallel() + got := runTestProg(t, "testprogcgo", "TracebackContextPreemption") + want := "OK\n" + if got != want { + t.Errorf("expected %q got %v", want, got) + } +} + func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { t.Parallel() if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index c07ea5e375..3cdb81e2fb 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -466,6 +466,10 @@ okarg: // Without the KeepAlive call, the finalizer could run at the start of // syscall.Read, closing the file descriptor before syscall.Read makes // the actual system call. +// +// Note: KeepAlive should only be used to prevent finalizers from +// running prematurely. In particular, when used with unsafe.Pointer, +// the rules for valid uses of unsafe.Pointer still apply. func KeepAlive(x interface{}) { // Introduce a use of x that the compiler can't eliminate. // This makes sure x is alive on entry. We need x to be alive diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 4dc1811fc6..ec4be31db3 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4850,7 +4850,6 @@ func (pp *p) destroy() { moveTimers(plocal, pp.timers) pp.timers = nil pp.numTimers = 0 - pp.adjustTimers = 0 pp.deletedTimers = 0 atomic.Store64(&pp.timer0When, 0) unlock(&pp.timersLock) diff --git a/src/runtime/race.go b/src/runtime/race.go index f1c3c3098d..7eaa9d2b72 100644 --- a/src/runtime/race.go +++ b/src/runtime/race.go @@ -344,7 +344,7 @@ func racereadrangepc1(addr, size, pc uintptr) func racewriterangepc1(addr, size, pc uintptr) func racecallbackthunk(uintptr) -// racecall allows calling an arbitrary function f from C race runtime +// racecall allows calling an arbitrary function fn from C race runtime // with up to 4 uintptr arguments. func racecall(fn *byte, arg0, arg1, arg2, arg3 uintptr) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index c5e2501991..e4e9ee50b8 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -727,12 +727,6 @@ type p struct { // Modified using atomic instructions. numTimers uint32 - // Number of timerModifiedEarlier timers on P's heap. - // This should only be modified while holding timersLock, - // or while the timer status is in a transient state - // such as timerModifying. - adjustTimers uint32 - // Number of timerDeleted timers in P's heap. // Modified using atomic instructions. deletedTimers uint32 diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index af15709a4a..3fe352ef57 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -184,6 +184,17 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { return _EXCEPTION_CONTINUE_SEARCH } + // VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap + // illegal instructions during runtime initialization to determine + // CPU features, so if we make it to the last handler and we're + // arm64 and it's an illegal instruction and this is coming from + // non-Go code, then assume it's this runtime probing happen, and + // pass that onward to SEH. + if GOARCH == "arm64" && info.exceptioncode == _EXCEPTION_ILLEGAL_INSTRUCTION && + (r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip()) { + return _EXCEPTION_CONTINUE_SEARCH + } + winthrow(info, r, gp) return 0 // not reached } diff --git a/src/runtime/testdata/testprog/checkptr.go b/src/runtime/testdata/testprog/checkptr.go index f76b64ad96..9c5561396e 100644 --- a/src/runtime/testdata/testprog/checkptr.go +++ b/src/runtime/testdata/testprog/checkptr.go @@ -4,11 +4,16 @@ package main -import "unsafe" +import ( + "runtime" + "time" + "unsafe" +) func init() { register("CheckPtrAlignmentNoPtr", CheckPtrAlignmentNoPtr) register("CheckPtrAlignmentPtr", CheckPtrAlignmentPtr) + register("CheckPtrAlignmentNilPtr", CheckPtrAlignmentNilPtr) register("CheckPtrArithmetic", CheckPtrArithmetic) register("CheckPtrArithmetic2", CheckPtrArithmetic2) register("CheckPtrSize", CheckPtrSize) @@ -29,6 +34,35 @@ func CheckPtrAlignmentPtr() { sink2 = (**int64)(unsafe.Pointer(uintptr(p) + 1)) } +// CheckPtrAlignmentNilPtr tests that checkptrAlignment doesn't crash +// on nil pointers (#47430). +func CheckPtrAlignmentNilPtr() { + var do func(int) + do = func(n int) { + // Inflate the stack so runtime.shrinkstack gets called during GC + if n > 0 { + do(n - 1) + } + + var p unsafe.Pointer + _ = (*int)(p) + } + + go func() { + for { + runtime.GC() + } + }() + + go func() { + for i := 0; ; i++ { + do(i % 1024) + } + }() + + time.Sleep(time.Second) +} + func CheckPtrArithmetic() { var x int i := uintptr(unsafe.Pointer(&x)) diff --git a/src/runtime/testdata/testprogcgo/tracebackctxt.go b/src/runtime/testdata/testprogcgo/tracebackctxt.go index 51fa4ad25c..62ff8eccd6 100644 --- a/src/runtime/testdata/testprogcgo/tracebackctxt.go +++ b/src/runtime/testdata/testprogcgo/tracebackctxt.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// The __attribute__((weak)) used below doesn't seem to work on Windows. - package main // Test the context argument to SetCgoTraceback. @@ -14,20 +12,24 @@ package main extern void C1(void); extern void C2(void); extern void tcContext(void*); +extern void tcContextSimple(void*); extern void tcTraceback(void*); extern void tcSymbolizer(void*); extern int getContextCount(void); +extern void TracebackContextPreemptionCallGo(int); */ import "C" import ( "fmt" "runtime" + "sync" "unsafe" ) func init() { register("TracebackContext", TracebackContext) + register("TracebackContextPreemption", TracebackContextPreemption) } var tracebackOK bool @@ -105,3 +107,30 @@ wantLoop: tracebackOK = false } } + +// Issue 47441. +func TracebackContextPreemption() { + runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContextSimple), unsafe.Pointer(C.tcSymbolizer)) + + const funcs = 10 + const calls = 1e5 + var wg sync.WaitGroup + for i := 0; i < funcs; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + for j := 0; j < calls; j++ { + C.TracebackContextPreemptionCallGo(C.int(i*calls + j)) + } + }(i) + } + wg.Wait() + + fmt.Println("OK") +} + +//export TracebackContextPreemptionGoFunction +func TracebackContextPreemptionGoFunction(i C.int) { + // Do some busy work. + fmt.Sprintf("%d\n", i) +} diff --git a/src/runtime/testdata/testprogcgo/tracebackctxt_c.c b/src/runtime/testdata/testprogcgo/tracebackctxt_c.c index 900cada0d3..910cb7b899 100644 --- a/src/runtime/testdata/testprogcgo/tracebackctxt_c.c +++ b/src/runtime/testdata/testprogcgo/tracebackctxt_c.c @@ -11,6 +11,7 @@ // Functions exported from Go. extern void G1(void); extern void G2(void); +extern void TracebackContextPreemptionGoFunction(int); void C1() { G1(); @@ -62,10 +63,17 @@ void tcContext(void* parg) { } } +void tcContextSimple(void* parg) { + struct cgoContextArg* arg = (struct cgoContextArg*)(parg); + if (arg->context == 0) { + arg->context = 1; + } +} + void tcTraceback(void* parg) { int base, i; struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg); - if (arg->context == 0) { + if (arg->context == 0 && arg->sigContext == 0) { // This shouldn't happen in this program. abort(); } @@ -89,3 +97,7 @@ void tcSymbolizer(void *parg) { arg->func = "cFunction"; arg->lineno = arg->pc + (arg->more << 16); } + +void TracebackContextPreemptionCallGo(int i) { + TracebackContextPreemptionGoFunction(i); +} diff --git a/src/runtime/textflag.h b/src/runtime/textflag.h index e727208cd0..214075e360 100644 --- a/src/runtime/textflag.h +++ b/src/runtime/textflag.h @@ -32,8 +32,8 @@ #define NOFRAME 512 // Function can call reflect.Type.Method or reflect.Type.MethodByName. #define REFLECTMETHOD 1024 -// Function is the top of the call stack. Call stack unwinders should stop -// at this function. +// Function is the outermost frame of the call stack. Call stack unwinders +// should stop at this function. #define TOPFRAME 2048 // Function is an ABI wrapper. #define ABIWRAPPER 4096 diff --git a/src/runtime/time.go b/src/runtime/time.go index 2f791c4ad8..ad267c3365 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -334,7 +334,6 @@ func deltimer(t *timer) bool { // Must fetch t.pp before setting status // to timerDeleted. tpp := t.pp.ptr() - atomic.Xadd(&tpp.adjustTimers, -1) if !atomic.Cas(&t.status, timerModifying, timerDeleted) { badTimer() } @@ -511,20 +510,9 @@ loop: tpp := t.pp.ptr() - // Update the adjustTimers field. Subtract one if we - // are removing a timerModifiedEarlier, add one if we - // are adding a timerModifiedEarlier. - adjust := int32(0) - if status == timerModifiedEarlier { - adjust-- - } if newStatus == timerModifiedEarlier { - adjust++ updateTimerModifiedEarliest(tpp, when) } - if adjust != 0 { - atomic.Xadd(&tpp.adjustTimers, adjust) - } // Set the new status of the timer. if !atomic.Cas(&t.status, timerModifying, newStatus) { @@ -592,9 +580,6 @@ func cleantimers(pp *p) { // Move t to the right position. dodeltimer0(pp) doaddtimer(pp, t) - if s == timerModifiedEarlier { - atomic.Xadd(&pp.adjustTimers, -1) - } if !atomic.Cas(&t.status, timerMoving, timerWaiting) { badTimer() } @@ -665,32 +650,23 @@ func moveTimers(pp *p, timers []*timer) { // it also moves timers that have been modified to run later, // and removes deleted timers. The caller must have locked the timers for pp. func adjusttimers(pp *p, now int64) { - if atomic.Load(&pp.adjustTimers) == 0 { - if verifyTimers { - verifyTimerHeap(pp) - } - return - } - // If we haven't yet reached the time of the first timerModifiedEarlier // timer, don't do anything. This speeds up programs that adjust // a lot of timers back and forth if the timers rarely expire. // We'll postpone looking through all the adjusted timers until // one would actually expire. - if first := atomic.Load64(&pp.timerModifiedEarliest); first != 0 { - if int64(first) > now { - if verifyTimers { - verifyTimerHeap(pp) - } - return + first := atomic.Load64(&pp.timerModifiedEarliest) + if first == 0 || int64(first) > now { + if verifyTimers { + verifyTimerHeap(pp) } - - // We are going to clear all timerModifiedEarlier timers. - atomic.Store64(&pp.timerModifiedEarliest, 0) + return } + // We are going to clear all timerModifiedEarlier timers. + atomic.Store64(&pp.timerModifiedEarliest, 0) + var moved []*timer -loop: for i := 0; i < len(pp.timers); i++ { t := pp.timers[i] if t.pp.ptr() != pp { @@ -717,11 +693,6 @@ loop: // loop to skip some other timer. dodeltimer(pp, i) moved = append(moved, t) - if s == timerModifiedEarlier { - if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 { - break loop - } - } // Look at this heap position again. i-- } @@ -820,9 +791,6 @@ func runtimer(pp *p, now int64) int64 { t.when = t.nextwhen dodeltimer0(pp) doaddtimer(pp, t) - if s == timerModifiedEarlier { - atomic.Xadd(&pp.adjustTimers, -1) - } if !atomic.Cas(&t.status, timerMoving, timerWaiting) { badTimer() } @@ -917,7 +885,6 @@ func clearDeletedTimers(pp *p) { atomic.Store64(&pp.timerModifiedEarliest, 0) cdel := int32(0) - cearlier := int32(0) to := 0 changedHeap := false timers := pp.timers @@ -942,9 +909,6 @@ nextTimer: if !atomic.Cas(&t.status, timerMoving, timerWaiting) { badTimer() } - if s == timerModifiedEarlier { - cearlier++ - } continue nextTimer } case timerDeleted: @@ -981,7 +945,6 @@ nextTimer: atomic.Xadd(&pp.deletedTimers, -cdel) atomic.Xadd(&pp.numTimers, -cdel) - atomic.Xadd(&pp.adjustTimers, -cearlier) timers = timers[:to] pp.timers = timers diff --git a/src/testing/testing.go b/src/testing/testing.go index 681f99ef93..a19238d31e 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -680,7 +680,11 @@ type T struct { func (c *common) private() {} -// Name returns the name of the running test or benchmark. +// Name returns the name of the running (sub-) test or benchmark. +// +// The name will include the name of the test along with the names of +// any nested sub-tests. If two sibling sub-tests have the same name, +// Name will append a suffix to guarantee the returned name is unique. func (c *common) Name() string { return c.name } |