diff options
author | Dmitri Shuralyov <dmitshur@golang.org> | 2023-06-20 13:19:34 -0400 |
---|---|---|
committer | Dmitri Shuralyov <dmitshur@golang.org> | 2023-06-20 13:19:35 -0400 |
commit | ebbff91f5930ba74a4285f8ba6ddfd0df0871d95 (patch) | |
tree | 3946ab04bca39c829797bda4b0d989083cfda23c | |
parent | 1c1c82432a78b06c8010c7257df58ff11cc05b61 (diff) | |
parent | a7b179370124c0114036b98a14f3f17cf76c122d (diff) | |
download | go-ebbff91f5930ba74a4285f8ba6ddfd0df0871d95.tar.gz go-ebbff91f5930ba74a4285f8ba6ddfd0df0871d95.zip |
[release-branch.go1.21] all: merge master (a7b1793) into release-branch.go1.21
Merge List:
+ 2023-06-20 a7b1793701 cmd/go: do not index std as a module in modcache
+ 2023-06-20 3d279283a4 cmd/go: restore go.mod files during toolchain selection
+ 2023-06-20 3b4b7b84de cmd/distpack: rename go.mod to _go.mod in toolchain modules
+ 2023-06-20 6459494014 cmd/go: disable sumdb less often for toolchain downloads
+ 2023-06-20 02789816c4 internal/bisect: add 'q' hash option for quiet hash behavior switching
+ 2023-06-20 98617fd23f runtime/trace: add godoc links
+ 2023-06-19 bc21d6a4fc cmd/go/internal/modfetch: fix retractions slice initial length not zero
+ 2023-06-17 261e267618 os/exec: document a method to check if a process is alive
+ 2023-06-16 dbf9bf2c39 cmd/internal/moddeps: allow the "misc" module to be missing from GOROOT
+ 2023-06-16 0183c1aa02 cmd/compile/internal/syntax: skip GOROOT/misc in TestStdLib if it doesn't exist
+ 2023-06-16 199fbd4b59 cmd/internal/testdir: skip Test if GOROOT/test does not exist
+ 2023-06-16 a48f9c26d5 go/types: skip tests that require GOROOT/test if it is not present
+ 2023-06-16 3891ecbd35 go/internal/gcimporter: skip TestImportTypeparamTests if GOROOT/test is missing
+ 2023-06-16 60876717b4 cmd/go/internal/test: don't wait for previous test actions when interrupted
+ 2023-06-16 c1bc44642d path/filepath: avoid assuming that GOROOT/test is present
+ 2023-06-16 9ece9a7ac9 cmd/cgo/internal/testshared: disable gccgo tests on PPC64
+ 2023-06-16 23c5e48c4a cmd/cgo/internal/testshared: strip newline from gccgo -dumpversion
+ 2023-06-16 cf7ae4f136 compress/bzip2: fix typo
+ 2023-06-16 3c8b7a9551 net/http: check RemoteAddr isn't nil before dereferencing
+ 2023-06-16 548790e64a net/http: close req.Body only when it's non-nil on js
+ 2023-06-16 6dc2d2aa6b testing/fstest: fix the Glob test when dir entries are out of order
+ 2023-06-16 2b0ff4b629 reflect: fix ArenaNew to match documentation
+ 2023-06-16 4eceefa338 cmd/distpack: make go_$GOOS_$GOARCH_exec programs executable
+ 2023-06-16 1a7709d6af runtime: use 1-byte load for address checking in racecallatomic
+ 2023-06-15 3e7ec13166 cmd/go: fix build config for 'go list -cover'
+ 2023-06-15 30b17f4f97 net/http: only disable Fetch API in tests
+ 2023-06-15 65db95d0ed math: document that Min/Max differ from min/max
+ 2023-06-15 60e6afb689 cmd/compile: do not report division by error during typecheck
+ 2023-06-15 f6e0dcc474 slices: add sort benchmark for sorted strings
Change-Id: If342a000b719335fbbb421f027a8b253b07c1cab
41 files changed, 685 insertions, 81 deletions
diff --git a/src/cmd/cgo/internal/testshared/shared_test.go b/src/cmd/cgo/internal/testshared/shared_test.go index dc880dd2c7..796c46b9bf 100644 --- a/src/cmd/cgo/internal/testshared/shared_test.go +++ b/src/cmd/cgo/internal/testshared/shared_test.go @@ -731,6 +731,10 @@ func TestThreeGopathShlibs(t *testing.T) { func requireGccgo(t *testing.T) { t.Helper() + if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { + t.Skip("gccgo test skipped on PPC64 until issue #60798 is resolved") + } + gccgoName := os.Getenv("GCCGO") if gccgoName == "" { gccgoName = "gccgo" @@ -748,7 +752,7 @@ func requireGccgo(t *testing.T) { if dot > 0 { output = output[:dot] } - major, err := strconv.Atoi(string(output)) + major, err := strconv.Atoi(strings.TrimSpace(string(output))) if err != nil { t.Skipf("can't parse gccgo version number %s", output) } diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 74583ca903..d5d4290f59 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -70,6 +70,18 @@ func TestStdLib(t *testing.T) { filepath.Join(goroot, "src"), filepath.Join(goroot, "misc"), } { + if filepath.Base(dir) == "misc" { + // cmd/distpack deletes GOROOT/misc, so skip that directory if it isn't present. + // cmd/distpack also requires GOROOT/VERSION to exist, so use that to + // suppress false-positive skips. + if _, err := os.Stat(dir); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil { + fmt.Printf("%s not present; skipping\n", dir) + continue + } + } + } + walkDirs(t, dir, func(filename string) { if skipRx != nil && skipRx.MatchString(filename) { // Always report skipped files since regexp diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go index 425724426a..2d25f80473 100644 --- a/src/cmd/compile/internal/typecheck/expr.go +++ b/src/cmd/compile/internal/typecheck/expr.go @@ -184,13 +184,6 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) } } - if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) { - if constant.Sign(r.Val()) == 0 { - base.Errorf("division by zero") - return l, r, nil - } - } - return l, r, t } diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go index 9a03526b68..ee852f5c4c 100644 --- a/src/cmd/compile/internal/types2/stdlib_test.go +++ b/src/cmd/compile/internal/types2/stdlib_test.go @@ -206,6 +206,14 @@ func firstComment(filename string) (first string) { func testTestDir(t *testing.T, path string, ignore ...string) { files, err := os.ReadDir(path) if err != nil { + // cmd/distpack deletes GOROOT/test, so skip the test if it isn't present. + // cmd/distpack also requires GOROOT/VERSION to exist, so use that to + // suppress false-positive skips. + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "test")); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil { + t.Skipf("skipping: GOROOT/test not present") + } + } t.Fatal(err) } diff --git a/src/cmd/distpack/archive.go b/src/cmd/distpack/archive.go index 730233765c..f731b3792f 100644 --- a/src/cmd/distpack/archive.go +++ b/src/cmd/distpack/archive.go @@ -92,7 +92,7 @@ func (a *Archive) Add(name, src string, info fs.FileInfo) { } // Sort sorts the files in the archive. -// It is only necessary to call Sort after calling Add. +// It is only necessary to call Sort after calling Add or RenameGoMod. // ArchiveDir returns a sorted archive, and the other methods // preserve the sorting of the archive. func (a *Archive) Sort() { @@ -164,6 +164,16 @@ func (a *Archive) SetTime(t time.Time) { } } +// RenameGoMod renames the go.mod files in the archive to _go.mod, +// for use with the module form, which cannot contain other go.mod files. +func (a *Archive) RenameGoMod() { + for i, f := range a.Files { + if strings.HasSuffix(f.Name, "/go.mod") { + a.Files[i].Name = strings.TrimSuffix(f.Name, "go.mod") + "_go.mod" + } + } +} + func amatch(pattern, name string) (bool, error) { // firstN returns the prefix of name corresponding to the first n path elements. // If n <= 0, firstN returns the entire name. diff --git a/src/cmd/distpack/pack.go b/src/cmd/distpack/pack.go index cddbd0747d..6867ac17c2 100644 --- a/src/cmd/distpack/pack.go +++ b/src/cmd/distpack/pack.go @@ -14,6 +14,17 @@ // A cross-compiled distribution for goos/goarch can be built using: // // GOOS=goos GOARCH=goarch ./make.bash -distpack +// +// To test that the module downloads are usable with the go command: +// +// ./make.bash -distpack +// mkdir -p /tmp/goproxy/golang.org/toolchain/ +// ln -sf $(pwd)/../pkg/distpack /tmp/goproxy/golang.org/toolchain/@v +// GOPROXY=file:///tmp/goproxy GOTOOLCHAIN=$(sed 1q ../VERSION) gotip version +// +// gotip can be replaced with an older released Go version once there is one. +// It just can't be the one make.bash built, because it knows it is already that +// version and will skip the download. package main import ( @@ -199,6 +210,8 @@ func main() { ) modVers := modVersionPrefix + "-" + version + "." + goosDashGoarch modArch.AddPrefix(modPath + "@" + modVers) + modArch.RenameGoMod() + modArch.Sort() testMod(modArch) // distpack returns the full path to name in the distpack directory. @@ -235,6 +248,8 @@ func mode(name string, _ fs.FileMode) fs.FileMode { strings.HasSuffix(name, ".pl") || strings.HasSuffix(name, ".rc") { return 0o755 + } else if ok, _ := amatch("**/go_?*_?*_exec", name); ok { + return 0o755 } return 0o644 } diff --git a/src/cmd/distpack/test.go b/src/cmd/distpack/test.go index 93c6564594..4544d72d1f 100644 --- a/src/cmd/distpack/test.go +++ b/src/cmd/distpack/test.go @@ -95,6 +95,10 @@ var modRules = []testRule{ {name: "golang.org/toolchain@*/pkg/tool/*/compile", goos: "darwin"}, {name: "golang.org/toolchain@*/pkg/tool/*/compile", goos: "windows", exclude: true}, {name: "golang.org/toolchain@*/pkg/tool/*/compile.exe", goos: "windows"}, + + // go.mod are renamed to _go.mod. + {name: "**/go.mod", exclude: true}, + {name: "**/_go.mod"}, } func testSrc(a *Archive) { diff --git a/src/cmd/go/go_unix_test.go b/src/cmd/go/go_unix_test.go index bab9494401..d04e496778 100644 --- a/src/cmd/go/go_unix_test.go +++ b/src/cmd/go/go_unix_test.go @@ -2,12 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +//go:build unix package main_test import ( + "bufio" + "context" + "internal/testenv" + "io" "os" + "os/exec" + "slices" + "strings" "syscall" "testing" ) @@ -33,3 +40,80 @@ func TestGoBuildUmask(t *testing.T) { t.Fatalf("wrote x with mode=%v, wanted no 0077 bits", mode) } } + +// TestTestInterrupt verifies the fix for issue #60203. +// +// If the whole process group for a 'go test' invocation receives +// SIGINT (as would be sent by pressing ^C on a console), +// it should return quickly, not deadlock. +func TestTestInterrupt(t *testing.T) { + if testing.Short() { + t.Skipf("skipping in short mode: test executes many subprocesses") + } + // Don't run this test in parallel, for the same reason. + + tg := testgo(t) + defer tg.cleanup() + tg.setenv("GOROOT", testGOROOT) + + ctx, cancel := context.WithCancel(context.Background()) + cmd := testenv.CommandContext(t, ctx, tg.goTool(), "test", "std", "-short", "-count=1") + cmd.Dir = tg.execDir + + // Override $TMPDIR when running the tests: since we're terminating the tests + // with a signal they might fail to clean up some temp files, and we don't + // want that to cause an "unexpected files" failure at the end of the run. + cmd.Env = append(slices.Clip(tg.env), tempEnvName()+"="+t.TempDir()) + + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + cmd.Cancel = func() error { + pgid := cmd.Process.Pid + return syscall.Kill(-pgid, syscall.SIGINT) + } + + pipe, err := cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + + t.Logf("running %v", cmd) + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + stdout := new(strings.Builder) + r := bufio.NewReader(pipe) + line, err := r.ReadString('\n') + if err != nil { + t.Fatal(err) + } + stdout.WriteString(line) + + // The output line for some test was written, so we know things are in progress. + // + // Cancel the rest of the run by sending SIGINT to the process group: + // it should finish up and exit with a nonzero status, + // not have to be killed with SIGKILL. + cancel() + + io.Copy(stdout, r) + if stdout.Len() > 0 { + t.Logf("stdout:\n%s", stdout) + } + err = cmd.Wait() + + ee, _ := err.(*exec.ExitError) + if ee == nil { + t.Fatalf("unexpectedly finished with nonzero status") + } + if len(ee.Stderr) > 0 { + t.Logf("stderr:\n%s", ee.Stderr) + } + if !ee.Exited() { + t.Fatalf("'go test' did not exit after interrupt: %v", err) + } + + t.Logf("interrupted tests without deadlocking") +} diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 1addadfea0..79120e6a99 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.go @@ -730,6 +730,9 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p)) } } + if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + load.PrepareForCoverageBuild(pkgs) + } b.Do(ctx, a) } diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go index 50f4bb2b37..8fe432a9f5 100644 --- a/src/cmd/go/internal/modfetch/coderepo.go +++ b/src/cmd/go/internal/modfetch/coderepo.go @@ -1013,7 +1013,7 @@ func (r *codeRepo) retractedVersions(ctx context.Context) (func(string) bool, er if err != nil { return nil, err } - retractions := make([]modfile.VersionInterval, len(f.Retract)) + retractions := make([]modfile.VersionInterval, 0, len(f.Retract)) for _, r := range f.Retract { retractions = append(retractions, r.VersionInterval) } diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go index 6e60e7d976..ea7d561d7b 100644 --- a/src/cmd/go/internal/modfetch/sumdb.go +++ b/src/cmd/go/internal/modfetch/sumdb.go @@ -34,12 +34,34 @@ import ( // useSumDB reports whether to use the Go checksum database for the given module. func useSumDB(mod module.Version) bool { if mod.Path == "golang.org/toolchain" { + must := true // Downloaded toolchains cannot be listed in go.sum, // so we require checksum database lookups even if // GOSUMDB=off or GONOSUMDB matches the pattern. // If GOSUMDB=off, then the eventual lookup will fail // with a good error message. - return true + + // Exception #1: using GOPROXY=file:// to test a distpack. + if strings.HasPrefix(cfg.GOPROXY, "file://") && !strings.ContainsAny(cfg.GOPROXY, ",|") { + must = false + } + // Exception #2: the Go proxy+checksum database cannot check itself + // while doing the initial download. + if strings.Contains(os.Getenv("GIT_HTTP_USER_AGENT"), "proxy.golang.org") { + must = false + } + + // Another potential exception would be GOPROXY=direct, + // but that would make toolchain downloads only as secure + // as HTTPS, and in particular they'd be susceptible to MITM + // attacks on systems with less-than-trustworthy root certificates. + // The checksum database provides a stronger guarantee, + // so we don't make that exception. + + // Otherwise, require the checksum database. + if must { + return true + } } return cfg.GOSUMDB != "off" && !module.MatchPrefixPatterns(cfg.GONOSUMDB, mod.Path) } diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index 238355732c..83d5faf28f 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -160,7 +160,7 @@ func GetModule(modroot string) (*Module, error) { return nil, errNotFromModuleCache } modroot = filepath.Clean(modroot) - if !str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) { + if str.HasFilePathPrefix(modroot, cfg.GOROOTsrc) || !str.HasFilePathPrefix(modroot, cfg.GOMODCACHE) { return nil, errNotFromModuleCache } return openIndexModule(modroot, true) diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 2ce4c1a28e..995da15c90 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -1217,7 +1217,15 @@ func (lockedStdout) Write(b []byte) (int, error) { func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) error { // Wait for previous test to get started and print its first json line. - <-r.prev + select { + case <-r.prev: + case <-base.Interrupted: + // We can't wait for the previous test action to complete: we don't start + // new actions after an interrupt, so if that action wasn't already running + // it might never happen. Instead, just don't log anything for this action. + base.SetExitStatus(1) + return nil + } if a.Failed { // We were unable to build the binary. diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go index 8eac03b339..8b1a0b94be 100644 --- a/src/cmd/go/internal/toolchain/select.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -245,6 +245,8 @@ var TestVersionSwitch string func Exec(gotoolchain string) { log.SetPrefix("go: ") + writeBits = sysWriteBits() + count, _ := strconv.Atoi(os.Getenv(countEnv)) if count >= maxSwitch-10 { fmt.Fprintf(os.Stderr, "go: switching from go%v to %v [depth %d]\n", gover.Local(), gotoolchain, count) @@ -357,10 +359,101 @@ func Exec(gotoolchain string) { } } + srcUGoMod := filepath.Join(dir, "src/_go.mod") + srcGoMod := filepath.Join(dir, "src/go.mod") + if size(srcGoMod) != size(srcUGoMod) { + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if path == srcUGoMod { + // Leave for last, in case we are racing with another go command. + return nil + } + if pdir, name := filepath.Split(path); name == "_go.mod" { + if err := raceSafeCopy(path, pdir+"go.mod"); err != nil { + return err + } + } + return nil + }) + // Handle src/go.mod; this is the signal to other racing go commands + // that everything is okay and they can skip this step. + if err == nil { + err = raceSafeCopy(srcUGoMod, srcGoMod) + } + if err != nil { + base.Fatalf("download %s: %v", gotoolchain, err) + } + } + // Reinvoke the go command. execGoToolchain(gotoolchain, dir, filepath.Join(dir, "bin/go")) } +func size(path string) int64 { + info, err := os.Stat(path) + if err != nil { + return -1 + } + return info.Size() +} + +var writeBits fs.FileMode + +// raceSafeCopy copies the file old to the file new, being careful to ensure +// that if multiple go commands call raceSafeCopy(old, new) at the same time, +// they don't interfere with each other: both will succeed and return and +// later observe the correct content in new. Like in the build cache, we arrange +// this by opening new without truncation and then writing the content. +// Both go commands can do this simultaneously and will write the same thing +// (old never changes content). +func raceSafeCopy(old, new string) error { + oldInfo, err := os.Stat(old) + if err != nil { + return err + } + newInfo, err := os.Stat(new) + if err == nil && newInfo.Size() == oldInfo.Size() { + return nil + } + data, err := os.ReadFile(old) + if err != nil { + return err + } + // The module cache has unwritable directories by default. + // Restore the user write bit in the directory so we can create + // the new go.mod file. We clear it again at the end on a + // best-effort basis (ignoring failures). + dir := filepath.Dir(old) + info, err := os.Stat(dir) + if err != nil { + return err + } + if err := os.Chmod(dir, info.Mode()|writeBits); err != nil { + return err + } + defer os.Chmod(dir, info.Mode()) + // Note: create the file writable, so that a racing go command + // doesn't get an error before we store the actual data. + f, err := os.OpenFile(new, os.O_CREATE|os.O_WRONLY, writeBits&^0o111) + if err != nil { + // If OpenFile failed because a racing go command completed our work + // (and then OpenFile failed because the directory or file is now read-only), + // count that as a success. + if size(old) == size(new) { + return nil + } + return err + } + defer os.Chmod(new, oldInfo.Mode()) + if _, err := f.Write(data); err != nil { + f.Close() + return err + } + return f.Close() +} + // modGoToolchain finds the enclosing go.work or go.mod file // and returns the go version and toolchain lines from the file. // The toolchain line overrides the version line diff --git a/src/cmd/go/internal/toolchain/umask_none.go b/src/cmd/go/internal/toolchain/umask_none.go new file mode 100644 index 0000000000..b092fe8b7d --- /dev/null +++ b/src/cmd/go/internal/toolchain/umask_none.go @@ -0,0 +1,13 @@ +// Copyright 2023 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. + +//go:build !(darwin || freebsd || linux || netbsd || openbsd) + +package toolchain + +import "io/fs" + +func sysWriteBits() fs.FileMode { + return 0700 +} diff --git a/src/cmd/go/internal/toolchain/umask_unix.go b/src/cmd/go/internal/toolchain/umask_unix.go new file mode 100644 index 0000000000..cbe4307311 --- /dev/null +++ b/src/cmd/go/internal/toolchain/umask_unix.go @@ -0,0 +1,28 @@ +// Copyright 2023 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. + +//go:build darwin || freebsd || linux || netbsd || openbsd + +package toolchain + +import ( + "io/fs" + "syscall" +) + +// sysWriteBits determines which bits to OR into the mode to make a directory writable. +// It must be called when there are no other file system operations happening. +func sysWriteBits() fs.FileMode { + // Read current umask. There's no way to read it without also setting it, + // so set it conservatively and then restore the original one. + m := syscall.Umask(0o777) + syscall.Umask(m) // restore bits + if m&0o22 == 0o22 { // group and world are unwritable by default + return 0o700 + } + if m&0o2 == 0o2 { // group is writable by default, but not world + return 0o770 + } + return 0o777 // everything is writable by default +} diff --git a/src/cmd/go/testdata/script/cover_list.txt b/src/cmd/go/testdata/script/cover_list.txt index c66c087793..6b8aaf45d1 100644 --- a/src/cmd/go/testdata/script/cover_list.txt +++ b/src/cmd/go/testdata/script/cover_list.txt @@ -16,6 +16,28 @@ go install m/example # with -cover. stale -cover m/example +# Collect build ID from for m/example built with -cover. +go list -cover -export -f '{{.BuildID}}' m/example +cp stdout $WORK/listbuildid.txt + +# Now build the m/example binary with coverage. +go build -cover -o $WORK/m.exe m/example + +# Ask for the binary build ID by running "go tool buildid". +go tool buildid $WORK/m.exe +cp stdout $WORK/rawtoolbuildid.txt + +# Make sure that the two build IDs agree with respect to the +# m/example package. Build IDs from binaries are of the form X/Y/Z/W +# where Y/Z is the package build ID; running the program below will +# pick out the parts of the ID that we want. +env GOCOVERDIR=$WORK +exec $WORK/m.exe $WORK/rawtoolbuildid.txt +cp stdout $WORK/toolbuildid.txt + +# Build IDs should match here. +cmp $WORK/toolbuildid.txt $WORK/listbuildid.txt + -- go.mod -- module m @@ -23,6 +45,21 @@ go 1.20 -- example/main.go -- package main +import ( + "fmt" + "os" + "strings" +) + func main() { - println("hi mom") + println(os.Args[1]) + content, err := os.ReadFile(os.Args[1]) + if err != nil { + os.Exit(1) + } + fields := strings.Split(strings.TrimSpace(string(content)), "/") + if len(fields) != 4 { + os.Exit(2) + } + fmt.Println(fields[1] + "/" + fields[2]) } diff --git a/src/cmd/internal/moddeps/moddeps_test.go b/src/cmd/internal/moddeps/moddeps_test.go index af3f99b801..ae890b66cb 100644 --- a/src/cmd/internal/moddeps/moddeps_test.go +++ b/src/cmd/internal/moddeps/moddeps_test.go @@ -504,7 +504,7 @@ func findGorootModules(t *testing.T) []gorootModule { knownGOROOTModules := [...]string{ "std", "cmd", - "misc", + // The "misc" module sometimes exists, but cmd/distpack intentionally removes it. } var seen = make(map[string]bool) // Key is module path. for _, m := range goroot.modules { diff --git a/src/cmd/internal/testdir/testdir_test.go b/src/cmd/internal/testdir/testdir_test.go index f5bd44eef2..bd7785900c 100644 --- a/src/cmd/internal/testdir/testdir_test.go +++ b/src/cmd/internal/testdir/testdir_test.go @@ -64,7 +64,7 @@ var ( // dirs are the directories to look for *.go files in. // TODO(bradfitz): just use all directories? - dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky"} + dirs = []string{".", "ken", "chan", "interface", "syntax", "dwarf", "fixedbugs", "codegen", "runtime", "abi", "typeparam", "typeparam/mdempsky", "arenas"} ) // Test is the main entrypoint that runs tests in the GOROOT/test directory. @@ -117,6 +117,15 @@ func Test(t *testing.T) { runoutputGate: make(chan bool, *runoutputLimit), } + // cmd/distpack deletes GOROOT/test, so skip the test if it isn't present. + // cmd/distpack also requires GOROOT/VERSION to exist, so use that to + // suppress false-positive skips. + if _, err := os.Stat(common.gorootTestDir); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil { + t.Skipf("skipping: GOROOT/test not present") + } + } + for _, dir := range dirs { for _, goFile := range goFiles(t, dir) { test := test{testCommon: common, dir: dir, goFile: goFile} diff --git a/src/compress/bzip2/bit_reader.go b/src/compress/bzip2/bit_reader.go index ab1d606514..b451265475 100644 --- a/src/compress/bzip2/bit_reader.go +++ b/src/compress/bzip2/bit_reader.go @@ -60,7 +60,7 @@ func (br *bitReader) ReadBits64(bits uint) (n uint64) { // |------------| // br.bits (num valid bits) // - // This the next line right shifts the desired bits into the + // The next line right shifts the desired bits into the // least-significant places and masks off anything above. n = (br.n >> (br.bits - bits)) & ((1 << bits) - 1) br.bits -= bits diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go index 9ab29f3b1c..25ff402277 100644 --- a/src/go/internal/gcimporter/gcimporter_test.go +++ b/src/go/internal/gcimporter/gcimporter_test.go @@ -137,6 +137,16 @@ func TestImportTypeparamTests(t *testing.T) { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) } + // cmd/distpack removes the GOROOT/test directory, so skip if it isn't there. + // cmd/distpack also requires the presence of GOROOT/VERSION, so use that to + // avoid false-positive skips. + gorootTest := filepath.Join(testenv.GOROOT(t), "test") + if _, err := os.Stat(gorootTest); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil { + t.Skipf("skipping: GOROOT/test not present") + } + } + testenv.MustHaveGoBuild(t) tmpdir := mktmpdir(t) @@ -144,7 +154,7 @@ func TestImportTypeparamTests(t *testing.T) { // Check go files in test/typeparam, except those that fail for a known // reason. - rootDir := filepath.Join(testenv.GOROOT(t), "test", "typeparam") + rootDir := filepath.Join(gorootTest, "typeparam") list, err := os.ReadDir(rootDir) if err != nil { t.Fatal(err) diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index 770d3bf52a..07c9222537 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -209,6 +209,14 @@ func firstComment(filename string) string { func testTestDir(t *testing.T, path string, ignore ...string) { files, err := os.ReadDir(path) if err != nil { + // cmd/distpack deletes GOROOT/test, so skip the test if it isn't present. + // cmd/distpack also requires GOROOT/VERSION to exist, so use that to + // suppress false-positive skips. + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "test")); os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil { + t.Skipf("skipping: GOROOT/test not present") + } + } t.Fatal(err) } diff --git a/src/internal/bisect/bisect.go b/src/internal/bisect/bisect.go index 37f76a4271..48c796e54a 100644 --- a/src/internal/bisect/bisect.go +++ b/src/internal/bisect/bisect.go @@ -198,10 +198,20 @@ func New(pattern string) (*Matcher, error) { m := new(Matcher) - // Allow multiple v, so that “bisect cmd vPATTERN” can force verbose all the time. p := pattern + // Special case for leading 'q' so that 'qn' quietly disables, e.g. fmahash=qn to disable fma + // Any instance of 'v' disables 'q'. + if len(p) > 0 && p[0] == 'q' { + m.quiet = true + p = p[1:] + if p == "" { + return nil, &parseError{"invalid pattern syntax: " + pattern} + } + } + // Allow multiple v, so that “bisect cmd vPATTERN” can force verbose all the time. for len(p) > 0 && p[0] == 'v' { m.verbose = true + m.quiet = false p = p[1:] if p == "" { return nil, &parseError{"invalid pattern syntax: " + pattern} @@ -297,7 +307,8 @@ func New(pattern string) (*Matcher, error) { // A Matcher is the parsed, compiled form of a PATTERN string. // The nil *Matcher is valid: it has all changes enabled but none reported. type Matcher struct { - verbose bool + verbose bool // annotate reporting with human-helpful information + quiet bool // disables all reporting. reset if verbose is true. use case is -d=fmahash=qn enable bool // when true, list is for “enable and report” (when false, “disable and report”) list []cond // conditions; later ones win over earlier ones dedup atomicPointerDedup @@ -339,20 +350,19 @@ func (m *Matcher) ShouldEnable(id uint64) bool { if m == nil { return true } - for i := len(m.list) - 1; i >= 0; i-- { - c := &m.list[i] - if id&c.mask == c.bits { - return c.result == m.enable - } - } - return false == m.enable + return m.matchResult(id) == m.enable } // ShouldPrint reports whether to print identifying information about the change with the given id. func (m *Matcher) ShouldPrint(id uint64) bool { - if m == nil { + if m == nil || m.quiet { return false } + return m.matchResult(id) +} + +// matchResult returns the result from the first condition that matches id. +func (m *Matcher) matchResult(id uint64) bool { for i := len(m.list) - 1; i >= 0; i-- { c := &m.list[i] if id&c.mask == c.bits { diff --git a/src/math/dim.go b/src/math/dim.go index 6a286cdc75..f369f70f00 100644 --- a/src/math/dim.go +++ b/src/math/dim.go @@ -34,6 +34,9 @@ func Dim(x, y float64) float64 { // Max(x, NaN) = Max(NaN, x) = NaN // Max(+0, ±0) = Max(±0, +0) = +0 // Max(-0, -0) = -0 +// +// Note that this differs from the built-in function max when called +// with NaN and +Inf. func Max(x, y float64) float64 { if haveArchMax { return archMax(x, y) @@ -67,6 +70,9 @@ func max(x, y float64) float64 { // Min(x, -Inf) = Min(-Inf, x) = -Inf // Min(x, NaN) = Min(NaN, x) = NaN // Min(-0, ±0) = Min(±0, -0) = -0 +// +// Note that this differs from the built-in function min when called +// with NaN and -Inf. func Min(x, y float64) float64 { if haveArchMin { return archMin(x, y) diff --git a/src/net/http/roundtrip_js.go b/src/net/http/roundtrip_js.go index f4d0b9d44c..2826383ce1 100644 --- a/src/net/http/roundtrip_js.go +++ b/src/net/http/roundtrip_js.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "strconv" + "strings" "syscall/js" ) @@ -44,11 +45,15 @@ const jsFetchRedirect = "js.fetch:redirect" // the browser globals. var jsFetchMissing = js.Global().Get("fetch").IsUndefined() -// jsFetchDisabled will be true if the "process" global is present. -// We use this as an indicator that we're running in Node.js. We -// want to disable the Fetch API in Node.js because it breaks -// our wasm tests. See https://go.dev/issue/57613 for more information. -var jsFetchDisabled = !js.Global().Get("process").IsUndefined() +// jsFetchDisabled controls whether the use of Fetch API is disabled. +// It's set to true when we detect we're running in Node.js, so that +// RoundTrip ends up talking over the same fake network the HTTP servers +// currently use in various tests and examples. See go.dev/issue/57613. +// +// TODO(go.dev/issue/60810): See if it's viable to test the Fetch API +// code path. +var jsFetchDisabled = js.Global().Get("process").Type() == js.TypeObject && + strings.HasPrefix(js.Global().Get("process").Get("argv0").String(), "node") // Determine whether the JS runtime supports streaming request bodies. // Courtesy: https://developer.chrome.com/articles/fetch-streaming-requests/#feature-detection @@ -130,7 +135,6 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { } opt.Set("headers", headers) - var readableStreamStart, readableStreamPull, readableStreamCancel js.Func if req.Body != nil { if !supportsPostRequestStreams() { body, err := io.ReadAll(req.Body) @@ -138,6 +142,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { req.Body.Close() // RoundTrip must always close the body, including on errors. return nil, err } + req.Body.Close() if len(body) != 0 { buf := uint8Array.New(len(body)) js.CopyBytesToJS(buf, body) @@ -148,7 +153,7 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { readableStreamCtorArg.Set("type", "bytes") readableStreamCtorArg.Set("autoAllocateChunkSize", t.writeBufferSize()) - readableStreamPull = js.FuncOf(func(this js.Value, args []js.Value) any { + readableStreamPull := js.FuncOf(func(this js.Value, args []js.Value) any { controller := args[0] byobRequest := controller.Get("byobRequest") if byobRequest.IsNull() { @@ -176,6 +181,10 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { // Note: This a return from the pull callback of the controller and *not* RoundTrip(). return nil }) + defer func() { + readableStreamPull.Release() + req.Body.Close() + }() readableStreamCtorArg.Set("pull", readableStreamPull) opt.Set("body", js.Global().Get("ReadableStream").New(readableStreamCtorArg)) @@ -196,11 +205,6 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { success = js.FuncOf(func(this js.Value, args []js.Value) any { success.Release() failure.Release() - readableStreamCancel.Release() - readableStreamPull.Release() - readableStreamStart.Release() - - req.Body.Close() result := args[0] header := Header{} @@ -265,11 +269,6 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) { failure = js.FuncOf(func(this js.Value, args []js.Value) any { success.Release() failure.Release() - readableStreamCancel.Release() - readableStreamPull.Release() - readableStreamStart.Release() - - req.Body.Close() err := args[0] // The error is a JS Error type diff --git a/src/net/http/server.go b/src/net/http/server.go index 680c5f68f4..8f63a90299 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -1856,7 +1856,9 @@ func isCommonNetReadError(err error) bool { // Serve a new connection. func (c *conn) serve(ctx context.Context) { - c.remoteAddr = c.rwc.RemoteAddr().String() + if ra := c.rwc.RemoteAddr(); ra != nil { + c.remoteAddr = ra.String() + } ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) var inFlightResponse *response defer func() { diff --git a/src/os/exec.go b/src/os/exec.go index d01ca592ba..ed5a75c4d1 100644 --- a/src/os/exec.go +++ b/src/os/exec.go @@ -86,7 +86,9 @@ func Getppid() int { return syscall.Getppid() } // about the underlying operating system process. // // On Unix systems, FindProcess always succeeds and returns a Process -// for the given pid, regardless of whether the process exists. +// for the given pid, regardless of whether the process exists. To test whether +// the process actually exists, see whether p.Signal(syscall.Signal(0)) reports +// an error. func FindProcess(pid int) (*Process, error) { return findProcess(pid) } diff --git a/src/os/exec_unix_test.go b/src/os/exec_unix_test.go index 82c072a746..26045192ff 100644 --- a/src/os/exec_unix_test.go +++ b/src/os/exec_unix_test.go @@ -9,6 +9,7 @@ package os_test import ( "internal/testenv" . "os" + "syscall" "testing" ) @@ -25,3 +26,20 @@ func TestErrProcessDone(t *testing.T) { t.Errorf("got %v want %v", got, ErrProcessDone) } } + +func TestUNIXProcessAlive(t *testing.T) { + testenv.MustHaveGoBuild(t) + t.Parallel() + + p, err := StartProcess(testenv.GoToolPath(t), []string{"sleep", "1"}, &ProcAttr{}) + if err != nil { + t.Skipf("starting test process: %v", err) + } + defer p.Kill() + + proc, _ := FindProcess(p.Pid) + err = proc.Signal(syscall.Signal(0)) + if err != nil { + t.Errorf("OS reported error for running process: %v", err) + } +} diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index 469a107d14..3c78e415d2 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -1622,36 +1622,33 @@ func TestBug3486(t *testing.T) { // https://golang.org/issue/3486 if runtime.GOOS == "ios" { t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) } - root, err := filepath.EvalSymlinks(testenv.GOROOT(t) + "/test") - if err != nil { - t.Fatal(err) - } - bugs := filepath.Join(root, "fixedbugs") - ken := filepath.Join(root, "ken") - seenBugs := false - seenKen := false - err = filepath.Walk(root, func(pth string, info fs.FileInfo, err error) error { + root := filepath.Join(testenv.GOROOT(t), "src", "unicode") + utf16 := filepath.Join(root, "utf16") + utf8 := filepath.Join(root, "utf8") + seenUTF16 := false + seenUTF8 := false + err := filepath.Walk(root, func(pth string, info fs.FileInfo, err error) error { if err != nil { t.Fatal(err) } switch pth { - case bugs: - seenBugs = true + case utf16: + seenUTF16 = true return filepath.SkipDir - case ken: - if !seenBugs { - t.Fatal("filepath.Walk out of order - ken before fixedbugs") + case utf8: + if !seenUTF16 { + t.Fatal("filepath.Walk out of order - utf8 before utf16") } - seenKen = true + seenUTF8 = true } return nil }) if err != nil { t.Fatal(err) } - if !seenKen { - t.Fatalf("%q not seen", ken) + if !seenUTF8 { + t.Fatalf("%q not seen", utf8) } } diff --git a/src/reflect/arena.go b/src/reflect/arena.go index 694a3a136c..cac1a1da5e 100644 --- a/src/reflect/arena.go +++ b/src/reflect/arena.go @@ -12,7 +12,7 @@ import "arena" // specified type, allocating storage for it in the provided arena. That is, // the returned Value's Type is PointerTo(typ). func ArenaNew(a *arena.Arena, typ Type) Value { - return ValueOf(arena_New(a, typ)) + return ValueOf(arena_New(a, PointerTo(typ))) } func arena_New(a *arena.Arena, typ any) any diff --git a/src/runtime/race/race_linux_test.go b/src/runtime/race/race_linux_test.go index e8a2d0fd8c..947ed7ca39 100644 --- a/src/runtime/race/race_linux_test.go +++ b/src/runtime/race/race_linux_test.go @@ -35,3 +35,31 @@ func TestAtomicMmap(t *testing.T) { t.Fatalf("bad atomic value: %v, want 2", *a) } } + +func TestAtomicPageBoundary(t *testing.T) { + // Test that atomic access near (but not cross) a page boundary + // doesn't fault. See issue 60825. + + // Mmap two pages of memory, and make the second page inaccessible, + // so we have an address at the end of a page. + pagesize := syscall.Getpagesize() + b, err := syscall.Mmap(0, 0, 2*pagesize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE) + if err != nil { + t.Fatalf("mmap failed %s", err) + } + defer syscall.Munmap(b) + err = syscall.Mprotect(b[pagesize:], syscall.PROT_NONE) + if err != nil { + t.Fatalf("mprotect high failed %s\n", err) + } + + // This should not fault. + a := (*uint32)(unsafe.Pointer(&b[pagesize-4])) + atomic.StoreUint32(a, 1) + if x := atomic.LoadUint32(a); x != 1 { + t.Fatalf("bad atomic value: %v, want 1", x) + } + if x := atomic.AddUint32(a, 1); x != 2 { + t.Fatalf("bad atomic value: %v, want 2", x) + } +} diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 0697be7180..4fa130e861 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -333,7 +333,7 @@ TEXT sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-25 TEXT racecallatomic<>(SB), NOSPLIT|NOFRAME, $0-0 // Trigger SIGSEGV early. MOVQ 16(SP), R12 - MOVL (R12), R13 + MOVBLZX (R12), R13 // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). CMPQ R12, runtime·racearenastart(SB) JB racecallatomic_data diff --git a/src/runtime/race_arm64.s b/src/runtime/race_arm64.s index edbb3b12c7..c818345852 100644 --- a/src/runtime/race_arm64.s +++ b/src/runtime/race_arm64.s @@ -348,7 +348,7 @@ TEXT racecallatomic<>(SB), NOSPLIT, $0 // Trigger SIGSEGV early. MOVD 40(RSP), R3 // 1st arg is addr. after two times BL, get it at 40(RSP) - MOVD (R3), R13 // segv here if addr is bad + MOVB (R3), R13 // segv here if addr is bad // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). MOVD runtime·racearenastart(SB), R10 CMP R10, R3 diff --git a/src/runtime/race_ppc64le.s b/src/runtime/race_ppc64le.s index 5fd4f785c8..39cfffc39b 100644 --- a/src/runtime/race_ppc64le.s +++ b/src/runtime/race_ppc64le.s @@ -363,7 +363,7 @@ TEXT sync∕atomic·CompareAndSwapUintptr(SB), NOSPLIT, $0-25 TEXT racecallatomic<>(SB), NOSPLIT, $0-0 // Trigger SIGSEGV early if address passed to atomic function is bad. MOVD (R6), R7 // 1st arg is addr - MOVD (R7), R9 // segv here if addr is bad + MOVB (R7), R9 // segv here if addr is bad // Check that addr is within [arenastart, arenaend) or within [racedatastart, racedataend). MOVD runtime·racearenastart(SB), R9 CMP R7, R9 diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go index f2ef0dd31d..2666d14201 100644 --- a/src/runtime/trace/annotation.go +++ b/src/runtime/trace/annotation.go @@ -21,7 +21,7 @@ type traceContextKey struct{} // like the Go execution tracer may assume there are only a bounded // number of unique task types in the system. // -// The returned Task's End method is used to mark the task's end. +// The returned Task's [Task.End] method is used to mark the task's end. // The trace tool measures task latency as the time between task creation // and when the End method is called, and provides the latency // distribution per task type. @@ -75,7 +75,7 @@ type Task struct { // TODO(hyangah): record parent id? } -// End marks the end of the operation represented by the Task. +// End marks the end of the operation represented by the [Task]. func (t *Task) End() { userTaskEnd(t.id) } @@ -97,7 +97,7 @@ func Log(ctx context.Context, category, message string) { userLog(id, category, message) } -// Logf is like Log, but the value is formatted using the specified format spec. +// Logf is like [Log], but the value is formatted using the specified format spec. func Logf(ctx context.Context, category, format string, args ...any) { if IsEnabled() { // Ideally this should be just Log, but that will @@ -142,7 +142,7 @@ func WithRegion(ctx context.Context, regionType string, fn func()) { } // StartRegion starts a region and returns it. -// The returned Region's End method must be called +// The returned Region's [Region.End] method must be called // from the same goroutine where the region was started. // Within each goroutine, regions must nest. That is, regions started // after this region must be ended before this region can be ended. diff --git a/src/runtime/trace/trace.go b/src/runtime/trace/trace.go index 86c97e2a11..935d222f02 100644 --- a/src/runtime/trace/trace.go +++ b/src/runtime/trace/trace.go @@ -33,7 +33,7 @@ // // import _ "net/http/pprof" // -// See the net/http/pprof package for more details about all of the +// See the [net/http/pprof] package for more details about all of the // debug endpoints installed by this import. // // # User annotation @@ -44,11 +44,11 @@ // There are three types of user annotations: log messages, regions, // and tasks. // -// Log emits a timestamped message to the execution trace along with +// [Log] emits a timestamped message to the execution trace along with // additional information such as the category of the message and -// which goroutine called Log. The execution tracer provides UIs to filter +// which goroutine called [Log]. The execution tracer provides UIs to filter // and group goroutines using the log category and the message supplied -// in Log. +// in [Log]. // // A region is for logging a time interval during a goroutine's execution. // By definition, a region starts and ends in the same goroutine. @@ -72,10 +72,10 @@ // operations such as an RPC request, an HTTP request, or an // interesting local operation which may require multiple goroutines // working together. Since tasks can involve multiple goroutines, -// they are tracked via a context.Context object. NewTask creates -// a new task and embeds it in the returned context.Context object. +// they are tracked via a [context.Context] object. [NewTask] creates +// a new task and embeds it in the returned [context.Context] object. // Log messages and regions are attached to the task, if any, in the -// Context passed to Log and WithRegion. +// Context passed to [Log] and [WithRegion]. // // For example, assume that we decided to froth milk, extract coffee, // and mix milk and coffee in separate goroutines. With a task, diff --git a/src/slices/sort_benchmark_test.go b/src/slices/sort_benchmark_test.go index edf29994cf..0f08842594 100644 --- a/src/slices/sort_benchmark_test.go +++ b/src/slices/sort_benchmark_test.go @@ -8,6 +8,7 @@ import ( "fmt" "math/rand" "sort" + "strconv" "strings" "testing" ) @@ -50,6 +51,15 @@ func BenchmarkSortInts(b *testing.B) { } } +func makeSortedStrings(n int) []string { + x := make([]string, n) + for i := 0; i < n; i++ { + x[i] = strconv.Itoa(i) + } + Sort(x) + return x +} + func BenchmarkSlicesSortInts(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() @@ -153,6 +163,15 @@ func BenchmarkSortStrings(b *testing.B) { } } +func BenchmarkSortStrings_Sorted(b *testing.B) { + ss := makeSortedStrings(N) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + sort.Strings(ss) + } +} + func BenchmarkSlicesSortStrings(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() @@ -162,6 +181,15 @@ func BenchmarkSlicesSortStrings(b *testing.B) { } } +func BenchmarkSlicesSortStrings_Sorted(b *testing.B) { + ss := makeSortedStrings(N) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Sort(ss) + } +} + // These benchmarks compare sorting a slice of structs with sort.Sort vs. // slices.SortFunc. type myStruct struct { diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go index ddb6080882..78b0b82640 100644 --- a/src/testing/fstest/testfs.go +++ b/src/testing/fstest/testfs.go @@ -270,7 +270,7 @@ func (t *fsTester) checkDir(dir string) { } } - t.checkGlob(dir, list) + t.checkGlob(dir, list2) } // formatEntry formats an fs.DirEntry into a string for error messages and comparison. diff --git a/src/testing/fstest/testfs_test.go b/src/testing/fstest/testfs_test.go index aefb4b3361..a48c597ff4 100644 --- a/src/testing/fstest/testfs_test.go +++ b/src/testing/fstest/testfs_test.go @@ -6,8 +6,10 @@ package fstest import ( "internal/testenv" + "io/fs" "os" "path/filepath" + "sort" "testing" ) @@ -38,3 +40,39 @@ func TestDash(t *testing.T) { t.Error(err) } } + +type shuffledFS MapFS + +func (fsys shuffledFS) Open(name string) (fs.File, error) { + f, err := MapFS(fsys).Open(name) + if err != nil { + return nil, err + } + return &shuffledFile{File: f}, nil +} + +type shuffledFile struct{ fs.File } + +func (f *shuffledFile) ReadDir(n int) ([]fs.DirEntry, error) { + dirents, err := f.File.(fs.ReadDirFile).ReadDir(n) + // Shuffle in a deterministic way, all we care about is making sure that the + // list of directory entries is not is the lexicographic order. + // + // We do this to make sure that the TestFS test suite is not affected by the + // order of directory entries. + sort.Slice(dirents, func(i, j int) bool { + return dirents[i].Name() > dirents[j].Name() + }) + return dirents, err +} + +func TestShuffledFS(t *testing.T) { + fsys := shuffledFS{ + "tmp/one": {Data: []byte("1")}, + "tmp/two": {Data: []byte("2")}, + "tmp/three": {Data: []byte("3")}, + } + if err := TestFS(fsys, "tmp/one", "tmp/two", "tmp/three"); err != nil { + t.Error(err) + } +} diff --git a/test/arenas/smoke.go b/test/arenas/smoke.go new file mode 100644 index 0000000000..56dad53fd0 --- /dev/null +++ b/test/arenas/smoke.go @@ -0,0 +1,65 @@ +// build -goexperiment arenas + +// Copyright 2023 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 ( + "arena" + "log" + "reflect" +) + +func main() { + a := arena.NewArena() + defer a.Free() + + const iValue = 10 + + i := arena.New[int](a) + *i = iValue + + if *i != iValue { + // This test doesn't reasonably expect this to fail. It's more likely + // that *i crashes for some reason. Still, why not check it. + log.Fatalf("bad i value: got %d, want %d", *i, iValue) + } + + const wantLen = 125 + const wantCap = 1912 + + sl := arena.MakeSlice[*int](a, wantLen, wantCap) + if len(sl) != wantLen { + log.Fatalf("bad arena slice length: got %d, want %d", len(sl), wantLen) + } + if cap(sl) != wantCap { + log.Fatalf("bad arena slice capacity: got %d, want %d", cap(sl), wantCap) + } + sl = sl[:cap(sl)] + for j := range sl { + sl[j] = i + } + for j := range sl { + if *sl[j] != iValue { + // This test doesn't reasonably expect this to fail. It's more likely + // that sl[j] crashes for some reason. Still, why not check it. + log.Fatalf("bad sl[j] value: got %d, want %d", *sl[j], iValue) + } + } + + t := reflect.TypeOf(int(0)) + v := reflect.ArenaNew(a, t) + if want := reflect.PointerTo(t); v.Type() != want { + log.Fatalf("unexpected type for arena-allocated value: got %s, want %s", v.Type(), want) + } + i2 := v.Interface().(*int) + *i2 = iValue + + if *i2 != iValue { + // This test doesn't reasonably expect this to fail. It's more likely + // that *i crashes for some reason. Still, why not check it. + log.Fatalf("bad i2 value: got %d, want %d", *i2, iValue) + } +} diff --git a/test/fixedbugs/issue60601.go b/test/fixedbugs/issue60601.go new file mode 100644 index 0000000000..5308989857 --- /dev/null +++ b/test/fixedbugs/issue60601.go @@ -0,0 +1,50 @@ +// run + +// Copyright 2023 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 ( + "strings" + "unsafe" +) + +func shift[T any]() int64 { + return 1 << unsafe.Sizeof(*new(T)) +} + +func div[T any]() uintptr { + return 1 / unsafe.Sizeof(*new(T)) +} + +func add[T any]() int64 { + return 1<<63 - 1 + int64(unsafe.Sizeof(*new(T))) +} + +func main() { + shift[[62]byte]() + shift[[63]byte]() + shift[[64]byte]() + shift[[100]byte]() + shift[[1e6]byte]() + + add[[1]byte]() + shouldPanic("divide by zero", func() { div[[0]byte]() }) +} + +func shouldPanic(str string, f func()) { + defer func() { + err := recover() + if err == nil { + panic("did not panic") + } + s := err.(error).Error() + if !strings.Contains(s, str) { + panic("got panic " + s + ", want " + str) + } + }() + + f() +} |