aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitri Shuralyov <dmitshur@golang.org>2023-06-20 13:19:34 -0400
committerDmitri Shuralyov <dmitshur@golang.org>2023-06-20 13:19:35 -0400
commitebbff91f5930ba74a4285f8ba6ddfd0df0871d95 (patch)
tree3946ab04bca39c829797bda4b0d989083cfda23c
parent1c1c82432a78b06c8010c7257df58ff11cc05b61 (diff)
parenta7b179370124c0114036b98a14f3f17cf76c122d (diff)
downloadgo-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
-rw-r--r--src/cmd/cgo/internal/testshared/shared_test.go6
-rw-r--r--src/cmd/compile/internal/syntax/parser_test.go12
-rw-r--r--src/cmd/compile/internal/typecheck/expr.go7
-rw-r--r--src/cmd/compile/internal/types2/stdlib_test.go8
-rw-r--r--src/cmd/distpack/archive.go12
-rw-r--r--src/cmd/distpack/pack.go15
-rw-r--r--src/cmd/distpack/test.go4
-rw-r--r--src/cmd/go/go_unix_test.go86
-rw-r--r--src/cmd/go/internal/list/list.go3
-rw-r--r--src/cmd/go/internal/modfetch/coderepo.go2
-rw-r--r--src/cmd/go/internal/modfetch/sumdb.go24
-rw-r--r--src/cmd/go/internal/modindex/read.go2
-rw-r--r--src/cmd/go/internal/test/test.go10
-rw-r--r--src/cmd/go/internal/toolchain/select.go93
-rw-r--r--src/cmd/go/internal/toolchain/umask_none.go13
-rw-r--r--src/cmd/go/internal/toolchain/umask_unix.go28
-rw-r--r--src/cmd/go/testdata/script/cover_list.txt39
-rw-r--r--src/cmd/internal/moddeps/moddeps_test.go2
-rw-r--r--src/cmd/internal/testdir/testdir_test.go11
-rw-r--r--src/compress/bzip2/bit_reader.go2
-rw-r--r--src/go/internal/gcimporter/gcimporter_test.go12
-rw-r--r--src/go/types/stdlib_test.go8
-rw-r--r--src/internal/bisect/bisect.go30
-rw-r--r--src/math/dim.go6
-rw-r--r--src/net/http/roundtrip_js.go33
-rw-r--r--src/net/http/server.go4
-rw-r--r--src/os/exec.go4
-rw-r--r--src/os/exec_unix_test.go18
-rw-r--r--src/path/filepath/path_test.go31
-rw-r--r--src/reflect/arena.go2
-rw-r--r--src/runtime/race/race_linux_test.go28
-rw-r--r--src/runtime/race_amd64.s2
-rw-r--r--src/runtime/race_arm64.s2
-rw-r--r--src/runtime/race_ppc64le.s2
-rw-r--r--src/runtime/trace/annotation.go8
-rw-r--r--src/runtime/trace/trace.go14
-rw-r--r--src/slices/sort_benchmark_test.go28
-rw-r--r--src/testing/fstest/testfs.go2
-rw-r--r--src/testing/fstest/testfs_test.go38
-rw-r--r--test/arenas/smoke.go65
-rw-r--r--test/fixedbugs/issue60601.go50
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()
+}