aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/README.md4
-rw-r--r--doc/go_spec.html40
-rw-r--r--doc/next/3-tools.md14
-rw-r--r--src/bytes/buffer_test.go2
-rw-r--r--src/bytes/bytes.go2
-rw-r--r--src/bytes/bytes_test.go47
-rw-r--r--src/bytes/reader.go4
-rw-r--r--src/cmd/compile/internal/liveness/mergelocals.go2
-rw-r--r--src/cmd/compile/internal/ssa/_gen/PPC64.rules1
-rw-r--r--src/cmd/compile/internal/ssa/deadstore.go19
-rw-r--r--src/cmd/compile/internal/ssa/deadstore_test.go45
-rw-r--r--src/cmd/compile/internal/ssa/rewrite.go27
-rw-r--r--src/cmd/compile/internal/ssa/rewritePPC64.go18
-rw-r--r--src/cmd/compile/internal/ssa/value.go2
-rw-r--r--src/cmd/compile/internal/types2/api.go2
-rw-r--r--src/cmd/compile/internal/types2/predicates.go4
-rw-r--r--src/cmd/compile/internal/types2/stmt.go23
-rw-r--r--src/cmd/dist/test.go2
-rw-r--r--src/cmd/go.mod6
-rw-r--r--src/cmd/go.sum12
-rw-r--r--src/cmd/go/counters_test.go140
-rw-r--r--src/cmd/go/internal/base/base.go27
-rw-r--r--src/cmd/go/internal/help/help.go3
-rw-r--r--src/cmd/go/internal/modfetch/cache.go3
-rw-r--r--src/cmd/go/internal/par/work.go5
-rw-r--r--src/cmd/go/internal/test/flagdefs.go1
-rw-r--r--src/cmd/go/internal/toolchain/select.go5
-rw-r--r--src/cmd/go/internal/toolchain/switch.go3
-rw-r--r--src/cmd/go/internal/work/gccgo.go3
-rw-r--r--src/cmd/go/main.go14
-rw-r--r--src/cmd/go/script_test.go20
-rw-r--r--src/cmd/go/testdata/counters.txt689
-rw-r--r--src/cmd/internal/telemetry/telemetry.go12
-rw-r--r--src/cmd/internal/telemetry/telemetry_bootstrap.go13
-rw-r--r--src/cmd/link/internal/ld/elf_test.go37
-rw-r--r--src/cmd/pprof/pprof.go10
-rw-r--r--src/cmd/vendor/golang.org/x/mod/modfile/read.go2
-rw-r--r--src/cmd/vendor/golang.org/x/mod/modfile/rule.go2
-rw-r--r--src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tile.go10
-rw-r--r--src/cmd/vendor/golang.org/x/sync/semaphore/semaphore.go42
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go13
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go30
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go11
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go60
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go159
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go19
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go26
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases.go12
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go15
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go69
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go53
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/facts/facts.go4
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go2
-rw-r--r--src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go4
-rw-r--r--src/cmd/vendor/modules.txt7
-rw-r--r--src/cmd/vet/main.go2
-rw-r--r--src/cmd/vet/testdata/stdversion/go.mod3
-rw-r--r--src/cmd/vet/testdata/stdversion/stdversion.go5
-rw-r--r--src/cmd/vet/vet_test.go31
-rw-r--r--src/compress/bzip2/bzip2.go2
-rw-r--r--src/compress/flate/deflate.go3
-rw-r--r--src/compress/gzip/gzip.go4
-rw-r--r--src/compress/lzw/writer.go6
-rw-r--r--src/encoding/json/encode.go22
-rw-r--r--src/expvar/expvar.go5
-rw-r--r--src/go/build/deps_test.go5
-rw-r--r--src/go/types/predicates.go4
-rw-r--r--src/go/types/stmt.go23
-rw-r--r--src/hash/maphash/smhasher_test.go13
-rw-r--r--src/internal/abi/runtime.go5
-rw-r--r--src/internal/bisect/bisect.go2
-rw-r--r--src/internal/filepathlite/path.go274
-rw-r--r--src/internal/filepathlite/path_nonwindows.go (renamed from src/path/filepath/path_nonwindows.go)2
-rw-r--r--src/internal/filepathlite/path_plan9.go41
-rw-r--r--src/internal/filepathlite/path_unix.go43
-rw-r--r--src/internal/filepathlite/path_windows.go329
-rw-r--r--src/internal/platform/supported.go2
-rw-r--r--src/internal/poll/sendfile.go7
-rw-r--r--src/internal/poll/sendfile_bsd.go3
-rw-r--r--src/internal/poll/sendfile_linux.go3
-rw-r--r--src/internal/poll/sendfile_solaris.go3
-rw-r--r--src/internal/poll/sendfile_windows.go3
-rw-r--r--src/internal/runtime/atomic/atomic_andor_test.go2
-rw-r--r--src/internal/safefilepath/path.go26
-rw-r--r--src/internal/safefilepath/path_plan9.go14
-rw-r--r--src/internal/safefilepath/path_unix.go16
-rw-r--r--src/internal/safefilepath/path_windows.go136
-rw-r--r--src/internal/stringslite/strings.go150
-rw-r--r--src/internal/syscall/windows/syscall_windows.go11
-rw-r--r--src/internal/sysinfo/sysinfo.go29
-rw-r--r--src/internal/testenv/testenv.go11
-rw-r--r--src/internal/weak/pointer.go4
-rwxr-xr-xsrc/make.bash11
-rwxr-xr-xsrc/make.rc11
-rw-r--r--src/mime/type.go13
-rw-r--r--src/net/dnsconfig_unix.go11
-rw-r--r--src/net/http/request_test.go8
-rw-r--r--src/net/http/routing_tree.go8
-rw-r--r--src/net/http/routing_tree_test.go11
-rw-r--r--src/net/http/server.go8
-rw-r--r--src/net/lookup_plan9.go19
-rw-r--r--src/net/lookup_test.go4
-rw-r--r--src/net/sendfile_linux.go2
-rw-r--r--src/net/sendfile_stub.go4
-rw-r--r--src/net/sendfile_test.go71
-rw-r--r--src/net/sendfile_unix_alt.go13
-rw-r--r--src/net/sendfile_windows.go2
-rw-r--r--src/net/url/url.go16
-rw-r--r--src/os/dir.go6
-rw-r--r--src/os/executable_procfs.go11
-rw-r--r--src/os/export_linux_test.go1
-rw-r--r--src/os/file.go4
-rw-r--r--src/os/file_plan9.go8
-rw-r--r--src/os/file_windows.go9
-rw-r--r--src/os/path.go3
-rw-r--r--src/os/path_plan9.go4
-rw-r--r--src/os/path_unix.go4
-rw-r--r--src/os/path_windows.go79
-rw-r--r--src/os/types_windows.go3
-rw-r--r--src/os/writeto_linux_test.go33
-rw-r--r--src/os/zero_copy_linux.go3
-rw-r--r--src/path/filepath/match.go3
-rw-r--r--src/path/filepath/path.go218
-rw-r--r--src/path/filepath/path_plan9.go19
-rw-r--r--src/path/filepath/path_unix.go19
-rw-r--r--src/path/filepath/path_windows.go200
-rw-r--r--src/path/filepath/symlink.go9
-rw-r--r--src/reflect/value.go11
-rw-r--r--src/runtime/asm_amd64.s4
-rw-r--r--src/runtime/extern.go5
-rw-r--r--src/runtime/iface.go4
-rw-r--r--src/runtime/map.go12
-rw-r--r--src/runtime/map_fast32.go8
-rw-r--r--src/runtime/map_fast64.go8
-rw-r--r--src/runtime/map_faststr.go16
-rw-r--r--src/runtime/os_windows.go16
-rw-r--r--src/runtime/runtime.go3
-rw-r--r--src/slices/slices.go36
-rw-r--r--src/slices/slices_test.go67
-rw-r--r--src/strconv/atoc.go4
-rw-r--r--src/strconv/atoi.go23
-rw-r--r--src/strings/clone.go9
-rw-r--r--src/strings/strings.go112
-rw-r--r--src/strings/strings_test.go47
-rw-r--r--src/syscall/fs_wasip1.go15
-rw-r--r--src/syscall/syscall_openbsd_libc.go28
-rw-r--r--src/syscall/zsyscall_openbsd_386.go15
-rw-r--r--src/syscall/zsyscall_openbsd_386.s2
-rw-r--r--src/syscall/zsyscall_openbsd_amd64.go15
-rw-r--r--src/syscall/zsyscall_openbsd_amd64.s2
-rw-r--r--src/syscall/zsyscall_openbsd_arm.go15
-rw-r--r--src/syscall/zsyscall_openbsd_arm.s2
-rw-r--r--src/syscall/zsyscall_openbsd_arm64.go15
-rw-r--r--src/syscall/zsyscall_openbsd_arm64.s2
-rw-r--r--src/syscall/zsyscall_openbsd_ppc64.go15
-rw-r--r--src/syscall/zsyscall_openbsd_ppc64.s3
-rw-r--r--src/syscall/zsyscall_openbsd_riscv64.go15
-rw-r--r--src/syscall/zsyscall_openbsd_riscv64.s2
-rw-r--r--src/testing/benchmark.go8
-rw-r--r--src/testing/export_test.go4
-rw-r--r--src/testing/fuzz.go10
-rw-r--r--src/testing/testing.go37
-rw-r--r--src/testing/testing_other.go18
-rw-r--r--src/testing/testing_windows.go38
-rw-r--r--src/testing/testing_windows_test.go25
-rw-r--r--src/time/format.go19
-rw-r--r--src/time/time.go5
-rw-r--r--src/unique/clone.go15
-rw-r--r--src/unique/handle.go2
-rw-r--r--test/codegen/shift.go21
176 files changed, 2297 insertions, 2341 deletions
diff --git a/doc/README.md b/doc/README.md
index 666e0966c9..c2b320711f 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -4,7 +4,9 @@ The `initial` and `next` subdirectories of this directory are for release notes.
## For developers
-Release notes should be added to `next` by editing existing files or creating new files.
+Release notes should be added to `next` by editing existing files or creating
+new files. **Do not add RELNOTE=yes comments in CLs.** Instead, add a file to
+the CL (or ask the author to do so).
At the end of the development cycle, the files will be merged by being
concatenated in sorted order by pathname. Files in the directory matching the
diff --git a/doc/go_spec.html b/doc/go_spec.html
index f5069f62d6..277cd27775 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Language version go1.22 (April 24, 2024)",
+ "Subtitle": "Language version go1.22 (April 25, 2024)",
"Path": "/ref/spec"
}-->
@@ -6643,7 +6643,7 @@ the range clause is equivalent to the same clause without that identifier.
</p>
<p>
-The range expression <code>x</code> is evaluated once before beginning the loop,
+The range expression <code>x</code> is evaluated before beginning the loop,
with one exception: if at most one iteration variable is present and <code>x</code> or
<a href="#Length_and_capacity"><code>len(x)</code></a> is <a href="#Constants">constant</a>,
the range expression is not evaluated.
@@ -6656,13 +6656,13 @@ if the respective iteration variables are present:
</p>
<pre class="grammar">
-Range expression 1st value 2nd value
+Range expression 1st value 2nd value
-array or slice a [n]E, *[n]E, or []E index i int a[i] E
-string s string type index i int see below rune
-map m map[K]V key k K m[k] V
-channel c chan E, &lt;-chan E element e E
-integer n integer type value i see below
+array or slice a [n]E, *[n]E, or []E index i int a[i] E
+string s string type index i int see below rune
+map m map[K]V key k K m[k] V
+channel c chan E, &lt;-chan E element e E
+integer value n integer type, or untyped int value i see below
</pre>
<ol>
@@ -6703,8 +6703,17 @@ is <code>nil</code>, the range expression blocks forever.
</li>
<li>
-For an integer value <code>n</code>, the iteration values 0 through <code>n-1</code>
+For an integer value <code>n</code>, where <code>n</code> is of <a href="#Numeric_types">integer type</a>
+or an untyped <a href="#Constants">integer constant</a>, the iteration values 0 through <code>n-1</code>
are produced in increasing order.
+If <code>n</code> is of integer type, the iteration values have that same type.
+Otherwise, the type of <code>n</code> is determined as if it were assigned to the
+iteration variable.
+Specifically:
+if the iteration variable is preexisting, the type of the iteration values is the type of the iteration
+variable, which must be of integer type.
+Otherwise, if the iteration variable is declared by the "range" clause or is absent,
+the type of the iteration values is the <a href="#Constants">default type</a> for <code>n</code>.
If <code>n</code> &lt= 0, the loop does not run any iterations.
</li>
</ol>
@@ -6716,11 +6725,7 @@ The iteration variables may be declared by the "range" clause using a form of
In this case their <a href="#Declarations_and_scope">scope</a> is the block of the "for" statement
and each iteration has its own new variables [<a href="#Go_1.22">Go 1.22</a>]
(see also <a href="#For_clause">"for" statements with a ForClause</a>).
-If the range expression is a (possibly untyped) integer expression <code>n</code>,
-the variable has the same type as if it was
-<a href="#Variable_declarations">declared</a> with initialization
-expression <code>n</code>.
-Otherwise, the variables have the types of their respective iteration values.
+The variables have the types of their respective iteration values.
</p>
<p>
@@ -6728,9 +6733,6 @@ If the iteration variables are not explicitly declared by the "range" clause,
they must be preexisting.
In this case, the iteration values are assigned to the respective variables
as in an <a href="#Assignment_statements">assignment statement</a>.
-If the range expression is a (possibly untyped) integer expression <code>n</code>,
-<code>n</code> too must be <a href="#Assignability">assignable</a> to the iteration variable;
-if there is no iteration variable, <code>n</code> must be assignable to <code>int</code>.
</p>
<pre>
@@ -6778,6 +6780,10 @@ for i := range 10 {
var u uint8
for u = range 256 {
}
+
+// invalid: 1e3 is a floating-point constant
+for range 1e3 {
+}
</pre>
diff --git a/doc/next/3-tools.md b/doc/next/3-tools.md
index bdbe6c0771..c052f3b084 100644
--- a/doc/next/3-tools.md
+++ b/doc/next/3-tools.md
@@ -8,5 +8,19 @@ Distributions that install the `go` command to a location other than
`$GOROOT/bin/go` should install a symlink instead of relocating
or copying the `go` binary.
+### Vet {#vet}
+
+The `go vet` subcommand now includes the
+[stdversion](https://beta.pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdversion)
+analyzer, which flags references to symbols that are too new for the version
+of Go in effect in the referring file. (The effective version is determined
+by the `go` directive in the file's enclosing `go.mod` file, and
+by any [`//go:build` constraints](https://pkg.go.dev/cmd/go#hdr-Build_constraints)
+in the file.)
+
+For example, it will report a diagnostic for a reference to the
+`reflect.TypeFor` function (introduced in go1.22) from a file in a
+module whose go.mod file specifies `go 1.21`.
+
### Cgo {#cgo}
diff --git a/src/bytes/buffer_test.go b/src/bytes/buffer_test.go
index 322e7367c7..3c964fc6b9 100644
--- a/src/bytes/buffer_test.go
+++ b/src/bytes/buffer_test.go
@@ -7,6 +7,7 @@ package bytes_test
import (
. "bytes"
"fmt"
+ "internal/testenv"
"io"
"math/rand"
"strconv"
@@ -100,6 +101,7 @@ var buf Buffer
// should not result in any allocations.
// This can be used to reset the underlying []byte of an existing Buffer.
func TestNewBufferShallow(t *testing.T) {
+ testenv.SkipIfOptimizationOff(t)
n := testing.AllocsPerRun(1000, func() {
buf = *NewBuffer(testBytes)
})
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 1871814c6e..23edd5a4be 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -583,7 +583,7 @@ func Repeat(b []byte, count int) []byte {
if count < 0 {
panic("bytes: negative Repeat count")
}
- if len(b) >= maxInt/count {
+ if len(b) > maxInt/count {
panic("bytes: Repeat output length overflow")
}
n := len(b) * count
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 5e8cf85fd9..200a357bc0 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -1242,33 +1242,48 @@ func repeat(b []byte, count int) (err error) {
// See Issue golang.org/issue/16237
func TestRepeatCatchesOverflow(t *testing.T) {
- tests := [...]struct {
+ type testCase struct {
s string
count int
errStr string
- }{
+ }
+
+ runTestCases := func(prefix string, tests []testCase) {
+ for i, tt := range tests {
+ err := repeat([]byte(tt.s), tt.count)
+ if tt.errStr == "" {
+ if err != nil {
+ t.Errorf("#%d panicked %v", i, err)
+ }
+ continue
+ }
+
+ if err == nil || !strings.Contains(err.Error(), tt.errStr) {
+ t.Errorf("%s#%d got %q want %q", prefix, i, err, tt.errStr)
+ }
+ }
+ }
+
+ const maxInt = int(^uint(0) >> 1)
+
+ runTestCases("", []testCase{
0: {"--", -2147483647, "negative"},
- 1: {"", int(^uint(0) >> 1), ""},
+ 1: {"", maxInt, ""},
2: {"-", 10, ""},
3: {"gopher", 0, ""},
4: {"-", -1, "negative"},
5: {"--", -102, "negative"},
6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
- }
-
- for i, tt := range tests {
- err := repeat([]byte(tt.s), tt.count)
- if tt.errStr == "" {
- if err != nil {
- t.Errorf("#%d panicked %v", i, err)
- }
- continue
- }
+ })
- if err == nil || !strings.Contains(err.Error(), tt.errStr) {
- t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
- }
+ const is64Bit = 1<<(^uintptr(0)>>63)/2 != 0
+ if !is64Bit {
+ return
}
+
+ runTestCases("64-bit", []testCase{
+ 0: {"-", maxInt, "out of range"},
+ })
}
func runesEqual(a, b []rune) bool {
diff --git a/src/bytes/reader.go b/src/bytes/reader.go
index 9ef49014ed..30c46fa6b3 100644
--- a/src/bytes/reader.go
+++ b/src/bytes/reader.go
@@ -152,8 +152,8 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
return
}
-// Reset resets the [Reader.Reader] to be reading from b.
+// Reset resets the [Reader] to be reading from b.
func (r *Reader) Reset(b []byte) { *r = Reader{b, 0, -1} }
-// NewReader returns a new [Reader.Reader] reading from b.
+// NewReader returns a new [Reader] reading from b.
func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} }
diff --git a/src/cmd/compile/internal/liveness/mergelocals.go b/src/cmd/compile/internal/liveness/mergelocals.go
index 1e65d6c1d1..017c4d1dbb 100644
--- a/src/cmd/compile/internal/liveness/mergelocals.go
+++ b/src/cmd/compile/internal/liveness/mergelocals.go
@@ -448,7 +448,7 @@ func (cs *cstate) setupHashBisection(cands []*ir.Name) {
//
// It is possible to have situations where a given ir.Name is
// non-address-taken at the source level, but whose address is
-// materialized in order to accomodate the needs of
+// materialized in order to accommodate the needs of
// architecture-dependent operations or one sort or another (examples
// include things like LoweredZero/DuffZero, etc). The issue here is
// that the SymAddr op will show up as touching a variable of
diff --git a/src/cmd/compile/internal/ssa/_gen/PPC64.rules b/src/cmd/compile/internal/ssa/_gen/PPC64.rules
index 7518119147..f0cb23ba9f 100644
--- a/src/cmd/compile/internal/ssa/_gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/PPC64.rules
@@ -158,6 +158,7 @@
// Merge shift right + shift left and clear left (e.g for a table lookup)
(CLRLSLDI [c] (SRWconst [s] x)) && mergePPC64ClrlsldiSrw(int64(c),s) != 0 => (RLWINM [mergePPC64ClrlsldiSrw(int64(c),s)] x)
+(CLRLSLDI [c] (SRDconst [s] x)) && mergePPC64ClrlsldiSrd(int64(c),s) != 0 => (RLWINM [mergePPC64ClrlsldiSrd(int64(c),s)] x)
(SLDconst [l] (SRWconst [r] x)) && mergePPC64SldiSrw(l,r) != 0 => (RLWINM [mergePPC64SldiSrw(l,r)] x)
// The following reduction shows up frequently too. e.g b[(x>>14)&0xFF]
(CLRLSLDI [c] i:(RLWINM [s] x)) && mergePPC64ClrlsldiRlwinm(c,s) != 0 => (RLWINM [mergePPC64ClrlsldiRlwinm(c,s)] x)
diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go
index cb3427103c..ce04cb3a24 100644
--- a/src/cmd/compile/internal/ssa/deadstore.go
+++ b/src/cmd/compile/internal/ssa/deadstore.go
@@ -21,12 +21,18 @@ func dse(f *Func) {
defer f.retSparseSet(storeUse)
shadowed := f.newSparseMap(f.NumValues())
defer f.retSparseMap(shadowed)
+ // localAddrs maps from a local variable (the Aux field of a LocalAddr value) to an instance of a LocalAddr value for that variable in the current block.
+ localAddrs := map[any]*Value{}
for _, b := range f.Blocks {
// Find all the stores in this block. Categorize their uses:
// loadUse contains stores which are used by a subsequent load.
// storeUse contains stores which are used by a subsequent store.
loadUse.clear()
storeUse.clear()
+ // TODO(deparker): use the 'clear' builtin once compiler bootstrap minimum version is raised to 1.21.
+ for k := range localAddrs {
+ delete(localAddrs, k)
+ }
stores = stores[:0]
for _, v := range b.Values {
if v.Op == OpPhi {
@@ -46,6 +52,13 @@ func dse(f *Func) {
}
}
} else {
+ if v.Op == OpLocalAddr {
+ if _, ok := localAddrs[v.Aux]; !ok {
+ localAddrs[v.Aux] = v
+ } else {
+ continue
+ }
+ }
for _, a := range v.Args {
if a.Block == b && a.Type.IsMemory() {
loadUse.add(a.ID)
@@ -100,6 +113,11 @@ func dse(f *Func) {
} else { // OpZero
sz = v.AuxInt
}
+ if ptr.Op == OpLocalAddr {
+ if la, ok := localAddrs[ptr.Aux]; ok {
+ ptr = la
+ }
+ }
sr := shadowRange(shadowed.get(ptr.ID))
if sr.contains(off, off+sz) {
// Modify the store/zero into a copy of the memory state,
@@ -146,6 +164,7 @@ type shadowRange int32
func (sr shadowRange) lo() int64 {
return int64(sr & 0xffff)
}
+
func (sr shadowRange) hi() int64 {
return int64((sr >> 16) & 0xffff)
}
diff --git a/src/cmd/compile/internal/ssa/deadstore_test.go b/src/cmd/compile/internal/ssa/deadstore_test.go
index 33cb4b9755..4ccd6b8e91 100644
--- a/src/cmd/compile/internal/ssa/deadstore_test.go
+++ b/src/cmd/compile/internal/ssa/deadstore_test.go
@@ -6,6 +6,7 @@ package ssa
import (
"cmd/compile/internal/types"
+ "cmd/internal/src"
"testing"
)
@@ -44,6 +45,7 @@ func TestDeadStore(t *testing.T) {
t.Errorf("dead store (zero) not removed")
}
}
+
func TestDeadStorePhi(t *testing.T) {
// make sure we don't get into an infinite loop with phi values.
c := testConfig(t)
@@ -127,3 +129,46 @@ func TestDeadStoreUnsafe(t *testing.T) {
t.Errorf("store %s incorrectly removed", v)
}
}
+
+func TestDeadStoreSmallStructInit(t *testing.T) {
+ c := testConfig(t)
+ ptrType := c.config.Types.BytePtr
+ typ := types.NewStruct([]*types.Field{
+ types.NewField(src.NoXPos, &types.Sym{Name: "A"}, c.config.Types.Int),
+ types.NewField(src.NoXPos, &types.Sym{Name: "B"}, c.config.Types.Int),
+ })
+ name := c.Temp(typ)
+ fun := c.Fun("entry",
+ Bloc("entry",
+ Valu("start", OpInitMem, types.TypeMem, 0, nil),
+ Valu("sp", OpSP, c.config.Types.Uintptr, 0, nil),
+ Valu("zero", OpConst64, c.config.Types.Int, 0, nil),
+ Valu("v6", OpLocalAddr, ptrType, 0, name, "sp", "start"),
+ Valu("v3", OpOffPtr, ptrType, 8, nil, "v6"),
+ Valu("v22", OpOffPtr, ptrType, 0, nil, "v6"),
+ Valu("zerostore1", OpStore, types.TypeMem, 0, c.config.Types.Int, "v22", "zero", "start"),
+ Valu("zerostore2", OpStore, types.TypeMem, 0, c.config.Types.Int, "v3", "zero", "zerostore1"),
+ Valu("v8", OpLocalAddr, ptrType, 0, name, "sp", "zerostore2"),
+ Valu("v23", OpOffPtr, ptrType, 8, nil, "v8"),
+ Valu("v25", OpOffPtr, ptrType, 0, nil, "v8"),
+ Valu("zerostore3", OpStore, types.TypeMem, 0, c.config.Types.Int, "v25", "zero", "zerostore2"),
+ Valu("zerostore4", OpStore, types.TypeMem, 0, c.config.Types.Int, "v23", "zero", "zerostore3"),
+ Goto("exit")),
+ Bloc("exit",
+ Exit("zerostore4")))
+
+ fun.f.Name = "smallstructinit"
+ CheckFunc(fun.f)
+ cse(fun.f)
+ dse(fun.f)
+ CheckFunc(fun.f)
+
+ v1 := fun.values["zerostore1"]
+ if v1.Op != OpCopy {
+ t.Errorf("dead store not removed")
+ }
+ v2 := fun.values["zerostore2"]
+ if v2.Op != OpCopy {
+ t.Errorf("dead store not removed")
+ }
+}
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index 9961b540b7..4e4d99af0b 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -1589,7 +1589,7 @@ func mergePPC64AndSrwi(m, s int64) int64 {
return encodePPC64RotateMask((32-s)&31, mask, 32)
}
-// Test if a shift right feeding into a CLRLSLDI can be merged into RLWINM.
+// Test if a word shift right feeding into a CLRLSLDI can be merged into RLWINM.
// Return the encoded RLWINM constant, or 0 if they cannot be merged.
func mergePPC64ClrlsldiSrw(sld, srw int64) int64 {
mask_1 := uint64(0xFFFFFFFF >> uint(srw))
@@ -1609,6 +1609,31 @@ func mergePPC64ClrlsldiSrw(sld, srw int64) int64 {
return encodePPC64RotateMask(int64(r_3), int64(mask_3), 32)
}
+// Test if a doubleword shift right feeding into a CLRLSLDI can be merged into RLWINM.
+// Return the encoded RLWINM constant, or 0 if they cannot be merged.
+func mergePPC64ClrlsldiSrd(sld, srd int64) int64 {
+ mask_1 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(srd)
+ // for CLRLSLDI, it's more convenient to think of it as a mask left bits then rotate left.
+ mask_2 := uint64(0xFFFFFFFFFFFFFFFF) >> uint(GetPPC64Shiftmb(int64(sld)))
+
+ // Rewrite mask to apply after the final left shift.
+ mask_3 := (mask_1 & mask_2) << uint(GetPPC64Shiftsh(sld))
+
+ r_1 := 64 - srd
+ r_2 := GetPPC64Shiftsh(sld)
+ r_3 := (r_1 + r_2) & 63 // This can wrap.
+
+ if uint64(uint32(mask_3)) != mask_3 || mask_3 == 0 {
+ return 0
+ }
+ // This combine only works when selecting and shifting the lower 32 bits.
+ v1 := bits.RotateLeft64(0xFFFFFFFF00000000, int(r_3))
+ if v1&mask_3 != 0 {
+ return 0
+ }
+ return encodePPC64RotateMask(int64(r_3-32), int64(mask_3), 32)
+}
+
// Test if a RLWINM feeding into a CLRLSLDI can be merged into RLWINM. Return
// the encoded RLWINM constant, or 0 if they cannot be merged.
func mergePPC64ClrlsldiRlwinm(sld int32, rlw int64) int64 {
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 4ac5eec073..266ac14c38 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -4628,6 +4628,24 @@ func rewriteValuePPC64_OpPPC64CLRLSLDI(v *Value) bool {
v.AddArg(x)
return true
}
+ // match: (CLRLSLDI [c] (SRDconst [s] x))
+ // cond: mergePPC64ClrlsldiSrd(int64(c),s) != 0
+ // result: (RLWINM [mergePPC64ClrlsldiSrd(int64(c),s)] x)
+ for {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpPPC64SRDconst {
+ break
+ }
+ s := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(mergePPC64ClrlsldiSrd(int64(c), s) != 0) {
+ break
+ }
+ v.reset(OpPPC64RLWINM)
+ v.AuxInt = int64ToAuxInt(mergePPC64ClrlsldiSrd(int64(c), s))
+ v.AddArg(x)
+ return true
+ }
// match: (CLRLSLDI [c] i:(RLWINM [s] x))
// cond: mergePPC64ClrlsldiRlwinm(c,s) != 0
// result: (RLWINM [mergePPC64ClrlsldiRlwinm(c,s)] x)
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index a9f4f102af..d08059c9d5 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -139,7 +139,7 @@ func (v *Value) AuxValAndOff() ValAndOff {
func (v *Value) AuxArm64BitField() arm64BitField {
if opcodeTable[v.Op].auxType != auxARM64BitField {
- v.Fatalf("op %s doesn't have a ValAndOff aux field", v.Op)
+ v.Fatalf("op %s doesn't have a ARM64BitField aux field", v.Op)
}
return arm64BitField(v.AuxInt)
}
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
index 029d105e2e..f3931dd262 100644
--- a/src/cmd/compile/internal/types2/api.go
+++ b/src/cmd/compile/internal/types2/api.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package types declares the data types and implements
+// Package types2 declares the data types and implements
// the algorithms for type-checking of Go packages. Use
// Config.Check to invoke the type checker for a package.
// Alternatively, create a new type checker with NewChecker
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index 6d9e6ec760..986cd6aa61 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -518,7 +518,9 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
func Default(t Type) Type {
- if t, ok := Unalias(t).(*Basic); ok {
+ // Alias and named types cannot denote untyped types
+ // so there's no need to call Unalias or under, below.
+ if t, _ := t.(*Basic); t != nil {
switch t.kind {
case UntypedBool:
return Typ[Bool]
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index 7fd7009e13..1984777008 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -923,19 +923,26 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
+ assert(obj.typ == nil)
+
+ // initialize lhs iteration variable, if any
+ typ := rhs[i]
+ if typ == nil {
+ obj.typ = Typ[Invalid]
+ obj.used = true // don't complain about unused variable
+ continue
+ }
// initialize lhs variable
if constIntRange {
check.initVar(obj, &x, "range clause")
- } else if typ := rhs[i]; typ != nil {
+ } else {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
- } else {
- obj.typ = Typ[Invalid]
- obj.used = true // don't complain about unused variable
}
+ assert(obj.typ != nil)
}
// declare variables
@@ -954,9 +961,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
continue
}
+ // assign to lhs iteration variable, if any
+ typ := rhs[i]
+ if typ == nil {
+ continue
+ }
+
if constIntRange {
check.assignVar(lhs, nil, &x, "range clause")
- } else if typ := rhs[i]; typ != nil {
+ } else {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 6f4d23182a..a87c2a1aae 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1585,7 +1585,7 @@ func raceDetectorSupported(goos, goarch string) bool {
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x"
case "darwin":
return goarch == "amd64" || goarch == "arm64"
- case "freebsd", "netbsd", "openbsd", "windows":
+ case "freebsd", "netbsd", "windows":
return goarch == "amd64"
default:
return false
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index acd8c9fa46..189d71f713 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -6,12 +6,12 @@ require (
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5
golang.org/x/arch v0.7.0
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f
- golang.org/x/mod v0.16.0
- golang.org/x/sync v0.6.0
+ golang.org/x/mod v0.17.0
+ golang.org/x/sync v0.7.0
golang.org/x/sys v0.19.0
golang.org/x/telemetry v0.0.0-20240401194020-3640ba572dd1
golang.org/x/term v0.18.0
- golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14
+ golang.org/x/tools v0.20.1-0.20240429173604-74c9cfe4d22f
)
require (
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index bd92d3c83f..3967087b8e 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -26,10 +26,10 @@ golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f h1:XQ2eu0I26WsNCKQkRehp+5mwjjChw94trD9LT8LLSq0=
golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f/go.mod h1:HTqTCkubWT8epEK9hDWWGkoOOB7LGSrU1qvWZCSwO50=
-golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
-golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240401194020-3640ba572dd1 h1:x0E096pmZoLhjEfcM4q2gJ3eZvnTpZiYDSPDYtm4wME=
@@ -38,7 +38,7 @@ golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14 h1:apiqzCtEqbg/NjzevIbwubwnRo7WZDOgdr8s6ZvIKi0=
-golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
+golang.org/x/tools v0.20.1-0.20240429173604-74c9cfe4d22f h1:VNKRNwDFpvmQ9DziicBj7Xs8Xr9zFtHVVCccBLiV+nI=
+golang.org/x/tools v0.20.1-0.20240429173604-74c9cfe4d22f/go.mod h1:EUhO3BJA9eB8d9EAsGPjXxkzI1Rl/NRgB9zrdAzyoWI=
rsc.io/markdown v0.0.0-20240117044121-669d2fdf1650 h1:fuOABZYWclLVNotDsHVaFixLdtoC7+UQZJ0KSC1ocm0=
rsc.io/markdown v0.0.0-20240117044121-669d2fdf1650/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=
diff --git a/src/cmd/go/counters_test.go b/src/cmd/go/counters_test.go
deleted file mode 100644
index 7c73889351..0000000000
--- a/src/cmd/go/counters_test.go
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2024 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_test
-
-import (
- "cmd/go/internal/base"
- "cmd/go/internal/cfg"
- "flag"
- "go/build"
- "internal/diff"
- "os"
- "slices"
- "strings"
- "testing"
-)
-
-var update = flag.Bool("update", false, "if true update testdata/counternames.txt")
-
-func TestCounterNamesUpToDate(t *testing.T) {
- if !*update {
- t.Parallel()
- }
-
- var counters []string
- // -C is a special case because it's handled by handleChdirFlag rather than
- // standard flag processing with FlagSets.
- // go/subcommand:unknown is also a special case: it's used when the subcommand
- // doesn't match any of the known commands.
- counters = append(counters, "go/flag:C", "go/subcommand:unknown")
- counters = append(counters, flagscounters("go/flag:", *flag.CommandLine)...)
-
- // Add help (without any arguments) as a special case. cmdcounters adds go help <cmd>
- // for all subcommands, but it's also valid to invoke go help without any arguments.
- counters = append(counters, "go/subcommand:help")
- for _, cmd := range base.Go.Commands {
- cmdcounters, err := cmdcounters(nil, cmd)
- if err != nil {
- t.Fatal(err)
- }
- counters = append(counters, cmdcounters...)
- }
-
- counters = append(counters, base.RegisteredCounterNames()...)
- for _, c := range counters {
- const counterPrefix = "go/"
- if !strings.HasPrefix(c, counterPrefix) {
- t.Fatalf("registered counter %q does not start with %q", c, counterPrefix)
- }
- }
-
- cstr := []byte(strings.Join(counters, "\n") + "\n")
- const counterNamesFile = "testdata/counters.txt"
- old, err := os.ReadFile(counterNamesFile)
- if err != nil {
- t.Fatalf("error reading %s: %v", counterNamesFile, err)
- }
- diff := diff.Diff(counterNamesFile, old, "generated counter names", cstr)
- if diff == nil {
- t.Logf("%s is up to date.", counterNamesFile)
- return
- }
-
- if *update {
- if err := os.WriteFile(counterNamesFile, cstr, 0666); err != nil {
- t.Fatal(err)
- }
- t.Logf("wrote %d bytes to %s", len(cstr), counterNamesFile)
- t.Logf("don't forget to file a proposal to update the list of collected counters")
- } else {
- t.Logf("\n%s", diff)
- t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", counterNamesFile)
- }
-}
-
-func flagscounters(prefix string, flagSet flag.FlagSet) []string {
- var counters []string
- flagSet.VisitAll(func(f *flag.Flag) {
- counters = append(counters, prefix+f.Name)
- })
- return counters
-}
-
-func cmdcounters(previous []string, cmd *base.Command) ([]string, error) {
- const subcommandPrefix = "go/subcommand:"
- const flagPrefix = "go/flag:"
- var counters []string
- previousComponent := strings.Join(previous, "-")
- if len(previousComponent) > 0 {
- previousComponent += "-"
- }
- if cmd.Runnable() {
- if cmd.Name() == "tool" {
- // TODO(matloob): Do we expect the same tools to be present on all
- // platforms/configurations? Should we only run this on certain
- // platforms?
- tools, err := toolNames()
- if err != nil {
- return nil, err
- }
- for _, t := range tools {
- counters = append(counters, subcommandPrefix+previousComponent+cmd.Name()+"-"+t)
- }
- counters = append(counters, subcommandPrefix+previousComponent+cmd.Name()+"-unknown")
- }
- counters = append(counters, subcommandPrefix+previousComponent+cmd.Name())
- }
- counters = append(counters, flagscounters(flagPrefix+previousComponent+cmd.Name()+"-", cmd.Flag)...)
- if len(previous) != 0 {
- counters = append(counters, subcommandPrefix+previousComponent+"help-"+cmd.Name())
- }
- counters = append(counters, subcommandPrefix+"help-"+previousComponent+cmd.Name())
-
- for _, subcmd := range cmd.Commands {
- subcmdcounters, err := cmdcounters(append(slices.Clone(previous), cmd.Name()), subcmd)
- if err != nil {
- return nil, err
- }
- counters = append(counters, subcmdcounters...)
- }
- return counters, nil
-}
-
-// toolNames returns the list of basenames of executables in the tool dir.
-func toolNames() ([]string, error) {
- entries, err := os.ReadDir(build.ToolDir)
- if err != nil {
- return nil, err
- }
- var names []string
- for _, e := range entries {
- if e.IsDir() {
- continue
- }
- name := strings.TrimSuffix(e.Name(), cfg.ToolExeSuffix())
- names = append(names, name)
- }
- return names, nil
-}
diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go
index cc3f94e56c..2171d13909 100644
--- a/src/cmd/go/internal/base/base.go
+++ b/src/cmd/go/internal/base/base.go
@@ -7,7 +7,6 @@
package base
import (
- "cmd/internal/telemetry"
"context"
"flag"
"fmt"
@@ -15,7 +14,6 @@ import (
"os"
"os/exec"
"reflect"
- "sort"
"strings"
"sync"
@@ -223,28 +221,3 @@ func RunStdin(cmdline []string) {
// Usage is the usage-reporting function, filled in by package main
// but here for reference by other packages.
var Usage func()
-
-var counterNames = map[string]bool{}
-
-type Counter interface {
- Inc()
-}
-
-// NewCounter registers a new counter. It must be called from an init function
-// or global variable initializer.
-func NewCounter(name string) Counter {
- if counterNames[name] {
- panic(fmt.Errorf("counter %q initialized twice", name))
- }
- counterNames[name] = true
- return telemetry.NewCounter(name)
-}
-
-func RegisteredCounterNames() []string {
- var names []string
- for name := range counterNames {
- names = append(names, name)
- }
- sort.Strings(names)
- return names
-}
diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go
index a2ecd6cf41..98382f2423 100644
--- a/src/cmd/go/internal/help/help.go
+++ b/src/cmd/go/internal/help/help.go
@@ -16,9 +16,10 @@ import (
"unicode/utf8"
"cmd/go/internal/base"
+ "cmd/internal/telemetry"
)
-var counterErrorsHelpUnknownTopic = base.NewCounter("go/errors:help-unknown-topic")
+var counterErrorsHelpUnknownTopic = telemetry.NewCounter("go/errors:help-unknown-topic")
// Help implements the 'help' command.
func Help(w io.Writer, args []string) {
diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go
index da76511d06..75b28b9bbc 100644
--- a/src/cmd/go/internal/modfetch/cache.go
+++ b/src/cmd/go/internal/modfetch/cache.go
@@ -26,6 +26,7 @@ import (
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/par"
"cmd/go/internal/robustio"
+ "cmd/internal/telemetry"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@@ -778,7 +779,7 @@ var (
statCacheOnce sync.Once
statCacheErr error
- counterErrorsGOMODCACHEEntryRelative = base.NewCounter("go/errors:gomodcache-entry-relative")
+ counterErrorsGOMODCACHEEntryRelative = telemetry.NewCounter("go/errors:gomodcache-entry-relative")
)
// checkCacheDir checks if the directory specified by GOMODCACHE exists. An
diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go
index 5b6de9425a..3f1e69adfe 100644
--- a/src/cmd/go/internal/par/work.go
+++ b/src/cmd/go/internal/par/work.go
@@ -189,10 +189,7 @@ func (c *Cache[K, V]) Get(key K) (V, bool) {
// TODO(jayconrod): Delete this after the package cache clearing functions
// in internal/load have been removed.
func (c *Cache[K, V]) Clear() {
- c.m.Range(func(key, value any) bool {
- c.m.Delete(key)
- return true
- })
+ c.m.Clear()
}
// Delete removes an entry from the map. It is safe to call Delete for an
diff --git a/src/cmd/go/internal/test/flagdefs.go b/src/cmd/go/internal/test/flagdefs.go
index baa0cdf4c6..0292c19d82 100644
--- a/src/cmd/go/internal/test/flagdefs.go
+++ b/src/cmd/go/internal/test/flagdefs.go
@@ -67,6 +67,7 @@ var passAnalyzersToVet = map[string]bool{
"sigchanyzer": true,
"slog": true,
"stdmethods": true,
+ "stdversion": true,
"stringintconv": true,
"structtag": true,
"testinggoroutine": true,
diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go
index 79f12f34bd..5115b59711 100644
--- a/src/cmd/go/internal/toolchain/select.go
+++ b/src/cmd/go/internal/toolchain/select.go
@@ -26,6 +26,7 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/run"
"cmd/go/internal/work"
+ "cmd/internal/telemetry"
"golang.org/x/mod/module"
)
@@ -81,7 +82,7 @@ func FilterEnv(env []string) []string {
return out
}
-var counterErrorsInvalidToolchainInFile = base.NewCounter("go/errors:invalid-toolchain-in-file")
+var counterErrorsInvalidToolchainInFile = telemetry.NewCounter("go/errors:invalid-toolchain-in-file")
// Select invokes a different Go toolchain if directed by
// the GOTOOLCHAIN environment variable or the user's configuration
@@ -245,7 +246,7 @@ func Select() {
Exec(gotoolchain)
}
-var counterSelectExec = base.NewCounter("go/toolchain/select-exec")
+var counterSelectExec = telemetry.NewCounter("go/toolchain/select-exec")
// TestVersionSwitch is set in the test go binary to the value in $TESTGO_VERSION_SWITCH.
// Valid settings are:
diff --git a/src/cmd/go/internal/toolchain/switch.go b/src/cmd/go/internal/toolchain/switch.go
index 1b1ce30c02..ba1e6973cf 100644
--- a/src/cmd/go/internal/toolchain/switch.go
+++ b/src/cmd/go/internal/toolchain/switch.go
@@ -16,6 +16,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/gover"
"cmd/go/internal/modfetch"
+ "cmd/internal/telemetry"
)
// A Switcher collects errors to be reported and then decides
@@ -103,7 +104,7 @@ func (s *Switcher) Switch(ctx context.Context) {
panic("unreachable")
}
-var counterSwitchExec = base.NewCounter("go/toolchain/switch-exec")
+var counterSwitchExec = telemetry.NewCounter("go/toolchain/switch-exec")
// SwitchOrFatal attempts a toolchain switch based on the information in err
// and otherwise falls back to base.Fatal(err).
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index 91d744e658..71f37e8d47 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -484,6 +484,9 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
case "c-shared":
ldflags = append(ldflags, "-shared", "-nostdlib")
+ if cfg.Goos != "windows" {
+ ldflags = append(ldflags, "-Wl,-z,nodelete")
+ }
ldflags = append(ldflags, goLibBegin...)
ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 73fe612e75..86f3c65a92 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -3,12 +3,10 @@
// license that can be found in the LICENSE file.
//go:generate go test cmd/go -v -run=^TestDocsUpToDate$ -fixdocs
-//go:generate go test cmd/go -v -run=^TestCounterNamesUpToDate$ -update
package main
import (
- "cmd/internal/telemetry"
"context"
"flag"
"fmt"
@@ -44,6 +42,7 @@ import (
"cmd/go/internal/vet"
"cmd/go/internal/work"
"cmd/go/internal/workcmd"
+ "cmd/internal/telemetry"
)
func init() {
@@ -89,7 +88,7 @@ func init() {
var _ = go11tag
-var counterErrorsGOPATHEntryRelative = base.NewCounter("go/errors:gopath-entry-relative")
+var counterErrorsGOPATHEntryRelative = telemetry.NewCounter("go/errors:gopath-entry-relative")
func main() {
log.SetFlags(0)
@@ -253,7 +252,14 @@ func invoke(cmd *base.Command, args []string) {
} else {
base.SetFromGOFLAGS(&cmd.Flag)
cmd.Flag.Parse(args[1:])
- telemetry.CountFlags("go/flag:"+strings.ReplaceAll(cfg.CmdName, " ", "-")+"-", cmd.Flag)
+ prefix := "go/flag:" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "-"
+ cmd.Flag.Visit(func(f *flag.Flag) {
+ counterName := prefix + f.Name
+ if f.Name == "buildmode" { // Special case: there is a limited set of buildmode values
+ counterName += "-" + f.Value.String()
+ }
+ telemetry.Inc(counterName)
+ })
args = cmd.Flag.Args()
}
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index fa660bafc8..a38dec3610 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -22,7 +22,6 @@ import (
"path/filepath"
"runtime"
"strings"
- "sync"
"testing"
"time"
@@ -395,32 +394,13 @@ func readCounters(t *testing.T, telemetryDir string) map[string]uint64 {
return totals
}
-//go:embed testdata/counters.txt
-var countersTxt string
-
-var (
- allowedCountersOnce sync.Once
- allowedCounters = map[string]bool{} // Set of allowed counters.
-)
-
func checkCounters(t *testing.T, telemetryDir string) {
- allowedCountersOnce.Do(func() {
- for _, counter := range strings.Fields(countersTxt) {
- allowedCounters[counter] = true
- }
- })
counters := readCounters(t, telemetryDir)
if _, ok := scriptGoInvoked.Load(testing.TB(t)); ok {
if !disabledOnPlatform && len(counters) == 0 {
t.Fatal("go was invoked but no counters were incremented")
}
}
- for name := range counters {
- if !allowedCounters[name] {
- t.Fatalf("incremented counter %q is not in testdata/counters.txt. "+
- "Please update counters_test.go to produce an entry for it.", name)
- }
- }
}
// Copied from https://go.googlesource.com/telemetry/+/5f08a0cbff3f/internal/telemetry/mode.go#122
diff --git a/src/cmd/go/testdata/counters.txt b/src/cmd/go/testdata/counters.txt
deleted file mode 100644
index 109be96556..0000000000
--- a/src/cmd/go/testdata/counters.txt
+++ /dev/null
@@ -1,689 +0,0 @@
-go/flag:C
-go/subcommand:unknown
-go/flag:fixdocs
-go/flag:fixreadme
-go/flag:flaky
-go/flag:proxy
-go/flag:test.bench
-go/flag:test.benchmem
-go/flag:test.benchtime
-go/flag:test.blockprofile
-go/flag:test.blockprofilerate
-go/flag:test.count
-go/flag:test.coverprofile
-go/flag:test.cpu
-go/flag:test.cpuprofile
-go/flag:test.failfast
-go/flag:test.fullpath
-go/flag:test.fuzz
-go/flag:test.fuzzcachedir
-go/flag:test.fuzzminimizetime
-go/flag:test.fuzztime
-go/flag:test.fuzzworker
-go/flag:test.gocoverdir
-go/flag:test.list
-go/flag:test.memprofile
-go/flag:test.memprofilerate
-go/flag:test.mutexprofile
-go/flag:test.mutexprofilefraction
-go/flag:test.outputdir
-go/flag:test.paniconexit0
-go/flag:test.parallel
-go/flag:test.run
-go/flag:test.short
-go/flag:test.shuffle
-go/flag:test.skip
-go/flag:test.testlogfile
-go/flag:test.timeout
-go/flag:test.trace
-go/flag:test.v
-go/flag:testsum
-go/flag:testwork
-go/flag:update
-go/subcommand:help
-go/subcommand:bug
-go/flag:bug-C
-go/flag:bug-v
-go/subcommand:help-bug
-go/subcommand:build
-go/flag:build-C
-go/flag:build-a
-go/flag:build-asan
-go/flag:build-asmflags
-go/flag:build-buildmode
-go/flag:build-buildvcs
-go/flag:build-compiler
-go/flag:build-cover
-go/flag:build-covermode
-go/flag:build-coverpkg
-go/flag:build-debug-actiongraph
-go/flag:build-debug-runtime-trace
-go/flag:build-debug-trace
-go/flag:build-gccgoflags
-go/flag:build-gcflags
-go/flag:build-installsuffix
-go/flag:build-ldflags
-go/flag:build-linkshared
-go/flag:build-mod
-go/flag:build-modcacherw
-go/flag:build-modfile
-go/flag:build-msan
-go/flag:build-n
-go/flag:build-o
-go/flag:build-overlay
-go/flag:build-p
-go/flag:build-pgo
-go/flag:build-pkgdir
-go/flag:build-race
-go/flag:build-tags
-go/flag:build-toolexec
-go/flag:build-trimpath
-go/flag:build-v
-go/flag:build-work
-go/flag:build-x
-go/subcommand:help-build
-go/subcommand:clean
-go/flag:clean-C
-go/flag:clean-a
-go/flag:clean-asan
-go/flag:clean-asmflags
-go/flag:clean-buildmode
-go/flag:clean-buildvcs
-go/flag:clean-cache
-go/flag:clean-compiler
-go/flag:clean-debug-actiongraph
-go/flag:clean-debug-runtime-trace
-go/flag:clean-debug-trace
-go/flag:clean-fuzzcache
-go/flag:clean-gccgoflags
-go/flag:clean-gcflags
-go/flag:clean-i
-go/flag:clean-installsuffix
-go/flag:clean-ldflags
-go/flag:clean-linkshared
-go/flag:clean-mod
-go/flag:clean-modcache
-go/flag:clean-modcacherw
-go/flag:clean-modfile
-go/flag:clean-msan
-go/flag:clean-n
-go/flag:clean-overlay
-go/flag:clean-p
-go/flag:clean-pgo
-go/flag:clean-pkgdir
-go/flag:clean-r
-go/flag:clean-race
-go/flag:clean-tags
-go/flag:clean-testcache
-go/flag:clean-toolexec
-go/flag:clean-trimpath
-go/flag:clean-v
-go/flag:clean-work
-go/flag:clean-x
-go/subcommand:help-clean
-go/subcommand:doc
-go/subcommand:help-doc
-go/subcommand:env
-go/flag:env-C
-go/flag:env-json
-go/flag:env-n
-go/flag:env-u
-go/flag:env-w
-go/flag:env-x
-go/subcommand:help-env
-go/subcommand:fix
-go/flag:fix-C
-go/flag:fix-a
-go/flag:fix-asan
-go/flag:fix-asmflags
-go/flag:fix-buildmode
-go/flag:fix-buildvcs
-go/flag:fix-compiler
-go/flag:fix-debug-actiongraph
-go/flag:fix-debug-runtime-trace
-go/flag:fix-debug-trace
-go/flag:fix-fix
-go/flag:fix-gccgoflags
-go/flag:fix-gcflags
-go/flag:fix-installsuffix
-go/flag:fix-ldflags
-go/flag:fix-linkshared
-go/flag:fix-mod
-go/flag:fix-modcacherw
-go/flag:fix-modfile
-go/flag:fix-msan
-go/flag:fix-n
-go/flag:fix-overlay
-go/flag:fix-p
-go/flag:fix-pgo
-go/flag:fix-pkgdir
-go/flag:fix-race
-go/flag:fix-tags
-go/flag:fix-toolexec
-go/flag:fix-trimpath
-go/flag:fix-v
-go/flag:fix-work
-go/flag:fix-x
-go/subcommand:help-fix
-go/subcommand:fmt
-go/flag:fmt-C
-go/flag:fmt-mod
-go/flag:fmt-modcacherw
-go/flag:fmt-modfile
-go/flag:fmt-n
-go/flag:fmt-overlay
-go/flag:fmt-x
-go/subcommand:help-fmt
-go/subcommand:generate
-go/flag:generate-C
-go/flag:generate-a
-go/flag:generate-asan
-go/flag:generate-asmflags
-go/flag:generate-buildmode
-go/flag:generate-buildvcs
-go/flag:generate-compiler
-go/flag:generate-debug-actiongraph
-go/flag:generate-debug-runtime-trace
-go/flag:generate-debug-trace
-go/flag:generate-gccgoflags
-go/flag:generate-gcflags
-go/flag:generate-installsuffix
-go/flag:generate-ldflags
-go/flag:generate-linkshared
-go/flag:generate-mod
-go/flag:generate-modcacherw
-go/flag:generate-modfile
-go/flag:generate-msan
-go/flag:generate-n
-go/flag:generate-overlay
-go/flag:generate-p
-go/flag:generate-pgo
-go/flag:generate-pkgdir
-go/flag:generate-race
-go/flag:generate-run
-go/flag:generate-skip
-go/flag:generate-tags
-go/flag:generate-toolexec
-go/flag:generate-trimpath
-go/flag:generate-v
-go/flag:generate-work
-go/flag:generate-x
-go/subcommand:help-generate
-go/subcommand:get
-go/flag:get-C
-go/flag:get-a
-go/flag:get-asan
-go/flag:get-asmflags
-go/flag:get-buildmode
-go/flag:get-buildvcs
-go/flag:get-compiler
-go/flag:get-d
-go/flag:get-debug-actiongraph
-go/flag:get-debug-runtime-trace
-go/flag:get-debug-trace
-go/flag:get-f
-go/flag:get-fix
-go/flag:get-gccgoflags
-go/flag:get-gcflags
-go/flag:get-insecure
-go/flag:get-installsuffix
-go/flag:get-ldflags
-go/flag:get-linkshared
-go/flag:get-m
-go/flag:get-modcacherw
-go/flag:get-modfile
-go/flag:get-msan
-go/flag:get-n
-go/flag:get-overlay
-go/flag:get-p
-go/flag:get-pgo
-go/flag:get-pkgdir
-go/flag:get-race
-go/flag:get-t
-go/flag:get-tags
-go/flag:get-toolexec
-go/flag:get-trimpath
-go/flag:get-u
-go/flag:get-v
-go/flag:get-work
-go/flag:get-x
-go/subcommand:help-get
-go/subcommand:install
-go/flag:install-C
-go/flag:install-a
-go/flag:install-asan
-go/flag:install-asmflags
-go/flag:install-buildmode
-go/flag:install-buildvcs
-go/flag:install-compiler
-go/flag:install-cover
-go/flag:install-covermode
-go/flag:install-coverpkg
-go/flag:install-debug-actiongraph
-go/flag:install-debug-runtime-trace
-go/flag:install-debug-trace
-go/flag:install-gccgoflags
-go/flag:install-gcflags
-go/flag:install-installsuffix
-go/flag:install-ldflags
-go/flag:install-linkshared
-go/flag:install-mod
-go/flag:install-modcacherw
-go/flag:install-modfile
-go/flag:install-msan
-go/flag:install-n
-go/flag:install-overlay
-go/flag:install-p
-go/flag:install-pgo
-go/flag:install-pkgdir
-go/flag:install-race
-go/flag:install-tags
-go/flag:install-toolexec
-go/flag:install-trimpath
-go/flag:install-v
-go/flag:install-work
-go/flag:install-x
-go/subcommand:help-install
-go/subcommand:list
-go/flag:list-C
-go/flag:list-a
-go/flag:list-asan
-go/flag:list-asmflags
-go/flag:list-buildmode
-go/flag:list-buildvcs
-go/flag:list-compiled
-go/flag:list-compiler
-go/flag:list-cover
-go/flag:list-covermode
-go/flag:list-coverpkg
-go/flag:list-debug-actiongraph
-go/flag:list-debug-runtime-trace
-go/flag:list-debug-trace
-go/flag:list-deps
-go/flag:list-e
-go/flag:list-export
-go/flag:list-f
-go/flag:list-find
-go/flag:list-gccgoflags
-go/flag:list-gcflags
-go/flag:list-installsuffix
-go/flag:list-json
-go/flag:list-ldflags
-go/flag:list-linkshared
-go/flag:list-m
-go/flag:list-mod
-go/flag:list-modcacherw
-go/flag:list-modfile
-go/flag:list-msan
-go/flag:list-n
-go/flag:list-overlay
-go/flag:list-p
-go/flag:list-pgo
-go/flag:list-pkgdir
-go/flag:list-race
-go/flag:list-retracted
-go/flag:list-reuse
-go/flag:list-tags
-go/flag:list-test
-go/flag:list-toolexec
-go/flag:list-trimpath
-go/flag:list-u
-go/flag:list-v
-go/flag:list-versions
-go/flag:list-work
-go/flag:list-x
-go/subcommand:help-list
-go/subcommand:help-mod
-go/subcommand:mod-download
-go/flag:mod-download-C
-go/flag:mod-download-json
-go/flag:mod-download-modcacherw
-go/flag:mod-download-modfile
-go/flag:mod-download-overlay
-go/flag:mod-download-reuse
-go/flag:mod-download-x
-go/subcommand:mod-help-download
-go/subcommand:help-mod-download
-go/subcommand:mod-edit
-go/flag:mod-edit-C
-go/flag:mod-edit-dropexclude
-go/flag:mod-edit-dropreplace
-go/flag:mod-edit-droprequire
-go/flag:mod-edit-dropretract
-go/flag:mod-edit-exclude
-go/flag:mod-edit-fmt
-go/flag:mod-edit-go
-go/flag:mod-edit-json
-go/flag:mod-edit-modcacherw
-go/flag:mod-edit-modfile
-go/flag:mod-edit-module
-go/flag:mod-edit-n
-go/flag:mod-edit-overlay
-go/flag:mod-edit-print
-go/flag:mod-edit-replace
-go/flag:mod-edit-require
-go/flag:mod-edit-retract
-go/flag:mod-edit-toolchain
-go/flag:mod-edit-x
-go/subcommand:mod-help-edit
-go/subcommand:help-mod-edit
-go/subcommand:mod-graph
-go/flag:mod-graph-C
-go/flag:mod-graph-go
-go/flag:mod-graph-modcacherw
-go/flag:mod-graph-modfile
-go/flag:mod-graph-overlay
-go/flag:mod-graph-x
-go/subcommand:mod-help-graph
-go/subcommand:help-mod-graph
-go/subcommand:mod-init
-go/flag:mod-init-C
-go/flag:mod-init-modcacherw
-go/flag:mod-init-modfile
-go/flag:mod-init-overlay
-go/subcommand:mod-help-init
-go/subcommand:help-mod-init
-go/subcommand:mod-tidy
-go/flag:mod-tidy-C
-go/flag:mod-tidy-compat
-go/flag:mod-tidy-e
-go/flag:mod-tidy-go
-go/flag:mod-tidy-modcacherw
-go/flag:mod-tidy-modfile
-go/flag:mod-tidy-overlay
-go/flag:mod-tidy-v
-go/flag:mod-tidy-x
-go/subcommand:mod-help-tidy
-go/subcommand:help-mod-tidy
-go/subcommand:mod-vendor
-go/flag:mod-vendor-C
-go/flag:mod-vendor-e
-go/flag:mod-vendor-modcacherw
-go/flag:mod-vendor-modfile
-go/flag:mod-vendor-o
-go/flag:mod-vendor-overlay
-go/flag:mod-vendor-v
-go/subcommand:mod-help-vendor
-go/subcommand:help-mod-vendor
-go/subcommand:mod-verify
-go/flag:mod-verify-C
-go/flag:mod-verify-modcacherw
-go/flag:mod-verify-modfile
-go/flag:mod-verify-overlay
-go/subcommand:mod-help-verify
-go/subcommand:help-mod-verify
-go/subcommand:mod-why
-go/flag:mod-why-C
-go/flag:mod-why-m
-go/flag:mod-why-modcacherw
-go/flag:mod-why-modfile
-go/flag:mod-why-overlay
-go/flag:mod-why-vendor
-go/subcommand:mod-help-why
-go/subcommand:help-mod-why
-go/subcommand:help-work
-go/subcommand:work-edit
-go/flag:work-edit-C
-go/flag:work-edit-dropreplace
-go/flag:work-edit-dropuse
-go/flag:work-edit-fmt
-go/flag:work-edit-go
-go/flag:work-edit-json
-go/flag:work-edit-print
-go/flag:work-edit-replace
-go/flag:work-edit-toolchain
-go/flag:work-edit-use
-go/subcommand:work-help-edit
-go/subcommand:help-work-edit
-go/subcommand:work-init
-go/flag:work-init-C
-go/flag:work-init-modcacherw
-go/flag:work-init-modfile
-go/flag:work-init-overlay
-go/subcommand:work-help-init
-go/subcommand:help-work-init
-go/subcommand:work-sync
-go/flag:work-sync-C
-go/flag:work-sync-modcacherw
-go/flag:work-sync-modfile
-go/flag:work-sync-overlay
-go/subcommand:work-help-sync
-go/subcommand:help-work-sync
-go/subcommand:work-use
-go/flag:work-use-C
-go/flag:work-use-modcacherw
-go/flag:work-use-modfile
-go/flag:work-use-overlay
-go/flag:work-use-r
-go/subcommand:work-help-use
-go/subcommand:help-work-use
-go/subcommand:work-vendor
-go/flag:work-vendor-C
-go/flag:work-vendor-e
-go/flag:work-vendor-modcacherw
-go/flag:work-vendor-modfile
-go/flag:work-vendor-o
-go/flag:work-vendor-overlay
-go/flag:work-vendor-v
-go/subcommand:work-help-vendor
-go/subcommand:help-work-vendor
-go/subcommand:run
-go/flag:run-C
-go/flag:run-a
-go/flag:run-asan
-go/flag:run-asmflags
-go/flag:run-buildmode
-go/flag:run-buildvcs
-go/flag:run-compiler
-go/flag:run-cover
-go/flag:run-covermode
-go/flag:run-coverpkg
-go/flag:run-debug-actiongraph
-go/flag:run-debug-runtime-trace
-go/flag:run-debug-trace
-go/flag:run-exec
-go/flag:run-gccgoflags
-go/flag:run-gcflags
-go/flag:run-installsuffix
-go/flag:run-ldflags
-go/flag:run-linkshared
-go/flag:run-mod
-go/flag:run-modcacherw
-go/flag:run-modfile
-go/flag:run-msan
-go/flag:run-n
-go/flag:run-overlay
-go/flag:run-p
-go/flag:run-pgo
-go/flag:run-pkgdir
-go/flag:run-race
-go/flag:run-tags
-go/flag:run-toolexec
-go/flag:run-trimpath
-go/flag:run-v
-go/flag:run-work
-go/flag:run-x
-go/subcommand:help-run
-go/subcommand:test
-go/flag:test-C
-go/flag:test-a
-go/flag:test-asan
-go/flag:test-asmflags
-go/flag:test-bench
-go/flag:test-benchmem
-go/flag:test-benchtime
-go/flag:test-blockprofile
-go/flag:test-blockprofilerate
-go/flag:test-buildmode
-go/flag:test-buildvcs
-go/flag:test-c
-go/flag:test-compiler
-go/flag:test-count
-go/flag:test-cover
-go/flag:test-covermode
-go/flag:test-coverpkg
-go/flag:test-coverprofile
-go/flag:test-cpu
-go/flag:test-cpuprofile
-go/flag:test-debug-actiongraph
-go/flag:test-debug-runtime-trace
-go/flag:test-debug-trace
-go/flag:test-exec
-go/flag:test-failfast
-go/flag:test-fullpath
-go/flag:test-fuzz
-go/flag:test-fuzzminimizetime
-go/flag:test-fuzztime
-go/flag:test-gccgoflags
-go/flag:test-gcflags
-go/flag:test-installsuffix
-go/flag:test-json
-go/flag:test-ldflags
-go/flag:test-linkshared
-go/flag:test-list
-go/flag:test-memprofile
-go/flag:test-memprofilerate
-go/flag:test-mod
-go/flag:test-modcacherw
-go/flag:test-modfile
-go/flag:test-msan
-go/flag:test-mutexprofile
-go/flag:test-mutexprofilefraction
-go/flag:test-n
-go/flag:test-o
-go/flag:test-outputdir
-go/flag:test-overlay
-go/flag:test-p
-go/flag:test-parallel
-go/flag:test-pgo
-go/flag:test-pkgdir
-go/flag:test-race
-go/flag:test-run
-go/flag:test-short
-go/flag:test-shuffle
-go/flag:test-skip
-go/flag:test-tags
-go/flag:test-test.bench
-go/flag:test-test.benchmem
-go/flag:test-test.benchtime
-go/flag:test-test.blockprofile
-go/flag:test-test.blockprofilerate
-go/flag:test-test.count
-go/flag:test-test.coverprofile
-go/flag:test-test.cpu
-go/flag:test-test.cpuprofile
-go/flag:test-test.failfast
-go/flag:test-test.fullpath
-go/flag:test-test.fuzz
-go/flag:test-test.fuzzminimizetime
-go/flag:test-test.fuzztime
-go/flag:test-test.list
-go/flag:test-test.memprofile
-go/flag:test-test.memprofilerate
-go/flag:test-test.mutexprofile
-go/flag:test-test.mutexprofilefraction
-go/flag:test-test.outputdir
-go/flag:test-test.parallel
-go/flag:test-test.run
-go/flag:test-test.short
-go/flag:test-test.shuffle
-go/flag:test-test.skip
-go/flag:test-test.timeout
-go/flag:test-test.trace
-go/flag:test-test.v
-go/flag:test-timeout
-go/flag:test-toolexec
-go/flag:test-trace
-go/flag:test-trimpath
-go/flag:test-v
-go/flag:test-vet
-go/flag:test-work
-go/flag:test-x
-go/subcommand:help-test
-go/subcommand:tool-addr2line
-go/subcommand:tool-asm
-go/subcommand:tool-buildid
-go/subcommand:tool-cgo
-go/subcommand:tool-compile
-go/subcommand:tool-covdata
-go/subcommand:tool-cover
-go/subcommand:tool-dist
-go/subcommand:tool-distpack
-go/subcommand:tool-doc
-go/subcommand:tool-fix
-go/subcommand:tool-link
-go/subcommand:tool-nm
-go/subcommand:tool-objdump
-go/subcommand:tool-pack
-go/subcommand:tool-pprof
-go/subcommand:tool-preprofile
-go/subcommand:tool-test2json
-go/subcommand:tool-trace
-go/subcommand:tool-vet
-go/subcommand:tool-unknown
-go/subcommand:tool
-go/flag:tool-C
-go/flag:tool-n
-go/subcommand:help-tool
-go/subcommand:version
-go/flag:version-C
-go/flag:version-m
-go/flag:version-v
-go/subcommand:help-version
-go/subcommand:vet
-go/flag:vet-C
-go/flag:vet-a
-go/flag:vet-asan
-go/flag:vet-asmflags
-go/flag:vet-buildmode
-go/flag:vet-buildvcs
-go/flag:vet-compiler
-go/flag:vet-debug-actiongraph
-go/flag:vet-debug-runtime-trace
-go/flag:vet-debug-trace
-go/flag:vet-gccgoflags
-go/flag:vet-gcflags
-go/flag:vet-installsuffix
-go/flag:vet-ldflags
-go/flag:vet-linkshared
-go/flag:vet-mod
-go/flag:vet-modcacherw
-go/flag:vet-modfile
-go/flag:vet-msan
-go/flag:vet-n
-go/flag:vet-overlay
-go/flag:vet-p
-go/flag:vet-pgo
-go/flag:vet-pkgdir
-go/flag:vet-race
-go/flag:vet-tags
-go/flag:vet-toolexec
-go/flag:vet-trimpath
-go/flag:vet-v
-go/flag:vet-vettool
-go/flag:vet-work
-go/flag:vet-x
-go/subcommand:help-vet
-go/subcommand:help-buildconstraint
-go/subcommand:help-buildmode
-go/subcommand:help-c
-go/subcommand:help-cache
-go/subcommand:help-environment
-go/subcommand:help-filetype
-go/subcommand:help-go.mod
-go/subcommand:help-gopath
-go/subcommand:help-goproxy
-go/subcommand:help-importpath
-go/subcommand:help-modules
-go/subcommand:help-module-auth
-go/subcommand:help-packages
-go/subcommand:help-private
-go/subcommand:help-testflag
-go/subcommand:help-testfunc
-go/subcommand:help-vcs
-go/errors:gomodcache-entry-relative
-go/errors:gopath-entry-relative
-go/errors:help-unknown-topic
-go/errors:invalid-toolchain-in-file
-go/toolchain/select-exec
-go/toolchain/switch-exec
diff --git a/src/cmd/internal/telemetry/telemetry.go b/src/cmd/internal/telemetry/telemetry.go
index 0e223442ff..d31f0eeff3 100644
--- a/src/cmd/internal/telemetry/telemetry.go
+++ b/src/cmd/internal/telemetry/telemetry.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !cmd_go_bootstrap
+//go:build !cmd_go_bootstrap && !compiler_bootstrap
// Package telemetry is a shim package around the golang.org/x/telemetry
// and golang.org/x/telemetry/counter packages that has code build tagged
@@ -38,14 +38,24 @@ func StartWithUpload() {
})
}
+// Inc increments the counter with the given name.
func Inc(name string) {
counter.Inc(name)
}
+// NewCounter returns a counter with the given name.
func NewCounter(name string) *counter.Counter {
return counter.New(name)
}
+// NewStack returns a new stack counter with the given name and depth.
+func NewStackCounter(name string, depth int) *counter.StackCounter {
+ return counter.NewStack(name, depth)
+}
+
+// CountFlags creates a counter for every flag that is set
+// and increments the counter. The name of the counter is
+// the concatenation of prefix and the flag name.
func CountFlags(prefix string, flagSet flag.FlagSet) {
counter.CountFlags(prefix, flagSet)
}
diff --git a/src/cmd/internal/telemetry/telemetry_bootstrap.go b/src/cmd/internal/telemetry/telemetry_bootstrap.go
index 9fb03507d9..2e127bec28 100644
--- a/src/cmd/internal/telemetry/telemetry_bootstrap.go
+++ b/src/cmd/internal/telemetry/telemetry_bootstrap.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build cmd_go_bootstrap
+//go:build cmd_go_bootstrap || compiler_bootstrap
package telemetry
@@ -12,8 +12,9 @@ type dummyCounter struct{}
func (dc dummyCounter) Inc() {}
-func Start() {}
-func StartWithUpload() {}
-func Inc(name string) {}
-func NewCounter(name string) dummyCounter { return dummyCounter{} }
-func CountFlags(name string, flagSet flag.FlagSet) {}
+func Start() {}
+func StartWithUpload() {}
+func Inc(name string) {}
+func NewCounter(name string) dummyCounter { return dummyCounter{} }
+func NewStackCounter(name string, depth int) dummyCounter { return dummyCounter{} }
+func CountFlags(name string, flagSet flag.FlagSet) {}
diff --git a/src/cmd/link/internal/ld/elf_test.go b/src/cmd/link/internal/ld/elf_test.go
index ad4149d55e..843b067e19 100644
--- a/src/cmd/link/internal/ld/elf_test.go
+++ b/src/cmd/link/internal/ld/elf_test.go
@@ -194,11 +194,25 @@ func TestElfBindNow(t *testing.T) {
progC = `package main; import "C"; func main() {}`
)
+ // Notes:
+ // - for linux/amd64 and linux/arm64, for relro we'll always see a
+ // .got section when building with -buildmode=pie (in addition
+ // to .dynamic); for some other less mainstream archs (ppc64le,
+ // s390) this is not the case (on ppc64le for example we only
+ // see got refs from C objects). Hence we put ".dynamic" in the
+ // 'want RO' list below and ".got" in the 'want RO if present".
+ // - when using the external linker, checking for read-only ".got"
+ // is problematic since some linkers will only make the .got
+ // read-only if its size is above a specific threshold, e.g.
+ // https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/scripttempl/elf.sc;h=d5022fa502f24db23f396f337a6c8978fbc8415b;hb=6fde04116b4b835fa9ec3b3497fcac4e4a0637e2#l74 . For this reason, don't try to verify read-only .got
+ // in the external linking case.
+
tests := []struct {
name string
args []string
prog string
wantSecsRO []string
+ wantSecsROIfPresent []string
mustHaveBuildModePIE bool
mustHaveCGO bool
mustInternalLink bool
@@ -214,7 +228,8 @@ func TestElfBindNow(t *testing.T) {
mustHaveBuildModePIE: true,
mustInternalLink: true,
wantDf1Pie: true,
- wantSecsRO: []string{".dynamic", ".got"},
+ wantSecsRO: []string{".dynamic"},
+ wantSecsROIfPresent: []string{".got"},
},
{
name: "bindnow-linkmode-internal",
@@ -234,7 +249,8 @@ func TestElfBindNow(t *testing.T) {
wantDfBindNow: true,
wantDf1Now: true,
wantDf1Pie: true,
- wantSecsRO: []string{".dynamic", ".got", ".got.plt"},
+ wantSecsRO: []string{".dynamic"},
+ wantSecsROIfPresent: []string{".got", ".got.plt"},
},
{
name: "bindnow-pie-linkmode-external",
@@ -245,8 +261,7 @@ func TestElfBindNow(t *testing.T) {
wantDfBindNow: true,
wantDf1Now: true,
wantDf1Pie: true,
- // NB: external linker produces .plt.got, not .got.plt
- wantSecsRO: []string{".dynamic", ".got"},
+ wantSecsRO: []string{".dynamic"},
},
}
@@ -339,10 +354,9 @@ func TestElfBindNow(t *testing.T) {
t.Fatalf("DT_FLAGS_1 DF_1_PIE got: %v, want: %v", gotDf1Pie, test.wantDf1Pie)
}
- // Skipping this newer portion of the test temporarily pending resolution of problems on ppc64le, loonpg64, possibly others.
- if false {
-
- for _, wsroname := range test.wantSecsRO {
+ wsrolists := [][]string{test.wantSecsRO, test.wantSecsROIfPresent}
+ for k, wsrolist := range wsrolists {
+ for _, wsroname := range wsrolist {
// Locate section of interest.
var wsro *elf.Section
for _, s := range elfFile.Sections {
@@ -352,8 +366,11 @@ func TestElfBindNow(t *testing.T) {
}
}
if wsro == nil {
- t.Fatalf("test %s: can't locate %q section",
- test.name, wsroname)
+ if k == 0 {
+ t.Fatalf("test %s: can't locate %q section",
+ test.name, wsroname)
+ }
+ continue
}
// Now walk the program headers. Section should be part of
diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go
index d4db9df285..24d6ee04a0 100644
--- a/src/cmd/pprof/pprof.go
+++ b/src/cmd/pprof/pprof.go
@@ -45,6 +45,16 @@ type fetcher struct {
}
func (f *fetcher) Fetch(src string, duration, timeout time.Duration) (*profile.Profile, string, error) {
+ // Firstly, determine if the src is an existing file on the disk.
+ // If it is a file, let regular pprof open it.
+ // If it is not a file, when the src contains `:`
+ // (e.g. mem_2023-11-02_03:55:24 or abc:123/mem_2023-11-02_03:55:24),
+ // url.Parse will recognize it as a link and ultimately report an error,
+ // similar to `abc:123/mem_2023-11-02_03:55:24:
+ // Get "http://abc:123/mem_2023-11-02_03:55:24": dial tcp: lookup abc: no such host`
+ if _, openErr := os.Stat(src); openErr == nil {
+ return nil, "", nil
+ }
sourceURL, timeout := adjustURL(src, duration, timeout)
if sourceURL == "" {
// Could not recognize URL, let regular pprof attempt to fetch the profile (eg. from a file)
diff --git a/src/cmd/vendor/golang.org/x/mod/modfile/read.go b/src/cmd/vendor/golang.org/x/mod/modfile/read.go
index 5b5bb5e115..2205682591 100644
--- a/src/cmd/vendor/golang.org/x/mod/modfile/read.go
+++ b/src/cmd/vendor/golang.org/x/mod/modfile/read.go
@@ -225,7 +225,7 @@ func (x *FileSyntax) Cleanup() {
if ww == 0 {
continue
}
- if ww == 1 {
+ if ww == 1 && len(stmt.RParen.Comments.Before) == 0 {
// Collapse block into single line.
line := &Line{
Comments: Comments{
diff --git a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
index 26acaa5f7c..0e7b7e2679 100644
--- a/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
+++ b/src/cmd/vendor/golang.org/x/mod/modfile/rule.go
@@ -975,6 +975,8 @@ func (f *File) AddGoStmt(version string) error {
var hint Expr
if f.Module != nil && f.Module.Syntax != nil {
hint = f.Module.Syntax
+ } else if f.Syntax == nil {
+ f.Syntax = new(FileSyntax)
}
f.Go = &Go{
Version: version,
diff --git a/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tile.go b/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tile.go
index 857d487551..37771c5310 100644
--- a/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tile.go
+++ b/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tile.go
@@ -115,16 +115,14 @@ func NewTiles(h int, oldTreeSize, newTreeSize int64) []Tile {
for level := uint(0); newTreeSize>>(H*level) > 0; level++ {
oldN := oldTreeSize >> (H * level)
newN := newTreeSize >> (H * level)
+ if oldN == newN {
+ continue
+ }
for n := oldN >> H; n < newN>>H; n++ {
tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: 1 << H})
}
n := newN >> H
- maxW := int(newN - n<<H)
- minW := 1
- if oldN > n<<H {
- minW = int(oldN - n<<H)
- }
- for w := minW; w <= maxW; w++ {
+ if w := int(newN - n<<H); w > 0 {
tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: w})
}
}
diff --git a/src/cmd/vendor/golang.org/x/sync/semaphore/semaphore.go b/src/cmd/vendor/golang.org/x/sync/semaphore/semaphore.go
index 30f632c577..b618162aab 100644
--- a/src/cmd/vendor/golang.org/x/sync/semaphore/semaphore.go
+++ b/src/cmd/vendor/golang.org/x/sync/semaphore/semaphore.go
@@ -35,11 +35,25 @@ type Weighted struct {
// Acquire acquires the semaphore with a weight of n, blocking until resources
// are available or ctx is done. On success, returns nil. On failure, returns
// ctx.Err() and leaves the semaphore unchanged.
-//
-// If ctx is already done, Acquire may still succeed without blocking.
func (s *Weighted) Acquire(ctx context.Context, n int64) error {
+ done := ctx.Done()
+
s.mu.Lock()
+ select {
+ case <-done:
+ // ctx becoming done has "happened before" acquiring the semaphore,
+ // whether it became done before the call began or while we were
+ // waiting for the mutex. We prefer to fail even if we could acquire
+ // the mutex without blocking.
+ s.mu.Unlock()
+ return ctx.Err()
+ default:
+ }
if s.size-s.cur >= n && s.waiters.Len() == 0 {
+ // Since we hold s.mu and haven't synchronized since checking done, if
+ // ctx becomes done before we return here, it becoming done must have
+ // "happened concurrently" with this call - it cannot "happen before"
+ // we return in this branch. So, we're ok to always acquire here.
s.cur += n
s.mu.Unlock()
return nil
@@ -48,7 +62,7 @@ func (s *Weighted) Acquire(ctx context.Context, n int64) error {
if n > s.size {
// Don't make other Acquire calls block on one that's doomed to fail.
s.mu.Unlock()
- <-ctx.Done()
+ <-done
return ctx.Err()
}
@@ -58,14 +72,14 @@ func (s *Weighted) Acquire(ctx context.Context, n int64) error {
s.mu.Unlock()
select {
- case <-ctx.Done():
- err := ctx.Err()
+ case <-done:
s.mu.Lock()
select {
case <-ready:
- // Acquired the semaphore after we were canceled. Rather than trying to
- // fix up the queue, just pretend we didn't notice the cancelation.
- err = nil
+ // Acquired the semaphore after we were canceled.
+ // Pretend we didn't and put the tokens back.
+ s.cur -= n
+ s.notifyWaiters()
default:
isFront := s.waiters.Front() == elem
s.waiters.Remove(elem)
@@ -75,9 +89,19 @@ func (s *Weighted) Acquire(ctx context.Context, n int64) error {
}
}
s.mu.Unlock()
- return err
+ return ctx.Err()
case <-ready:
+ // Acquired the semaphore. Check that ctx isn't already done.
+ // We check the done channel instead of calling ctx.Err because we
+ // already have the channel, and ctx.Err is O(n) with the nesting
+ // depth of ctx.
+ select {
+ case <-done:
+ s.Release(n)
+ return ctx.Err()
+ default:
+ }
return nil
}
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go
index 5da33c7e6e..5211773657 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go
@@ -112,6 +112,19 @@ type Pass struct {
// analysis's ResultType.
ResultOf map[*Analyzer]interface{}
+ // ReadFile returns the contents of the named file.
+ //
+ // The only valid file names are the elements of OtherFiles
+ // and IgnoredFiles, and names returned by
+ // Fset.File(f.FileStart).Name() for each f in Files.
+ //
+ // Analyzers must use this function (if provided) instead of
+ // accessing the file system directly. This allows a driver to
+ // provide a virtualized file tree (including, for example,
+ // unsaved editor buffers) and to track dependencies precisely
+ // to avoid unnecessary recomputation.
+ ReadFile func(filename string) ([]byte, error)
+
// -- facts --
// ImportObjectFact retrieves a fact associated with obj.
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
index 44867d599e..2a0aa57712 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
@@ -32,7 +32,7 @@ bases, and so on.
# Analyzer
-The primary type in the API is Analyzer. An Analyzer statically
+The primary type in the API is [Analyzer]. An Analyzer statically
describes an analysis function: its name, documentation, flags,
relationship to other analyzers, and of course, its logic.
@@ -72,7 +72,7 @@ help that describes the analyses it performs.
The doc comment contains a brief one-line summary,
optionally followed by paragraphs of explanation.
-The Analyzer type has more fields besides those shown above:
+The [Analyzer] type has more fields besides those shown above:
type Analyzer struct {
Name string
@@ -114,7 +114,7 @@ instance of the Pass type.
# Pass
-A Pass describes a single unit of work: the application of a particular
+A [Pass] describes a single unit of work: the application of a particular
Analyzer to a particular package of Go code.
The Pass provides information to the Analyzer's Run function about the
package being analyzed, and provides operations to the Run function for
@@ -135,16 +135,14 @@ reporting diagnostics and other information back to the driver.
The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees,
type information, and source positions for a single package of Go code.
-The OtherFiles field provides the names, but not the contents, of non-Go
-files such as assembly that are part of this package. See the "asmdecl"
-or "buildtags" analyzers for examples of loading non-Go files and reporting
-diagnostics against them.
-
-The IgnoredFiles field provides the names, but not the contents,
-of ignored Go and non-Go source files that are not part of this package
-with the current build configuration but may be part of other build
-configurations. See the "buildtags" analyzer for an example of loading
-and checking IgnoredFiles.
+The OtherFiles field provides the names of non-Go
+files such as assembly that are part of this package.
+Similarly, the IgnoredFiles field provides the names of Go and non-Go
+source files that are not part of this package with the current build
+configuration but may be part of other build configurations.
+The contents of these files may be read using Pass.ReadFile;
+see the "asmdecl" or "buildtags" analyzers for examples of loading
+non-Go files and reporting diagnostics against them.
The ResultOf field provides the results computed by the analyzers
required by this one, as expressed in its Analyzer.Requires field. The
@@ -177,7 +175,7 @@ Diagnostic is defined as:
The optional Category field is a short identifier that classifies the
kind of message when an analysis produces several kinds of diagnostic.
-The Diagnostic struct does not have a field to indicate its severity
+The [Diagnostic] struct does not have a field to indicate its severity
because opinions about the relative importance of Analyzers and their
diagnostics vary widely among users. The design of this framework does
not hold each Analyzer responsible for identifying the severity of its
@@ -191,7 +189,7 @@ and buildtag, inspect the raw text of Go source files or even non-Go
files such as assembly. To report a diagnostic against a line of a
raw text file, use the following sequence:
- content, err := os.ReadFile(filename)
+ content, err := pass.ReadFile(filename)
if err != nil { ... }
tf := fset.AddFile(filename, -1, len(content))
tf.SetLinesForContent(content)
@@ -216,7 +214,7 @@ addition, it records which functions are printf wrappers for use by
later analysis passes to identify other printf wrappers by induction.
A result such as “f is a printf wrapper” that is not interesting by
itself but serves as a stepping stone to an interesting result (such as
-a diagnostic) is called a "fact".
+a diagnostic) is called a [Fact].
The analysis API allows an analysis to define new types of facts, to
associate facts of these types with objects (named entities) declared
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
index f2ca95aa9e..3417232ce3 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
@@ -173,7 +173,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
Files:
for _, fname := range sfiles {
- content, tf, err := analysisutil.ReadFile(pass.Fset, fname)
+ content, tf, err := analysisutil.ReadFile(pass, fname)
if err != nil {
return nil, err
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
index 55bdad78b7..51ba2a91e5 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
@@ -89,7 +89,7 @@ func checkOtherFile(pass *analysis.Pass, filename string) error {
// We cannot use the Go parser, since this may not be a Go source file.
// Read the raw bytes instead.
- content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
+ content, tf, err := analysisutil.ReadFile(pass, filename)
if err != nil {
return err
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go
index 0001ba5363..19ef6b9bce 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_old.go
@@ -83,7 +83,7 @@ func checkGoFile(pass *analysis.Pass, f *ast.File) {
}
func checkOtherFile(pass *analysis.Pass, filename string) error {
- content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
+ content, tf, err := analysisutil.ReadFile(pass, filename)
if err != nil {
return err
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go
index 2691f189aa..f6727c5ada 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/directive/directive.go
@@ -90,7 +90,7 @@ func checkGoFile(pass *analysis.Pass, f *ast.File) {
func checkOtherFile(pass *analysis.Pass, filename string) error {
// We cannot use the Go parser, since is not a Go source file.
// Read the raw bytes instead.
- content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
+ content, tf, err := analysisutil.ReadFile(pass, filename)
if err != nil {
return err
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go
index 0b3ded47ea..6eff3a20fe 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go
@@ -48,7 +48,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
}
for _, fname := range sfiles {
- content, tf, err := analysisutil.ReadFile(pass.Fset, fname)
+ content, tf, err := analysisutil.ReadFile(pass, fname)
if err != nil {
return nil, err
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
index 89291602a5..f7f071dc8b 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
@@ -14,6 +14,7 @@ import (
"go/types"
"os"
+ "golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/analysisinternal"
)
@@ -60,12 +61,16 @@ func HasSideEffects(info *types.Info, e ast.Expr) bool {
// ReadFile reads a file and adds it to the FileSet
// so that we can report errors against it using lineStart.
-func ReadFile(fset *token.FileSet, filename string) ([]byte, *token.File, error) {
- content, err := os.ReadFile(filename)
+func ReadFile(pass *analysis.Pass, filename string) ([]byte, *token.File, error) {
+ readFile := pass.ReadFile
+ if readFile == nil {
+ readFile = os.ReadFile
+ }
+ content, err := readFile(filename)
if err != nil {
return nil, nil, err
}
- tf := fset.AddFile(filename, -1, len(content))
+ tf := pass.Fset.AddFile(filename, -1, len(content))
tf.SetLinesForContent(content)
return content, tf, nil
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go
index 1ee16126ad..85da8346f7 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/doc.go
@@ -11,14 +11,53 @@
//
// The check applies to calls of the formatting functions such as
// [fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of
-// those functions.
+// those functions such as [log.Printf]. It reports a variety of
+// mistakes such as syntax errors in the format string and mismatches
+// (of number and type) between the verbs and their arguments.
//
-// In this example, the %d format operator requires an integer operand:
+// See the documentation of the fmt package for the complete set of
+// format operators and their operand types.
+//
+// # Examples
+//
+// The %d format operator requires an integer operand.
+// Here it is incorrectly applied to a string:
//
// fmt.Printf("%d", "hello") // fmt.Printf format %d has arg "hello" of wrong type string
//
-// See the documentation of the fmt package for the complete set of
-// format operators and their operand types.
+// A call to Printf must have as many operands as there are "verbs" in
+// the format string, not too few:
+//
+// fmt.Printf("%d") // fmt.Printf format reads arg 1, but call has 0 args
+//
+// nor too many:
+//
+// fmt.Printf("%d", 1, 2) // fmt.Printf call needs 1 arg, but has 2 args
+//
+// Explicit argument indexes must be no greater than the number of
+// arguments:
+//
+// fmt.Printf("%[3]d", 1, 2) // fmt.Printf call has invalid argument index 3
+//
+// The checker also uses a heuristic to report calls to Print-like
+// functions that appear to have been intended for their Printf-like
+// counterpart:
+//
+// log.Print("%d", 123) // log.Print call has possible formatting directive %d
+//
+// # Inferred printf wrappers
+//
+// Functions that delegate their arguments to fmt.Printf are
+// considered "printf wrappers"; calls to them are subject to the same
+// checking. In this example, logf is a printf wrapper:
+//
+// func logf(level int, format string, args ...any) {
+// if enabled(level) {
+// log.Printf(format, args...)
+// }
+// }
+//
+// logf(3, "invalid request: %v") // logf format reads arg 1, but call has 0 args
//
// To enable printf checking on a function that is not found by this
// analyzer's heuristics (for example, because control is obscured by
@@ -26,14 +65,19 @@
//
// func MyPrintf(format string, args ...any) {
// if false {
-// _ = fmt.Sprintf(format, args...) // enable printf checker
+// _ = fmt.Sprintf(format, args...) // enable printf checking
// }
// ...
// }
//
-// The -funcs flag specifies a comma-separated list of names of additional
-// known formatting functions or methods. If the name contains a period,
-// it must denote a specific function using one of the following forms:
+// # Specifying printf wrappers by flag
+//
+// The -funcs flag specifies a comma-separated list of names of
+// additional known formatting functions or methods. (This legacy flag
+// is rarely used due to the automatic inference described above.)
+//
+// If the name contains a period, it must denote a specific function
+// using one of the following forms:
//
// dir/pkg.Function
// dir/pkg.Type.Method
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go
new file mode 100644
index 0000000000..75d8697759
--- /dev/null
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go
@@ -0,0 +1,159 @@
+// Copyright 2024 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 stdversion reports uses of standard library symbols that are
+// "too new" for the Go version in force in the referring file.
+package stdversion
+
+import (
+ "go/ast"
+ "go/build"
+ "go/types"
+ "regexp"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typesinternal"
+ "golang.org/x/tools/internal/versions"
+)
+
+const Doc = `report uses of too-new standard library symbols
+
+The stdversion analyzer reports references to symbols in the standard
+library that were introduced by a Go release higher than the one in
+force in the referring file. (Recall that the file's Go version is
+defined by the 'go' directive its module's go.mod file, or by a
+"//go:build go1.X" build tag at the top of the file.)
+
+The analyzer does not report a diagnostic for a reference to a "too
+new" field or method of a type that is itself "too new", as this may
+have false positives, for example if fields or methods are accessed
+through a type alias that is guarded by a Go version constraint.
+`
+
+var Analyzer = &analysis.Analyzer{
+ Name: "stdversion",
+ Doc: Doc,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdversion",
+ RunDespiteErrors: true,
+ Run: run,
+}
+
+func run(pass *analysis.Pass) (any, error) {
+ // Prior to go1.22, versions.FileVersion returns only the
+ // toolchain version, which is of no use to us, so
+ // disable this analyzer on earlier versions.
+ if !slicesContains(build.Default.ReleaseTags, "go1.22") {
+ return nil, nil
+ }
+
+ // Don't report diagnostics for modules marked before go1.21,
+ // since at that time the go directive wasn't clearly
+ // specified as a toolchain requirement.
+ //
+ // TODO(adonovan): after go1.21, call GoVersion directly.
+ pkgVersion := any(pass.Pkg).(interface{ GoVersion() string }).GoVersion()
+ if !versions.AtLeast(pkgVersion, "go1.21") {
+ return nil, nil
+ }
+
+ // disallowedSymbols returns the set of standard library symbols
+ // in a given package that are disallowed at the specified Go version.
+ type key struct {
+ pkg *types.Package
+ version string
+ }
+ memo := make(map[key]map[types.Object]string) // records symbol's minimum Go version
+ disallowedSymbols := func(pkg *types.Package, version string) map[types.Object]string {
+ k := key{pkg, version}
+ disallowed, ok := memo[k]
+ if !ok {
+ disallowed = typesinternal.TooNewStdSymbols(pkg, version)
+ memo[k] = disallowed
+ }
+ return disallowed
+ }
+
+ // Scan the syntax looking for references to symbols
+ // that are disallowed by the version of the file.
+ inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+ nodeFilter := []ast.Node{
+ (*ast.File)(nil),
+ (*ast.Ident)(nil),
+ }
+ var fileVersion string // "" => no check
+ inspect.Preorder(nodeFilter, func(n ast.Node) {
+ switch n := n.(type) {
+ case *ast.File:
+ if isGenerated(n) {
+ // Suppress diagnostics in generated files (such as cgo).
+ fileVersion = ""
+ } else {
+ fileVersion = versions.Lang(versions.FileVersion(pass.TypesInfo, n))
+ // (may be "" if unknown)
+ }
+
+ case *ast.Ident:
+ if fileVersion != "" {
+ if obj, ok := pass.TypesInfo.Uses[n]; ok && obj.Pkg() != nil {
+ disallowed := disallowedSymbols(obj.Pkg(), fileVersion)
+ if minVersion, ok := disallowed[origin(obj)]; ok {
+ noun := "module"
+ if fileVersion != pkgVersion {
+ noun = "file"
+ }
+ pass.ReportRangef(n, "%s.%s requires %v or later (%s is %s)",
+ obj.Pkg().Name(), obj.Name(), minVersion, noun, fileVersion)
+ }
+ }
+ }
+ }
+ })
+ return nil, nil
+}
+
+// Reduced from x/tools/gopls/internal/golang/util.go. Good enough for now.
+// TODO(adonovan): use ast.IsGenerated in go1.21.
+func isGenerated(f *ast.File) bool {
+ for _, group := range f.Comments {
+ for _, comment := range group.List {
+ if matched := generatedRx.MatchString(comment.Text); matched {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// Matches cgo generated comment as well as the proposed standard:
+//
+// https://golang.org/s/generatedcode
+var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
+
+// origin returns the original uninstantiated symbol for obj.
+func origin(obj types.Object) types.Object {
+ switch obj := obj.(type) {
+ case *types.Var:
+ return obj.Origin()
+ case *types.Func:
+ return obj.Origin()
+ case *types.TypeName:
+ if named, ok := obj.Type().(*types.Named); ok { // (don't unalias)
+ return named.Origin().Obj()
+ }
+ }
+ return obj
+}
+
+// TODO(adonovan): use go1.21 slices.Contains.
+func slicesContains[S ~[]E, E comparable](slice S, x E) bool {
+ for _, elem := range slice {
+ if elem == x {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
index 39d0d9e429..f5e760ca26 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
@@ -447,6 +447,18 @@ func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
}
}
+type tokenRange struct {
+ p, e token.Pos
+}
+
+func (r tokenRange) Pos() token.Pos {
+ return r.p
+}
+
+func (r tokenRange) End() token.Pos {
+ return r.e
+}
+
func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
// Want functions with 0 results and 1 parameter.
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
@@ -464,12 +476,11 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 {
// Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
// We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
- // TODO(adonovan): use ReportRangef(tparams).
- pass.Reportf(fn.Pos(), "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
+ at := tokenRange{tparams.Opening, tparams.Closing}
+ pass.ReportRangef(at, "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
}
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
- // TODO(adonovan): use ReportRangef(fn.Name).
- pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
+ pass.ReportRangef(fn.Name, "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
}
}
diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
index 1fa0d1f68f..d77fb203d8 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
@@ -49,6 +49,7 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
+ "golang.org/x/tools/internal/analysisinternal"
"golang.org/x/tools/internal/facts"
"golang.org/x/tools/internal/versions"
)
@@ -377,6 +378,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
ExportPackageFact: facts.ExportPackageFact,
AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
}
+ pass.ReadFile = analysisinternal.MakeReadFile(pass)
t0 := time.Now()
act.result, act.err = a.Run(pass)
diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go
index e154be0bd6..a92f80dd2d 100644
--- a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go
+++ b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Package typeutil defines various utilities for types, such as Map,
-// a mapping from types.Type to interface{} values.
+// a mapping from types.Type to any values.
package typeutil // import "golang.org/x/tools/go/types/typeutil"
import (
@@ -17,7 +17,7 @@ import (
)
// Map is a hash-table-based mapping from types (types.Type) to
-// arbitrary interface{} values. The concrete types that implement
+// arbitrary any values. The concrete types that implement
// the Type interface are pointers. Since they are not canonicalized,
// == cannot be used to check for equivalence, and thus we cannot
// simply use a Go map.
@@ -34,7 +34,7 @@ type Map struct {
// entry is an entry (key/value association) in a hash bucket.
type entry struct {
key types.Type
- value interface{}
+ value any
}
// SetHasher sets the hasher used by Map.
@@ -82,7 +82,7 @@ func (m *Map) Delete(key types.Type) bool {
// At returns the map entry for the given key.
// The result is nil if the entry is not present.
-func (m *Map) At(key types.Type) interface{} {
+func (m *Map) At(key types.Type) any {
if m != nil && m.table != nil {
for _, e := range m.table[m.hasher.Hash(key)] {
if e.key != nil && types.Identical(key, e.key) {
@@ -95,7 +95,7 @@ func (m *Map) At(key types.Type) interface{} {
// Set sets the map entry for key to val,
// and returns the previous entry, if any.
-func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+func (m *Map) Set(key types.Type, value any) (prev any) {
if m.table != nil {
hash := m.hasher.Hash(key)
bucket := m.table[hash]
@@ -142,7 +142,7 @@ func (m *Map) Len() int {
// f will not be invoked for it, but if f inserts a map entry that
// Iterate has not yet reached, whether or not f will be invoked for
// it is unspecified.
-func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+func (m *Map) Iterate(f func(key types.Type, value any)) {
if m != nil {
for _, bucket := range m.table {
for _, e := range bucket {
@@ -158,7 +158,7 @@ func (m *Map) Iterate(f func(key types.Type, value interface{})) {
// The order is unspecified.
func (m *Map) Keys() []types.Type {
keys := make([]types.Type, 0, m.Len())
- m.Iterate(func(key types.Type, _ interface{}) {
+ m.Iterate(func(key types.Type, _ any) {
keys = append(keys, key)
})
return keys
@@ -171,7 +171,7 @@ func (m *Map) toString(values bool) string {
var buf bytes.Buffer
fmt.Fprint(&buf, "{")
sep := ""
- m.Iterate(func(key types.Type, value interface{}) {
+ m.Iterate(func(key types.Type, value any) {
fmt.Fprint(&buf, sep)
sep = ", "
fmt.Fprint(&buf, key)
@@ -209,7 +209,7 @@ type Hasher struct {
memo map[types.Type]uint32
// ptrMap records pointer identity.
- ptrMap map[interface{}]uint32
+ ptrMap map[any]uint32
// sigTParams holds type parameters from the signature being hashed.
// Signatures are considered identical modulo renaming of type parameters, so
@@ -227,7 +227,7 @@ type Hasher struct {
func MakeHasher() Hasher {
return Hasher{
memo: make(map[types.Type]uint32),
- ptrMap: make(map[interface{}]uint32),
+ ptrMap: make(map[any]uint32),
sigTParams: nil,
}
}
@@ -261,7 +261,7 @@ func (h Hasher) hashFor(t types.Type) uint32 {
return uint32(t.Kind())
case *aliases.Alias:
- return h.Hash(t.Underlying())
+ return h.Hash(aliases.Unalias(t))
case *types.Array:
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
@@ -432,7 +432,7 @@ func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
// pointers values are not dependent on the GC.
-func (h Hasher) hashPtr(ptr interface{}) uint32 {
+func (h Hasher) hashPtr(ptr any) uint32 {
if hash, ok := h.ptrMap[ptr]; ok {
return hash
}
@@ -462,7 +462,7 @@ func (h Hasher) shallowHash(t types.Type) uint32 {
// so there's no need to optimize anything else.
switch t := t.(type) {
case *aliases.Alias:
- return h.shallowHash(t.Underlying())
+ return h.shallowHash(aliases.Unalias(t))
case *types.Signature:
var hash uint32 = 604171
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases.go b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases.go
index f89112c8ee..c24c2eee45 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases.go
@@ -16,10 +16,14 @@ import (
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
-// When GoVersion>=1.22 and GODEBUG=gotypesalias=1,
-// the Type() of the return value is a *types.Alias.
-func NewAlias(pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
- if enabled() {
+// The enabled parameter determines whether the resulting [TypeName]'s
+// type is an [types.Alias]. Its value must be the result of a call to
+// [Enabled], which computes the effective value of
+// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
+// function is expensive and should be called once per task (e.g.
+// package import), not once per call to NewAlias.
+func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
+ if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
newAlias(tname, rhs)
return tname
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go
index 1872b56ff8..c027b9f315 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go121.go
@@ -15,16 +15,17 @@ import (
// It will never be created by go/types.
type Alias struct{}
-func (*Alias) String() string { panic("unreachable") }
-
+func (*Alias) String() string { panic("unreachable") }
func (*Alias) Underlying() types.Type { panic("unreachable") }
-
-func (*Alias) Obj() *types.TypeName { panic("unreachable") }
+func (*Alias) Obj() *types.TypeName { panic("unreachable") }
+func Rhs(alias *Alias) types.Type { panic("unreachable") }
// Unalias returns the type t for go <=1.21.
func Unalias(t types.Type) types.Type { return t }
-// Always false for go <=1.21. Ignores GODEBUG.
-func enabled() bool { return false }
-
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
+
+// Enabled reports whether [NewAlias] should create [types.Alias] types.
+//
+// Before go1.22, this function always returns false.
+func Enabled() bool { return false }
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go
index 8b92116284..b329954841 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/aliases/aliases_go122.go
@@ -12,14 +12,22 @@ import (
"go/parser"
"go/token"
"go/types"
- "os"
- "strings"
- "sync"
)
// Alias is an alias of types.Alias.
type Alias = types.Alias
+// Rhs returns the type on the right-hand side of the alias declaration.
+func Rhs(alias *Alias) types.Type {
+ if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
+ return alias.Rhs() // go1.23+
+ }
+
+ // go1.22's Alias didn't have the Rhs method,
+ // so Unalias is the best we can do.
+ return Unalias(alias)
+}
+
// Unalias is a wrapper of types.Unalias.
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
@@ -33,40 +41,23 @@ func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
return a
}
-// enabled returns true when types.Aliases are enabled.
-func enabled() bool {
- // Use the gotypesalias value in GODEBUG if set.
- godebug := os.Getenv("GODEBUG")
- value := -1 // last set value.
- for _, f := range strings.Split(godebug, ",") {
- switch f {
- case "gotypesalias=1":
- value = 1
- case "gotypesalias=0":
- value = 0
- }
- }
- switch value {
- case 0:
- return false
- case 1:
- return true
- default:
- return aliasesDefault()
- }
-}
-
-// aliasesDefault reports if aliases are enabled by default.
-func aliasesDefault() bool {
- // Dynamically check if Aliases will be produced from go/types.
- aliasesDefaultOnce.Do(func() {
- fset := token.NewFileSet()
- f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
- pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
- _, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias)
- })
- return gotypesaliasDefault
+// Enabled reports whether [NewAlias] should create [types.Alias] types.
+//
+// This function is expensive! Call it sparingly.
+func Enabled() bool {
+ // The only reliable way to compute the answer is to invoke go/types.
+ // We don't parse the GODEBUG environment variable, because
+ // (a) it's tricky to do so in a manner that is consistent
+ // with the godebug package; in particular, a simple
+ // substring check is not good enough. The value is a
+ // rightmost-wins list of options. But more importantly:
+ // (b) it is impossible to detect changes to the effective
+ // setting caused by os.Setenv("GODEBUG"), as happens in
+ // many tests. Therefore any attempt to cache the result
+ // is just incorrect.
+ fset := token.NewFileSet()
+ f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
+ pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
+ _, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
+ return enabled
}
-
-var gotypesaliasDefault bool
-var aliasesDefaultOnce sync.Once
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
index c3022a2862..2c406ded0c 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go
@@ -12,8 +12,10 @@ import (
"go/ast"
"go/token"
"go/types"
+ "os"
"strconv"
+ "golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/aliases"
)
@@ -32,22 +34,22 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos
func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
// TODO(adonovan): think about generics, and also generic aliases.
under := aliases.Unalias(typ)
- // Don't call Underlying unconditionally: although it removed
+ // Don't call Underlying unconditionally: although it removes
// Named and Alias, it also removes TypeParam.
- if n, ok := typ.(*types.Named); ok {
+ if n, ok := under.(*types.Named); ok {
under = n.Underlying()
}
- switch u := under.(type) {
+ switch under := under.(type) {
case *types.Basic:
switch {
- case u.Info()&types.IsNumeric != 0:
+ case under.Info()&types.IsNumeric != 0:
return &ast.BasicLit{Kind: token.INT, Value: "0"}
- case u.Info()&types.IsBoolean != 0:
+ case under.Info()&types.IsBoolean != 0:
return &ast.Ident{Name: "false"}
- case u.Info()&types.IsString != 0:
+ case under.Info()&types.IsString != 0:
return &ast.BasicLit{Kind: token.STRING, Value: `""`}
default:
- panic(fmt.Sprintf("unknown basic type %v", u))
+ panic(fmt.Sprintf("unknown basic type %v", under))
}
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
return ast.NewIdent("nil")
@@ -178,7 +180,7 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
List: returns,
},
}
- case *types.Named:
+ case interface{ Obj() *types.TypeName }: // *types.{Alias,Named,TypeParam}
if t.Obj().Pkg() == nil {
return ast.NewIdent(t.Obj().Name())
}
@@ -393,3 +395,38 @@ func equivalentTypes(want, got types.Type) bool {
}
return types.AssignableTo(want, got)
}
+
+// MakeReadFile returns a simple implementation of the Pass.ReadFile function.
+func MakeReadFile(pass *analysis.Pass) func(filename string) ([]byte, error) {
+ return func(filename string) ([]byte, error) {
+ if err := checkReadable(pass, filename); err != nil {
+ return nil, err
+ }
+ return os.ReadFile(filename)
+ }
+}
+
+// checkReadable enforces the access policy defined by the ReadFile field of [analysis.Pass].
+func checkReadable(pass *analysis.Pass, filename string) error {
+ if slicesContains(pass.OtherFiles, filename) ||
+ slicesContains(pass.IgnoredFiles, filename) {
+ return nil
+ }
+ for _, f := range pass.Files {
+ // TODO(adonovan): use go1.20 f.FileStart
+ if pass.Fset.File(f.Pos()).Name() == filename {
+ return nil
+ }
+ }
+ return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename)
+}
+
+// TODO(adonovan): use go1.21 slices.Contains.
+func slicesContains[S ~[]E, E comparable](slice S, x E) bool {
+ for _, elem := range slice {
+ if elem == x {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/facts/facts.go b/src/cmd/vendor/golang.org/x/tools/internal/facts/facts.go
index f0aa97ec1b..e1c18d373c 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/facts/facts.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/facts/facts.go
@@ -295,10 +295,10 @@ func (s *Set) Encode() []byte {
// we aren't careful about which structs or methods
// we rexport: it should be only those referenced
// from the API of s.pkg.
- // TOOD(adonovan): opt: be more precise. e.g.
+ // TODO(adonovan): opt: be more precise. e.g.
// intersect with the set of objects computed by
// importMap(s.pkg.Imports()).
- // TOOD(adonovan): opt: implement "shallow" facts.
+ // TODO(adonovan): opt: implement "shallow" facts.
if k.pkg != s.pkg {
if k.obj == nil {
continue // imported package fact
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go
index 9771b8c3d6..89bd256dc6 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go
@@ -73,7 +73,7 @@ func IsTypeParam(t types.Type) bool {
// implements the following rule for uninstantiated generic types:
//
// If V and T are generic named types, then V is considered assignable to T if,
-// for every possible instantation of V[A_1, ..., A_N], the instantiation
+// for every possible instantiation of V[A_1, ..., A_N], the instantiation
// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
//
// If T has structural constraints, they must be satisfied by V.
diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go
index e0c27ed251..834e05381c 100644
--- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go
+++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go
@@ -1449,10 +1449,10 @@ const (
NotAGenericType
// WrongTypeArgCount occurs when a type or function is instantiated with an
- // incorrent number of type arguments, including when a generic type or
+ // incorrect number of type arguments, including when a generic type or
// function is used without instantiation.
//
- // Errors inolving failed type inference are assigned other error codes.
+ // Errors involving failed type inference are assigned other error codes.
//
// Example:
// type T[p any] int
diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt
index 293c69c4c2..9ff5cd0f92 100644
--- a/src/cmd/vendor/modules.txt
+++ b/src/cmd/vendor/modules.txt
@@ -25,7 +25,7 @@ golang.org/x/arch/x86/x86asm
# golang.org/x/build v0.0.0-20240222153247-cf4ed81bb19f
## explicit; go 1.21
golang.org/x/build/relnote
-# golang.org/x/mod v0.16.0
+# golang.org/x/mod v0.17.0
## explicit; go 1.18
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile
@@ -36,7 +36,7 @@ golang.org/x/mod/sumdb/dirhash
golang.org/x/mod/sumdb/note
golang.org/x/mod/sumdb/tlog
golang.org/x/mod/zip
-# golang.org/x/sync v0.6.0
+# golang.org/x/sync v0.7.0
## explicit; go 1.18
golang.org/x/sync/errgroup
golang.org/x/sync/semaphore
@@ -71,7 +71,7 @@ golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
-# golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14
+# golang.org/x/tools v0.20.1-0.20240429173604-74c9cfe4d22f
## explicit; go 1.19
golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover
@@ -103,6 +103,7 @@ golang.org/x/tools/go/analysis/passes/shift
golang.org/x/tools/go/analysis/passes/sigchanyzer
golang.org/x/tools/go/analysis/passes/slog
golang.org/x/tools/go/analysis/passes/stdmethods
+golang.org/x/tools/go/analysis/passes/stdversion
golang.org/x/tools/go/analysis/passes/stringintconv
golang.org/x/tools/go/analysis/passes/structtag
golang.org/x/tools/go/analysis/passes/testinggoroutine
diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go
index c5197284b5..7b9a700635 100644
--- a/src/cmd/vet/main.go
+++ b/src/cmd/vet/main.go
@@ -32,6 +32,7 @@ import (
"golang.org/x/tools/go/analysis/passes/sigchanyzer"
"golang.org/x/tools/go/analysis/passes/slog"
"golang.org/x/tools/go/analysis/passes/stdmethods"
+ "golang.org/x/tools/go/analysis/passes/stdversion"
"golang.org/x/tools/go/analysis/passes/stringintconv"
"golang.org/x/tools/go/analysis/passes/structtag"
"golang.org/x/tools/go/analysis/passes/testinggoroutine"
@@ -70,6 +71,7 @@ func main() {
sigchanyzer.Analyzer,
slog.Analyzer,
stdmethods.Analyzer,
+ stdversion.Analyzer,
stringintconv.Analyzer,
structtag.Analyzer,
tests.Analyzer,
diff --git a/src/cmd/vet/testdata/stdversion/go.mod b/src/cmd/vet/testdata/stdversion/go.mod
new file mode 100644
index 0000000000..90ae83b840
--- /dev/null
+++ b/src/cmd/vet/testdata/stdversion/go.mod
@@ -0,0 +1,3 @@
+module stdversion
+
+go 1.21
diff --git a/src/cmd/vet/testdata/stdversion/stdversion.go b/src/cmd/vet/testdata/stdversion/stdversion.go
new file mode 100644
index 0000000000..ba5846cd13
--- /dev/null
+++ b/src/cmd/vet/testdata/stdversion/stdversion.go
@@ -0,0 +1,5 @@
+package stdversion
+
+import "reflect"
+
+var _ = reflect.TypeFor[int]() // ERROR "reflect.TypeFor requires go1.22 or later \(module is go1.21\)"
diff --git a/src/cmd/vet/vet_test.go b/src/cmd/vet/vet_test.go
index 278a88afb3..ad42cf1d7c 100644
--- a/src/cmd/vet/vet_test.go
+++ b/src/cmd/vet/vet_test.go
@@ -152,6 +152,37 @@ func TestVet(t *testing.T) {
t.Log("vet stderr:\n", cmd.Stderr)
}
})
+
+ // The stdversion analyzer requires a lower-than-tip go
+ // version in its go.mod file for it to report anything.
+ // So again we use a testdata go.mod file to "downgrade".
+ t.Run("stdversion", func(t *testing.T) {
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "vet", "-vettool="+vetPath(t), ".")
+ cmd.Env = append(os.Environ(), "GOWORK=off")
+ cmd.Dir = "testdata/stdversion"
+ cmd.Stderr = new(strings.Builder) // all vet output goes to stderr
+ cmd.Run()
+ stderr := cmd.Stderr.(fmt.Stringer).String()
+
+ filename := filepath.FromSlash("testdata/stdversion/stdversion.go")
+
+ // Unlike the tests above, which runs vet in cmd/vet/, this one
+ // runs it in subdirectory, so the "full names" in the output
+ // are in fact short "./rangeloop.go".
+ // But we can't just pass "./rangeloop.go" as the "full name"
+ // argument to errorCheck as it does double duty as both a
+ // string that appears in the output, and as file name
+ // openable relative to the test directory, containing text
+ // expectations.
+ //
+ // So, we munge the file.
+ stderr = strings.ReplaceAll(stderr, filepath.FromSlash("./stdversion.go"), filename)
+
+ if err := errorCheck(stderr, false, filename, filepath.Base(filename)); err != nil {
+ t.Errorf("error check failed: %s", err)
+ t.Log("vet stderr:\n", cmd.Stderr)
+ }
+ })
}
func cgoEnabled(t *testing.T) bool {
diff --git a/src/compress/bzip2/bzip2.go b/src/compress/bzip2/bzip2.go
index 73e201b80e..d41ff2c83b 100644
--- a/src/compress/bzip2/bzip2.go
+++ b/src/compress/bzip2/bzip2.go
@@ -27,8 +27,8 @@ type reader struct {
blockCRC uint32
wantBlockCRC uint32
setupDone bool // true if we have parsed the bzip2 header.
- blockSize int // blockSize in bytes, i.e. 900 * 1000.
eof bool
+ blockSize int // blockSize in bytes, i.e. 900 * 1000.
c [256]uint // the ``C'' array for the inverse BWT.
tt []uint32 // mirrors the ``tt'' array in the bzip2 source and contains the P array in the upper 24 bits.
tPos uint32 // Index of the next output byte in tt.
diff --git a/src/compress/flate/deflate.go b/src/compress/flate/deflate.go
index ea343b2298..0e07afab7d 100644
--- a/src/compress/flate/deflate.go
+++ b/src/compress/flate/deflate.go
@@ -87,7 +87,6 @@ type compressor struct {
// compression algorithm
fill func(*compressor, []byte) int // copy data to window
step func(*compressor) // process window
- sync bool // requesting flush
bestSpeed *deflateFast // Encoder for BestSpeed
// Input hash chains
@@ -107,6 +106,8 @@ type compressor struct {
blockStart int // window index where current tokens start
byteAvailable bool // if true, still need to process window[index-1].
+ sync bool // requesting flush
+
// queued output tokens
tokens []token
diff --git a/src/compress/gzip/gzip.go b/src/compress/gzip/gzip.go
index ab4598d89f..5f24444237 100644
--- a/src/compress/gzip/gzip.go
+++ b/src/compress/gzip/gzip.go
@@ -30,11 +30,11 @@ type Writer struct {
w io.Writer
level int
wroteHeader bool
+ closed bool
+ buf [10]byte
compressor *flate.Writer
digest uint32 // CRC-32, IEEE polynomial (section 8)
size uint32 // Uncompressed size (section 2.3.1)
- closed bool
- buf [10]byte
err error
}
diff --git a/src/compress/lzw/writer.go b/src/compress/lzw/writer.go
index 99ad3501c5..9fbb08dbae 100644
--- a/src/compress/lzw/writer.go
+++ b/src/compress/lzw/writer.go
@@ -36,15 +36,15 @@ const (
type Writer struct {
// w is the writer that compressed bytes are written to.
w writer
+ // litWidth is the width in bits of literal codes.
+ litWidth uint
// order, write, bits, nBits and width are the state for
// converting a code stream into a byte stream.
order Order
write func(*Writer, uint32) error
- bits uint32
nBits uint
width uint
- // litWidth is the width in bits of literal codes.
- litWidth uint
+ bits uint32
// hi is the code implied by the next code emission.
// overflow is the code at which hi overflows the code width.
hi, overflow uint32
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 0035a65cfc..bd55c7caf0 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -12,13 +12,13 @@ package json
import (
"bytes"
+ "cmp"
"encoding"
"encoding/base64"
"fmt"
"math"
"reflect"
"slices"
- "sort"
"strconv"
"strings"
"sync"
@@ -1162,21 +1162,23 @@ func typeFields(t reflect.Type) structFields {
}
}
- sort.Slice(fields, func(i, j int) bool {
- x := fields
+ slices.SortFunc(fields, func(a, b field) int {
// sort field by name, breaking ties with depth, then
// breaking ties with "name came from json tag", then
// breaking ties with index sequence.
- if x[i].name != x[j].name {
- return x[i].name < x[j].name
+ if c := strings.Compare(a.name, b.name); c != 0 {
+ return c
}
- if len(x[i].index) != len(x[j].index) {
- return len(x[i].index) < len(x[j].index)
+ if c := cmp.Compare(len(a.index), len(b.index)); c != 0 {
+ return c
}
- if x[i].tag != x[j].tag {
- return x[i].tag
+ if a.tag != b.tag {
+ if a.tag {
+ return -1
+ }
+ return +1
}
- return slices.Compare(x[i].index, x[j].index) == -1
+ return slices.Compare(a.index, b.index)
})
// Delete all fields that are hidden by the Go rules for embedded fields,
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index ffe35d62f9..4f66848f1f 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -169,10 +169,7 @@ func (v *Map) Init() *Map {
v.keysMu.Lock()
defer v.keysMu.Unlock()
v.keys = v.keys[:0]
- v.m.Range(func(k, _ any) bool {
- v.m.Delete(k)
- return true
- })
+ v.m.Clear()
return v
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 0ea34b1bd7..14880d9ef1 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -66,6 +66,7 @@ var depsRules = `
internal/goexperiment,
internal/goos
< internal/bytealg
+ < internal/stringslite
< internal/itoa
< internal/unsafeheader
< runtime/internal/sys
@@ -173,7 +174,7 @@ var depsRules = `
io/fs
< internal/testlog
< internal/poll
- < internal/safefilepath
+ < internal/filepathlite
< os
< os/signal;
@@ -182,7 +183,7 @@ var depsRules = `
unicode, fmt !< net, os, os/signal;
- os/signal, internal/safefilepath, STR
+ os/signal, internal/filepathlite, STR
< path/filepath
< io/ioutil;
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 1889694342..83bc64772f 100644
--- a/src/go/types/predicates.go
+++ b/src/go/types/predicates.go
@@ -521,7 +521,9 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
func Default(t Type) Type {
- if t, ok := Unalias(t).(*Basic); ok {
+ // Alias and named types cannot denote untyped types
+ // so there's no need to call Unalias or under, below.
+ if t, _ := t.(*Basic); t != nil {
switch t.kind {
case UntypedBool:
return Typ[Bool]
diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go
index 30b4948216..bfb51fd2e5 100644
--- a/src/go/types/stmt.go
+++ b/src/go/types/stmt.go
@@ -923,19 +923,26 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
+ assert(obj.typ == nil)
+
+ // initialize lhs iteration variable, if any
+ typ := rhs[i]
+ if typ == nil {
+ obj.typ = Typ[Invalid]
+ obj.used = true // don't complain about unused variable
+ continue
+ }
// initialize lhs variable
if constIntRange {
check.initVar(obj, &x, "range clause")
- } else if typ := rhs[i]; typ != nil {
+ } else {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
- } else {
- obj.typ = Typ[Invalid]
- obj.used = true // don't complain about unused variable
}
+ assert(obj.typ != nil)
}
// declare variables
@@ -954,9 +961,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
continue
}
+ // assign to lhs iteration variable, if any
+ typ := rhs[i]
+ if typ == nil {
+ continue
+ }
+
if constIntRange {
check.assignVar(lhs, nil, &x, "range clause")
- } else if typ := rhs[i]; typ != nil {
+ } else {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
diff --git a/src/hash/maphash/smhasher_test.go b/src/hash/maphash/smhasher_test.go
index 7fa30aa12f..b17ef794f7 100644
--- a/src/hash/maphash/smhasher_test.go
+++ b/src/hash/maphash/smhasher_test.go
@@ -8,6 +8,7 @@ package maphash
import (
"fmt"
+ "internal/testenv"
"math"
"math/rand"
"runtime"
@@ -30,6 +31,7 @@ var fixedSeed = MakeSeed()
// hash should not depend on values outside key.
// hash should not depend on alignment.
func TestSmhasherSanity(t *testing.T) {
+ t.Parallel()
r := rand.New(rand.NewSource(1234))
const REP = 10
const KEYMAX = 128
@@ -118,6 +120,7 @@ func (s *hashSet) check(t *testing.T) {
// a string plus adding zeros must make distinct hashes
func TestSmhasherAppendedZeros(t *testing.T) {
+ t.Parallel()
s := "hello" + strings.Repeat("\x00", 256)
h := newHashSet()
for i := 0; i <= len(s); i++ {
@@ -128,6 +131,7 @@ func TestSmhasherAppendedZeros(t *testing.T) {
// All 0-3 byte strings have distinct hashes.
func TestSmhasherSmallKeys(t *testing.T) {
+ testenv.ParallelOn64Bit(t)
h := newHashSet()
var b [3]byte
for i := 0; i < 256; i++ {
@@ -149,6 +153,7 @@ func TestSmhasherSmallKeys(t *testing.T) {
// Different length strings of all zeros have distinct hashes.
func TestSmhasherZeros(t *testing.T) {
+ t.Parallel()
N := 256 * 1024
if testing.Short() {
N = 1024
@@ -169,6 +174,7 @@ func TestSmhasherTwoNonzero(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ testenv.ParallelOn64Bit(t)
h := newHashSet()
for n := 2; n <= 16; n++ {
twoNonZero(h, n)
@@ -211,6 +217,7 @@ func TestSmhasherCyclic(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ t.Parallel()
r := rand.New(rand.NewSource(1234))
const REPEAT = 8
const N = 1000000
@@ -240,6 +247,7 @@ func TestSmhasherSparse(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ t.Parallel()
h := newHashSet()
sparse(t, h, 32, 6)
sparse(t, h, 40, 6)
@@ -278,6 +286,7 @@ func TestSmhasherPermutation(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ testenv.ParallelOn64Bit(t)
h := newHashSet()
permutation(t, h, []uint32{0, 1, 2, 3, 4, 5, 6, 7}, 8)
permutation(t, h, []uint32{0, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 8)
@@ -344,6 +353,7 @@ func TestSmhasherAvalanche(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ t.Parallel()
avalancheTest1(t, &bytesKey{make([]byte, 2)})
avalancheTest1(t, &bytesKey{make([]byte, 4)})
avalancheTest1(t, &bytesKey{make([]byte, 8)})
@@ -407,6 +417,7 @@ func avalancheTest1(t *testing.T, k key) {
// All bit rotations of a set of distinct keys
func TestSmhasherWindowed(t *testing.T) {
+ t.Parallel()
windowed(t, &bytesKey{make([]byte, 128)})
}
func windowed(t *testing.T, k key) {
@@ -438,6 +449,7 @@ func TestSmhasherText(t *testing.T) {
if testing.Short() {
t.Skip("Skipping in short mode")
}
+ t.Parallel()
h := newHashSet()
text(t, h, "Foo", "Bar")
text(t, h, "FooBar", "")
@@ -472,6 +484,7 @@ func TestSmhasherSeed(t *testing.T) {
if unsafe.Sizeof(uintptr(0)) == 4 {
t.Skip("32-bit platforms don't have ideal seed-input distributions (see issue 33988)")
}
+ t.Parallel()
h := newHashSet()
const N = 100000
s := "hello"
diff --git a/src/internal/abi/runtime.go b/src/internal/abi/runtime.go
index 9b91cdf5ef..2a3181a48d 100644
--- a/src/internal/abi/runtime.go
+++ b/src/internal/abi/runtime.go
@@ -4,5 +4,8 @@
package abi
-// ZeroValSize is the size in bytes of runtime.zeroVal.
+// ZeroValSize is the size in bytes of [ZeroVal].
const ZeroValSize = 1024
+
+// ZeroVal is a region containing all zero bytes.
+var ZeroVal [ZeroValSize]byte
diff --git a/src/internal/bisect/bisect.go b/src/internal/bisect/bisect.go
index fa753e80e7..a79bb8000d 100644
--- a/src/internal/bisect/bisect.go
+++ b/src/internal/bisect/bisect.go
@@ -496,7 +496,7 @@ func printStack(w Writer, h uint64, stk []uintptr) error {
for {
f, more := frames.Next()
buf = append(buf, prefix...)
- buf = append(buf, f.Func.Name()...)
+ buf = append(buf, f.Function...)
buf = append(buf, "()\n"...)
buf = append(buf, prefix...)
buf = append(buf, '\t')
diff --git a/src/internal/filepathlite/path.go b/src/internal/filepathlite/path.go
new file mode 100644
index 0000000000..e3daa447d9
--- /dev/null
+++ b/src/internal/filepathlite/path.go
@@ -0,0 +1,274 @@
+// Copyright 2024 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 filepathlite implements a subset of path/filepath,
+// only using packages which may be imported by "os".
+//
+// Tests for these functions are in path/filepath.
+package filepathlite
+
+import (
+ "errors"
+ "internal/stringslite"
+ "io/fs"
+ "slices"
+)
+
+var errInvalidPath = errors.New("invalid path")
+
+// A lazybuf is a lazily constructed path buffer.
+// It supports append, reading previously appended bytes,
+// and retrieving the final string. It does not allocate a buffer
+// to hold the output until that output diverges from s.
+type lazybuf struct {
+ path string
+ buf []byte
+ w int
+ volAndPath string
+ volLen int
+}
+
+func (b *lazybuf) index(i int) byte {
+ if b.buf != nil {
+ return b.buf[i]
+ }
+ return b.path[i]
+}
+
+func (b *lazybuf) append(c byte) {
+ if b.buf == nil {
+ if b.w < len(b.path) && b.path[b.w] == c {
+ b.w++
+ return
+ }
+ b.buf = make([]byte, len(b.path))
+ copy(b.buf, b.path[:b.w])
+ }
+ b.buf[b.w] = c
+ b.w++
+}
+
+func (b *lazybuf) prepend(prefix ...byte) {
+ b.buf = slices.Insert(b.buf, 0, prefix...)
+ b.w += len(prefix)
+}
+
+func (b *lazybuf) string() string {
+ if b.buf == nil {
+ return b.volAndPath[:b.volLen+b.w]
+ }
+ return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
+}
+
+// Clean is filepath.Clean.
+func Clean(path string) string {
+ originalPath := path
+ volLen := volumeNameLen(path)
+ path = path[volLen:]
+ if path == "" {
+ if volLen > 1 && IsPathSeparator(originalPath[0]) && IsPathSeparator(originalPath[1]) {
+ // should be UNC
+ return FromSlash(originalPath)
+ }
+ return originalPath + "."
+ }
+ rooted := IsPathSeparator(path[0])
+
+ // Invariants:
+ // reading from path; r is index of next byte to process.
+ // writing to buf; w is index of next byte to write.
+ // dotdot is index in buf where .. must stop, either because
+ // it is the leading slash or it is a leading ../../.. prefix.
+ n := len(path)
+ out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
+ r, dotdot := 0, 0
+ if rooted {
+ out.append(Separator)
+ r, dotdot = 1, 1
+ }
+
+ for r < n {
+ switch {
+ case IsPathSeparator(path[r]):
+ // empty path element
+ r++
+ case path[r] == '.' && (r+1 == n || IsPathSeparator(path[r+1])):
+ // . element
+ r++
+ case path[r] == '.' && path[r+1] == '.' && (r+2 == n || IsPathSeparator(path[r+2])):
+ // .. element: remove to last separator
+ r += 2
+ switch {
+ case out.w > dotdot:
+ // can backtrack
+ out.w--
+ for out.w > dotdot && !IsPathSeparator(out.index(out.w)) {
+ out.w--
+ }
+ case !rooted:
+ // cannot backtrack, but not rooted, so append .. element.
+ if out.w > 0 {
+ out.append(Separator)
+ }
+ out.append('.')
+ out.append('.')
+ dotdot = out.w
+ }
+ default:
+ // real path element.
+ // add slash if needed
+ if rooted && out.w != 1 || !rooted && out.w != 0 {
+ out.append(Separator)
+ }
+ // copy element
+ for ; r < n && !IsPathSeparator(path[r]); r++ {
+ out.append(path[r])
+ }
+ }
+ }
+
+ // Turn empty string into "."
+ if out.w == 0 {
+ out.append('.')
+ }
+
+ postClean(&out) // avoid creating absolute paths on Windows
+ return FromSlash(out.string())
+}
+
+// IsLocal is filepath.IsLocal.
+func IsLocal(path string) bool {
+ return isLocal(path)
+}
+
+func unixIsLocal(path string) bool {
+ if IsAbs(path) || path == "" {
+ return false
+ }
+ hasDots := false
+ for p := path; p != ""; {
+ var part string
+ part, p, _ = stringslite.Cut(p, "/")
+ if part == "." || part == ".." {
+ hasDots = true
+ break
+ }
+ }
+ if hasDots {
+ path = Clean(path)
+ }
+ if path == ".." || stringslite.HasPrefix(path, "../") {
+ return false
+ }
+ return true
+}
+
+// Localize is filepath.Localize.
+func Localize(path string) (string, error) {
+ if !fs.ValidPath(path) {
+ return "", errInvalidPath
+ }
+ return localize(path)
+}
+
+// ToSlash is filepath.ToSlash.
+func ToSlash(path string) string {
+ if Separator == '/' {
+ return path
+ }
+ return replaceStringByte(path, Separator, '/')
+}
+
+// FromSlash is filepath.ToSlash.
+func FromSlash(path string) string {
+ if Separator == '/' {
+ return path
+ }
+ return replaceStringByte(path, '/', Separator)
+}
+
+func replaceStringByte(s string, old, new byte) string {
+ if stringslite.IndexByte(s, old) == -1 {
+ return s
+ }
+ n := []byte(s)
+ for i := range n {
+ if n[i] == old {
+ n[i] = new
+ }
+ }
+ return string(n)
+}
+
+// Split is filepath.Split.
+func Split(path string) (dir, file string) {
+ vol := VolumeName(path)
+ i := len(path) - 1
+ for i >= len(vol) && !IsPathSeparator(path[i]) {
+ i--
+ }
+ return path[:i+1], path[i+1:]
+}
+
+// Ext is filepath.Ext.
+func Ext(path string) string {
+ for i := len(path) - 1; i >= 0 && !IsPathSeparator(path[i]); i-- {
+ if path[i] == '.' {
+ return path[i:]
+ }
+ }
+ return ""
+}
+
+// Base is filepath.Base.
+func Base(path string) string {
+ if path == "" {
+ return "."
+ }
+ // Strip trailing slashes.
+ for len(path) > 0 && IsPathSeparator(path[len(path)-1]) {
+ path = path[0 : len(path)-1]
+ }
+ // Throw away volume name
+ path = path[len(VolumeName(path)):]
+ // Find the last element
+ i := len(path) - 1
+ for i >= 0 && !IsPathSeparator(path[i]) {
+ i--
+ }
+ if i >= 0 {
+ path = path[i+1:]
+ }
+ // If empty now, it had only slashes.
+ if path == "" {
+ return string(Separator)
+ }
+ return path
+}
+
+// Dir is filepath.Dir.
+func Dir(path string) string {
+ vol := VolumeName(path)
+ i := len(path) - 1
+ for i >= len(vol) && !IsPathSeparator(path[i]) {
+ i--
+ }
+ dir := Clean(path[len(vol) : i+1])
+ if dir == "." && len(vol) > 2 {
+ // must be UNC
+ return vol
+ }
+ return vol + dir
+}
+
+// VolumeName is filepath.VolumeName.
+func VolumeName(path string) string {
+ return FromSlash(path[:volumeNameLen(path)])
+}
+
+// VolumeNameLen returns the length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func VolumeNameLen(path string) int {
+ return volumeNameLen(path)
+}
diff --git a/src/path/filepath/path_nonwindows.go b/src/internal/filepathlite/path_nonwindows.go
index db69f0228b..c9c4c02a3d 100644
--- a/src/path/filepath/path_nonwindows.go
+++ b/src/internal/filepathlite/path_nonwindows.go
@@ -4,6 +4,6 @@
//go:build !windows
-package filepath
+package filepathlite
func postClean(out *lazybuf) {}
diff --git a/src/internal/filepathlite/path_plan9.go b/src/internal/filepathlite/path_plan9.go
new file mode 100644
index 0000000000..5bbb724f91
--- /dev/null
+++ b/src/internal/filepathlite/path_plan9.go
@@ -0,0 +1,41 @@
+// Copyright 2010 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 filepathlite
+
+import (
+ "internal/bytealg"
+ "internal/stringslite"
+)
+
+const (
+ Separator = '/' // OS-specific path separator
+ ListSeparator = '\000' // OS-specific path list separator
+)
+
+func IsPathSeparator(c uint8) bool {
+ return Separator == c
+}
+
+func isLocal(path string) bool {
+ return unixIsLocal(path)
+}
+
+func localize(path string) (string, error) {
+ if path[0] == '#' || bytealg.IndexByteString(path, 0) >= 0 {
+ return "", errInvalidPath
+ }
+ return path, nil
+}
+
+// IsAbs reports whether the path is absolute.
+func IsAbs(path string) bool {
+ return stringslite.HasPrefix(path, "/") || stringslite.HasPrefix(path, "#")
+}
+
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func volumeNameLen(path string) int {
+ return 0
+}
diff --git a/src/internal/filepathlite/path_unix.go b/src/internal/filepathlite/path_unix.go
new file mode 100644
index 0000000000..e31f1ae74f
--- /dev/null
+++ b/src/internal/filepathlite/path_unix.go
@@ -0,0 +1,43 @@
+// Copyright 2010 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 unix || (js && wasm) || wasip1
+
+package filepathlite
+
+import (
+ "internal/bytealg"
+ "internal/stringslite"
+)
+
+const (
+ Separator = '/' // OS-specific path separator
+ ListSeparator = ':' // OS-specific path list separator
+)
+
+func IsPathSeparator(c uint8) bool {
+ return Separator == c
+}
+
+func isLocal(path string) bool {
+ return unixIsLocal(path)
+}
+
+func localize(path string) (string, error) {
+ if bytealg.IndexByteString(path, 0) >= 0 {
+ return "", errInvalidPath
+ }
+ return path, nil
+}
+
+// IsAbs reports whether the path is absolute.
+func IsAbs(path string) bool {
+ return stringslite.HasPrefix(path, "/")
+}
+
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+func volumeNameLen(path string) int {
+ return 0
+}
diff --git a/src/internal/filepathlite/path_windows.go b/src/internal/filepathlite/path_windows.go
new file mode 100644
index 0000000000..8f34838a98
--- /dev/null
+++ b/src/internal/filepathlite/path_windows.go
@@ -0,0 +1,329 @@
+// Copyright 2010 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 filepathlite
+
+import (
+ "internal/bytealg"
+ "internal/stringslite"
+ "syscall"
+)
+
+const (
+ Separator = '\\' // OS-specific path separator
+ ListSeparator = ';' // OS-specific path list separator
+)
+
+func IsPathSeparator(c uint8) bool {
+ return c == '\\' || c == '/'
+}
+
+func isLocal(path string) bool {
+ if path == "" {
+ return false
+ }
+ if IsPathSeparator(path[0]) {
+ // Path rooted in the current drive.
+ return false
+ }
+ if stringslite.IndexByte(path, ':') >= 0 {
+ // Colons are only valid when marking a drive letter ("C:foo").
+ // Rejecting any path with a colon is conservative but safe.
+ return false
+ }
+ hasDots := false // contains . or .. path elements
+ for p := path; p != ""; {
+ var part string
+ part, p, _ = cutPath(p)
+ if part == "." || part == ".." {
+ hasDots = true
+ }
+ if isReservedName(part) {
+ return false
+ }
+ }
+ if hasDots {
+ path = Clean(path)
+ }
+ if path == ".." || stringslite.HasPrefix(path, `..\`) {
+ return false
+ }
+ return true
+}
+
+func localize(path string) (string, error) {
+ for i := 0; i < len(path); i++ {
+ switch path[i] {
+ case ':', '\\', 0:
+ return "", errInvalidPath
+ }
+ }
+ containsSlash := false
+ for p := path; p != ""; {
+ // Find the next path element.
+ var element string
+ i := bytealg.IndexByteString(p, '/')
+ if i < 0 {
+ element = p
+ p = ""
+ } else {
+ containsSlash = true
+ element = p[:i]
+ p = p[i+1:]
+ }
+ if isReservedName(element) {
+ return "", errInvalidPath
+ }
+ }
+ if containsSlash {
+ // We can't depend on strings, so substitute \ for / manually.
+ buf := []byte(path)
+ for i, b := range buf {
+ if b == '/' {
+ buf[i] = '\\'
+ }
+ }
+ path = string(buf)
+ }
+ return path, nil
+}
+
+// isReservedName reports if name is a Windows reserved device name.
+// It does not detect names with an extension, which are also reserved on some Windows versions.
+//
+// For details, search for PRN in
+// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file.
+func isReservedName(name string) bool {
+ // Device names can have arbitrary trailing characters following a dot or colon.
+ base := name
+ for i := 0; i < len(base); i++ {
+ switch base[i] {
+ case ':', '.':
+ base = base[:i]
+ }
+ }
+ // Trailing spaces in the last path element are ignored.
+ for len(base) > 0 && base[len(base)-1] == ' ' {
+ base = base[:len(base)-1]
+ }
+ if !isReservedBaseName(base) {
+ return false
+ }
+ if len(base) == len(name) {
+ return true
+ }
+ // The path element is a reserved name with an extension.
+ // Some Windows versions consider this a reserved name,
+ // while others do not. Use FullPath to see if the name is
+ // reserved.
+ if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` {
+ return true
+ }
+ return false
+}
+
+func isReservedBaseName(name string) bool {
+ if len(name) == 3 {
+ switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
+ case "CON", "PRN", "AUX", "NUL":
+ return true
+ }
+ }
+ if len(name) >= 4 {
+ switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
+ case "COM", "LPT":
+ if len(name) == 4 && '1' <= name[3] && name[3] <= '9' {
+ return true
+ }
+ // Superscript ¹, ², and ³ are considered numbers as well.
+ switch name[3:] {
+ case "\u00b2", "\u00b3", "\u00b9":
+ return true
+ }
+ return false
+ }
+ }
+
+ // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle.
+ // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles
+ //
+ // While CONIN$ and CONOUT$ aren't documented as being files,
+ // they behave the same as CON. For example, ./CONIN$ also opens the console input.
+ if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") {
+ return true
+ }
+ if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") {
+ return true
+ }
+ return false
+}
+
+func equalFold(a, b string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := 0; i < len(a); i++ {
+ if toUpper(a[i]) != toUpper(b[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func toUpper(c byte) byte {
+ if 'a' <= c && c <= 'z' {
+ return c - ('a' - 'A')
+ }
+ return c
+}
+
+// IsAbs reports whether the path is absolute.
+func IsAbs(path string) (b bool) {
+ l := volumeNameLen(path)
+ if l == 0 {
+ return false
+ }
+ // If the volume name starts with a double slash, this is an absolute path.
+ if IsPathSeparator(path[0]) && IsPathSeparator(path[1]) {
+ return true
+ }
+ path = path[l:]
+ if path == "" {
+ return false
+ }
+ return IsPathSeparator(path[0])
+}
+
+// volumeNameLen returns length of the leading volume name on Windows.
+// It returns 0 elsewhere.
+//
+// See:
+// https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
+func volumeNameLen(path string) int {
+ switch {
+ case len(path) >= 2 && path[1] == ':':
+ // Path starts with a drive letter.
+ //
+ // Not all Windows functions necessarily enforce the requirement that
+ // drive letters be in the set A-Z, and we don't try to here.
+ //
+ // We don't handle the case of a path starting with a non-ASCII character,
+ // in which case the "drive letter" might be multiple bytes long.
+ return 2
+
+ case len(path) == 0 || !IsPathSeparator(path[0]):
+ // Path does not have a volume component.
+ return 0
+
+ case pathHasPrefixFold(path, `\\.\UNC`):
+ // We're going to treat the UNC host and share as part of the volume
+ // prefix for historical reasons, but this isn't really principled;
+ // Windows's own GetFullPathName will happily remove the first
+ // component of the path in this space, converting
+ // \\.\unc\a\b\..\c into \\.\unc\a\c.
+ return uncLen(path, len(`\\.\UNC\`))
+
+ case pathHasPrefixFold(path, `\\.`) ||
+ pathHasPrefixFold(path, `\\?`) || pathHasPrefixFold(path, `\??`):
+ // Path starts with \\.\, and is a Local Device path; or
+ // path starts with \\?\ or \??\ and is a Root Local Device path.
+ //
+ // We treat the next component after the \\.\ prefix as
+ // part of the volume name, which means Clean(`\\?\c:\`)
+ // won't remove the trailing \. (See #64028.)
+ if len(path) == 3 {
+ return 3 // exactly \\.
+ }
+ _, rest, ok := cutPath(path[4:])
+ if !ok {
+ return len(path)
+ }
+ return len(path) - len(rest) - 1
+
+ case len(path) >= 2 && IsPathSeparator(path[1]):
+ // Path starts with \\, and is a UNC path.
+ return uncLen(path, 2)
+ }
+ return 0
+}
+
+// pathHasPrefixFold tests whether the path s begins with prefix,
+// ignoring case and treating all path separators as equivalent.
+// If s is longer than prefix, then s[len(prefix)] must be a path separator.
+func pathHasPrefixFold(s, prefix string) bool {
+ if len(s) < len(prefix) {
+ return false
+ }
+ for i := 0; i < len(prefix); i++ {
+ if IsPathSeparator(prefix[i]) {
+ if !IsPathSeparator(s[i]) {
+ return false
+ }
+ } else if toUpper(prefix[i]) != toUpper(s[i]) {
+ return false
+ }
+ }
+ if len(s) > len(prefix) && !IsPathSeparator(s[len(prefix)]) {
+ return false
+ }
+ return true
+}
+
+// uncLen returns the length of the volume prefix of a UNC path.
+// prefixLen is the prefix prior to the start of the UNC host;
+// for example, for "//host/share", the prefixLen is len("//")==2.
+func uncLen(path string, prefixLen int) int {
+ count := 0
+ for i := prefixLen; i < len(path); i++ {
+ if IsPathSeparator(path[i]) {
+ count++
+ if count == 2 {
+ return i
+ }
+ }
+ }
+ return len(path)
+}
+
+// cutPath slices path around the first path separator.
+func cutPath(path string) (before, after string, found bool) {
+ for i := range path {
+ if IsPathSeparator(path[i]) {
+ return path[:i], path[i+1:], true
+ }
+ }
+ return path, "", false
+}
+
+// isUNC reports whether path is a UNC path.
+func isUNC(path string) bool {
+ return len(path) > 1 && IsPathSeparator(path[0]) && IsPathSeparator(path[1])
+}
+
+// postClean adjusts the results of Clean to avoid turning a relative path
+// into an absolute or rooted one.
+func postClean(out *lazybuf) {
+ if out.volLen != 0 || out.buf == nil {
+ return
+ }
+ // If a ':' appears in the path element at the start of a path,
+ // insert a .\ at the beginning to avoid converting relative paths
+ // like a/../c: into c:.
+ for _, c := range out.buf {
+ if IsPathSeparator(c) {
+ break
+ }
+ if c == ':' {
+ out.prepend('.', Separator)
+ return
+ }
+ }
+ // If a path begins with \??\, insert a \. at the beginning
+ // to avoid converting paths like \a\..\??\c:\x into \??\c:\x
+ // (equivalent to c:\x).
+ if len(out.buf) >= 3 && IsPathSeparator(out.buf[0]) && out.buf[1] == '?' && out.buf[2] == '?' {
+ out.prepend(Separator, '.')
+ }
+}
diff --git a/src/internal/platform/supported.go b/src/internal/platform/supported.go
index 8f43cbf25f..a774247e6b 100644
--- a/src/internal/platform/supported.go
+++ b/src/internal/platform/supported.go
@@ -26,7 +26,7 @@ func RaceDetectorSupported(goos, goarch string) bool {
return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x"
case "darwin":
return goarch == "amd64" || goarch == "arm64"
- case "freebsd", "netbsd", "openbsd", "windows":
+ case "freebsd", "netbsd", "windows":
return goarch == "amd64"
default:
return false
diff --git a/src/internal/poll/sendfile.go b/src/internal/poll/sendfile.go
new file mode 100644
index 0000000000..41b0481c1a
--- /dev/null
+++ b/src/internal/poll/sendfile.go
@@ -0,0 +1,7 @@
+// Copyright 2024 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 poll
+
+var TestHookDidSendFile = func(dstFD *FD, src int, written int64, err error, handled bool) {}
diff --git a/src/internal/poll/sendfile_bsd.go b/src/internal/poll/sendfile_bsd.go
index 8fcdb1c22e..669df94cc1 100644
--- a/src/internal/poll/sendfile_bsd.go
+++ b/src/internal/poll/sendfile_bsd.go
@@ -14,6 +14,9 @@ const maxSendfileSize int = 4 << 20
// SendFile wraps the sendfile system call.
func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error, handled bool) {
+ defer func() {
+ TestHookDidSendFile(dstFD, src, written, err, handled)
+ }()
if err := dstFD.writeLock(); err != nil {
return 0, err, false
}
diff --git a/src/internal/poll/sendfile_linux.go b/src/internal/poll/sendfile_linux.go
index c2a0653294..d1c4d5c0d3 100644
--- a/src/internal/poll/sendfile_linux.go
+++ b/src/internal/poll/sendfile_linux.go
@@ -12,6 +12,9 @@ const maxSendfileSize int = 4 << 20
// SendFile wraps the sendfile system call.
func SendFile(dstFD *FD, src int, remain int64) (written int64, err error, handled bool) {
+ defer func() {
+ TestHookDidSendFile(dstFD, src, written, err, handled)
+ }()
if err := dstFD.writeLock(); err != nil {
return 0, err, false
}
diff --git a/src/internal/poll/sendfile_solaris.go b/src/internal/poll/sendfile_solaris.go
index 1ba0c8d064..ec675833a2 100644
--- a/src/internal/poll/sendfile_solaris.go
+++ b/src/internal/poll/sendfile_solaris.go
@@ -17,6 +17,9 @@ const maxSendfileSize int = 4 << 20
// SendFile wraps the sendfile system call.
func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error, handled bool) {
+ defer func() {
+ TestHookDidSendFile(dstFD, src, written, err, handled)
+ }()
if err := dstFD.writeLock(); err != nil {
return 0, err, false
}
diff --git a/src/internal/poll/sendfile_windows.go b/src/internal/poll/sendfile_windows.go
index 8c3353bc6f..2ae8a8d1d7 100644
--- a/src/internal/poll/sendfile_windows.go
+++ b/src/internal/poll/sendfile_windows.go
@@ -11,6 +11,9 @@ import (
// SendFile wraps the TransmitFile call.
func SendFile(fd *FD, src syscall.Handle, n int64) (written int64, err error) {
+ defer func() {
+ TestHookDidSendFile(fd, 0, written, err, written > 0)
+ }()
if fd.kind == kindPipe {
// TransmitFile does not work with pipes
return 0, syscall.ESPIPE
diff --git a/src/internal/runtime/atomic/atomic_andor_test.go b/src/internal/runtime/atomic/atomic_andor_test.go
index 631a6e637d..5b594d8edf 100644
--- a/src/internal/runtime/atomic/atomic_andor_test.go
+++ b/src/internal/runtime/atomic/atomic_andor_test.go
@@ -54,6 +54,7 @@ func TestAnd32(t *testing.T) {
func TestAnd64(t *testing.T) {
// Basic sanity check.
x := uint64(0xffffffffffffffff)
+ sink = &x
for i := uint64(0); i < 64; i++ {
old := x
v := atomic.And64(&x, ^(1 << i))
@@ -131,6 +132,7 @@ func TestOr32(t *testing.T) {
func TestOr64(t *testing.T) {
// Basic sanity check.
x := uint64(0)
+ sink = &x
for i := uint64(0); i < 64; i++ {
old := x
v := atomic.Or64(&x, 1<<i)
diff --git a/src/internal/safefilepath/path.go b/src/internal/safefilepath/path.go
deleted file mode 100644
index c2cc6ce5d4..0000000000
--- a/src/internal/safefilepath/path.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2022 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 safefilepath manipulates operating-system file paths.
-package safefilepath
-
-import (
- "errors"
- "io/fs"
-)
-
-var errInvalidPath = errors.New("invalid path")
-
-// Localize is filepath.Localize.
-//
-// It is implemented in this package to avoid a dependency cycle
-// between os and file/filepath.
-//
-// Tests for this function are in path/filepath.
-func Localize(path string) (string, error) {
- if !fs.ValidPath(path) {
- return "", errInvalidPath
- }
- return localize(path)
-}
diff --git a/src/internal/safefilepath/path_plan9.go b/src/internal/safefilepath/path_plan9.go
deleted file mode 100644
index 55627c5102..0000000000
--- a/src/internal/safefilepath/path_plan9.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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 safefilepath
-
-import "internal/bytealg"
-
-func localize(path string) (string, error) {
- if path[0] == '#' || bytealg.IndexByteString(path, 0) >= 0 {
- return "", errInvalidPath
- }
- return path, nil
-}
diff --git a/src/internal/safefilepath/path_unix.go b/src/internal/safefilepath/path_unix.go
deleted file mode 100644
index 873d0935ec..0000000000
--- a/src/internal/safefilepath/path_unix.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 unix || (js && wasm) || wasip1
-
-package safefilepath
-
-import "internal/bytealg"
-
-func localize(path string) (string, error) {
- if bytealg.IndexByteString(path, 0) >= 0 {
- return "", errInvalidPath
- }
- return path, nil
-}
diff --git a/src/internal/safefilepath/path_windows.go b/src/internal/safefilepath/path_windows.go
deleted file mode 100644
index b626196f11..0000000000
--- a/src/internal/safefilepath/path_windows.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2022 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 safefilepath
-
-import (
- "internal/bytealg"
- "syscall"
-)
-
-func localize(path string) (string, error) {
- for i := 0; i < len(path); i++ {
- switch path[i] {
- case ':', '\\', 0:
- return "", errInvalidPath
- }
- }
- containsSlash := false
- for p := path; p != ""; {
- // Find the next path element.
- var element string
- i := bytealg.IndexByteString(p, '/')
- if i < 0 {
- element = p
- p = ""
- } else {
- containsSlash = true
- element = p[:i]
- p = p[i+1:]
- }
- if IsReservedName(element) {
- return "", errInvalidPath
- }
- }
- if containsSlash {
- // We can't depend on strings, so substitute \ for / manually.
- buf := []byte(path)
- for i, b := range buf {
- if b == '/' {
- buf[i] = '\\'
- }
- }
- path = string(buf)
- }
- return path, nil
-}
-
-// IsReservedName reports if name is a Windows reserved device name.
-// It does not detect names with an extension, which are also reserved on some Windows versions.
-//
-// For details, search for PRN in
-// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file.
-func IsReservedName(name string) bool {
- // Device names can have arbitrary trailing characters following a dot or colon.
- base := name
- for i := 0; i < len(base); i++ {
- switch base[i] {
- case ':', '.':
- base = base[:i]
- }
- }
- // Trailing spaces in the last path element are ignored.
- for len(base) > 0 && base[len(base)-1] == ' ' {
- base = base[:len(base)-1]
- }
- if !isReservedBaseName(base) {
- return false
- }
- if len(base) == len(name) {
- return true
- }
- // The path element is a reserved name with an extension.
- // Some Windows versions consider this a reserved name,
- // while others do not. Use FullPath to see if the name is
- // reserved.
- if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` {
- return true
- }
- return false
-}
-
-func isReservedBaseName(name string) bool {
- if len(name) == 3 {
- switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
- case "CON", "PRN", "AUX", "NUL":
- return true
- }
- }
- if len(name) >= 4 {
- switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) {
- case "COM", "LPT":
- if len(name) == 4 && '1' <= name[3] && name[3] <= '9' {
- return true
- }
- // Superscript ¹, ², and ³ are considered numbers as well.
- switch name[3:] {
- case "\u00b2", "\u00b3", "\u00b9":
- return true
- }
- return false
- }
- }
-
- // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle.
- // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles
- //
- // While CONIN$ and CONOUT$ aren't documented as being files,
- // they behave the same as CON. For example, ./CONIN$ also opens the console input.
- if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") {
- return true
- }
- if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") {
- return true
- }
- return false
-}
-
-func equalFold(a, b string) bool {
- if len(a) != len(b) {
- return false
- }
- for i := 0; i < len(a); i++ {
- if toUpper(a[i]) != toUpper(b[i]) {
- return false
- }
- }
- return true
-}
-
-func toUpper(c byte) byte {
- if 'a' <= c && c <= 'z' {
- return c - ('a' - 'A')
- }
- return c
-}
diff --git a/src/internal/stringslite/strings.go b/src/internal/stringslite/strings.go
new file mode 100644
index 0000000000..4114b86130
--- /dev/null
+++ b/src/internal/stringslite/strings.go
@@ -0,0 +1,150 @@
+// Copyright 2024 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 stringslite implements a subset of strings,
+// only using packages that may be imported by "os".
+//
+// Tests for these functions are in the strings package.
+package stringslite
+
+import (
+ "internal/bytealg"
+ "unsafe"
+)
+
+func HasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
+}
+
+func HasSuffix(s, suffix string) bool {
+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
+}
+
+func IndexByte(s string, c byte) int {
+ return bytealg.IndexByteString(s, c)
+}
+
+func Index(s, substr string) int {
+ n := len(substr)
+ switch {
+ case n == 0:
+ return 0
+ case n == 1:
+ return IndexByte(s, substr[0])
+ case n == len(s):
+ if substr == s {
+ return 0
+ }
+ return -1
+ case n > len(s):
+ return -1
+ case n <= bytealg.MaxLen:
+ // Use brute force when s and substr both are small
+ if len(s) <= bytealg.MaxBruteForce {
+ return bytealg.IndexString(s, substr)
+ }
+ c0 := substr[0]
+ c1 := substr[1]
+ i := 0
+ t := len(s) - n + 1
+ fails := 0
+ for i < t {
+ if s[i] != c0 {
+ // IndexByte is faster than bytealg.IndexString, so use it as long as
+ // we're not getting lots of false positives.
+ o := IndexByte(s[i+1:t], c0)
+ if o < 0 {
+ return -1
+ }
+ i += o + 1
+ }
+ if s[i+1] == c1 && s[i:i+n] == substr {
+ return i
+ }
+ fails++
+ i++
+ // Switch to bytealg.IndexString when IndexByte produces too many false positives.
+ if fails > bytealg.Cutover(i) {
+ r := bytealg.IndexString(s[i:], substr)
+ if r >= 0 {
+ return r + i
+ }
+ return -1
+ }
+ }
+ return -1
+ }
+ c0 := substr[0]
+ c1 := substr[1]
+ i := 0
+ t := len(s) - n + 1
+ fails := 0
+ for i < t {
+ if s[i] != c0 {
+ o := IndexByte(s[i+1:t], c0)
+ if o < 0 {
+ return -1
+ }
+ i += o + 1
+ }
+ if s[i+1] == c1 && s[i:i+n] == substr {
+ return i
+ }
+ i++
+ fails++
+ if fails >= 4+i>>4 && i < t {
+ // See comment in ../bytes/bytes.go.
+ j := bytealg.IndexRabinKarp(s[i:], substr)
+ if j < 0 {
+ return -1
+ }
+ return i + j
+ }
+ }
+ return -1
+}
+
+func Cut(s, sep string) (before, after string, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
+
+func CutPrefix(s, prefix string) (after string, found bool) {
+ if !HasPrefix(s, prefix) {
+ return s, false
+ }
+ return s[len(prefix):], true
+}
+
+func CutSuffix(s, suffix string) (before string, found bool) {
+ if !HasSuffix(s, suffix) {
+ return s, false
+ }
+ return s[:len(s)-len(suffix)], true
+}
+
+func TrimPrefix(s, prefix string) string {
+ if HasPrefix(s, prefix) {
+ return s[len(prefix):]
+ }
+ return s
+}
+
+func TrimSuffix(s, suffix string) string {
+ if HasSuffix(s, suffix) {
+ return s[:len(s)-len(suffix)]
+ }
+ return s
+}
+
+func Clone(s string) string {
+ if len(s) == 0 {
+ return ""
+ }
+ b := make([]byte, len(s))
+ copy(b, s)
+ return unsafe.String(&b[0], len(b))
+}
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
index fb15e19c0e..cc26a50bb0 100644
--- a/src/internal/syscall/windows/syscall_windows.go
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -488,3 +488,14 @@ func FinalPath(h syscall.Handle, flags uint32) (string, error) {
}
return syscall.UTF16ToString(buf), nil
}
+
+// QueryPerformanceCounter retrieves the current value of performance counter.
+//
+//go:linkname QueryPerformanceCounter
+func QueryPerformanceCounter() int64 // Implemented in runtime package.
+
+// QueryPerformanceFrequency retrieves the frequency of the performance counter.
+// The returned value is represented as counts per second.
+//
+//go:linkname QueryPerformanceFrequency
+func QueryPerformanceFrequency() int64 // Implemented in runtime package.
diff --git a/src/internal/sysinfo/sysinfo.go b/src/internal/sysinfo/sysinfo.go
index 6a29ad2bc1..ae0d5a440c 100644
--- a/src/internal/sysinfo/sysinfo.go
+++ b/src/internal/sysinfo/sysinfo.go
@@ -11,25 +11,14 @@ import (
"sync"
)
-var cpuInfo struct {
- once sync.Once
- name string
-}
+var CPUName = sync.OnceValue(func() string {
+ if name := cpu.Name(); name != "" {
+ return name
+ }
-func CPUName() string {
- cpuInfo.once.Do(func() {
- // Try to get the information from internal/cpu.
- if name := cpu.Name(); name != "" {
- cpuInfo.name = name
- return
- }
+ if name := osCpuInfoName(); name != "" {
+ return name
+ }
- // TODO(martisch): use /proc/cpuinfo and /sys/devices/system/cpu/ on Linux as fallback.
- if name := osCpuInfoName(); name != "" {
- cpuInfo.name = name
- return
- }
- })
-
- return cpuInfo.name
-}
+ return ""
+})
diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go
index 3b9d2fd1e9..9fb92406e8 100644
--- a/src/internal/testenv/testenv.go
+++ b/src/internal/testenv/testenv.go
@@ -16,6 +16,7 @@ import (
"flag"
"fmt"
"internal/cfg"
+ "internal/goarch"
"internal/platform"
"os"
"os/exec"
@@ -511,3 +512,13 @@ func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string
func SyscallIsNotSupported(err error) bool {
return syscallIsNotSupported(err)
}
+
+// ParallelOn64Bit calls t.Parallel() unless there is a case that cannot be parallel.
+// This function should be used when it is necessary to avoid t.Parallel on
+// 32-bit machines, typically because the test uses lots of memory.
+func ParallelOn64Bit(t *testing.T) {
+ if goarch.PtrSize == 4 {
+ return
+ }
+ t.Parallel()
+}
diff --git a/src/internal/weak/pointer.go b/src/internal/weak/pointer.go
index 44d26738bc..8e05af2d23 100644
--- a/src/internal/weak/pointer.go
+++ b/src/internal/weak/pointer.go
@@ -68,10 +68,10 @@ func Make[T any](ptr *T) Pointer[T] {
// Strong creates a strong pointer from the weak pointer.
// Returns nil if the original value for the weak pointer was reclaimed by
// the garbage collector.
-// If a weak pointer points to an object with a finalizer, thhen Strong will
+// If a weak pointer points to an object with a finalizer, then Strong will
// return nil as soon as the object's finalizer is queued for execution.
func (p Pointer[T]) Strong() *T {
- return (*T)(runtime_makeStrongFromWeak(unsafe.Pointer(p.u)))
+ return (*T)(runtime_makeStrongFromWeak(p.u))
}
// Implemented in runtime.
diff --git a/src/make.bash b/src/make.bash
index 933573dd9d..5b49fcccf7 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -159,14 +159,15 @@ if [[ -z "$GOROOT_BOOTSTRAP" ]]; then
fi
export GOROOT_BOOTSTRAP
-nogoenv() {
- GO111MODULE=off GOENV=off GOOS= GOARCH= GOEXPERIMENT= GOFLAGS= "$@"
+bootstrapenv() {
+ GOROOT="$GOROOT_BOOTSTRAP" GO111MODULE=off GOENV=off GOOS= GOARCH= GOEXPERIMENT= GOFLAGS= "$@"
}
export GOROOT="$(cd .. && pwd)"
IFS=$'\n'; for go_exe in $(type -ap go); do
if [[ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]]; then
- goroot=$(GOROOT= nogoenv "$go_exe" env GOROOT)
+ GOROOT_BOOTSTRAP=""
+ goroot=$(bootstrapenv "$go_exe" env GOROOT)
if [[ "$goroot" != "$GOROOT" ]]; then
if [[ "$goroot_bootstrap_set" == "true" ]]; then
printf 'WARNING: %s does not exist, found %s from env\n' "$GOROOT_BOOTSTRAP/bin/go" "$go_exe" >&2
@@ -184,7 +185,7 @@ fi
# Get the exact bootstrap toolchain version to help with debugging.
# We clear GOOS and GOARCH to avoid an ominous but harmless warning if
# the bootstrap doesn't support them.
-GOROOT_BOOTSTRAP_VERSION=$(nogoenv "$GOROOT_BOOTSTRAP/bin/go" version | sed 's/go version //')
+GOROOT_BOOTSTRAP_VERSION=$(bootstrapenv "$GOROOT_BOOTSTRAP/bin/go" version | sed 's/go version //')
echo "Building Go cmd/dist using $GOROOT_BOOTSTRAP. ($GOROOT_BOOTSTRAP_VERSION)"
if $verbose; then
echo cmd/dist
@@ -195,7 +196,7 @@ if [[ "$GOROOT_BOOTSTRAP" == "$GOROOT" ]]; then
exit 1
fi
rm -f cmd/dist/dist
-GOROOT="$GOROOT_BOOTSTRAP" nogoenv "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
+bootstrapenv "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
# -e doesn't propagate out of eval, so check success by hand.
eval $(./cmd/dist/dist env -p || echo FAIL=true)
diff --git a/src/make.rc b/src/make.rc
index 607e9360dc..27456f759d 100755
--- a/src/make.rc
+++ b/src/make.rc
@@ -44,8 +44,8 @@ if(~ $1 -v) {
shift
}
-fn nogoenv {
- GO111MODULE=off GOENV=off GOOS=() GOARCH=() GOEXPERIMENT=() GOFLAGS=() $*
+fn bootstrapenv {
+ GOROOT=$GOROOT_BOOTSTRAP GO111MODULE=off GOENV=off GOOS=() GOARCH=() GOEXPERIMENT=() GOFLAGS=() $*
}
bootgo = 1.20.6
@@ -60,8 +60,9 @@ if(! ~ $#GOROOT_BOOTSTRAP 1){
}
for(p in $path){
if(! test -x $GOROOT_BOOTSTRAP/bin/go){
+ GOROOT_BOOTSTRAP = ()
if(go_exe = `{path=$p whatis go}){
- goroot = `{GOROOT=() nogoenv $go_exe env GOROOT}
+ goroot = `{bootstrapenv $go_exe env GOROOT}
if(! ~ $goroot $GOROOT){
if(~ $goroot_bootstrap_set 'true'){
echo 'WARNING: '$GOROOT_BOOTSTRAP'/bin/go does not exist, found '$go_exe' from env' >[1=2]
@@ -86,11 +87,11 @@ if(~ $GOROOT_BOOTSTRAP $GOROOT){
# Get the exact bootstrap toolchain version to help with debugging.
# We clear GOOS and GOARCH to avoid an ominous but harmless warning if
# the bootstrap doesn't support them.
-GOROOT_BOOTSTRAP_VERSION=`{nogoenv $GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //'}
+GOROOT_BOOTSTRAP_VERSION=`{bootstrapenv $GOROOT_BOOTSTRAP/bin/go version | sed 's/go version //'}
echo 'Building Go cmd/dist using '$GOROOT_BOOTSTRAP'. ('$"GOROOT_BOOTSTRAP_VERSION')'
if(~ $#vflag 1)
echo cmd/dist
-GOROOT=$GOROOT_BOOTSTRAP nogoenv $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist
+bootstrapenv $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist
eval `{./cmd/dist/dist env -9}
if(~ $#vflag 1)
diff --git a/src/mime/type.go b/src/mime/type.go
index 465ecf0d59..6d4266929c 100644
--- a/src/mime/type.go
+++ b/src/mime/type.go
@@ -22,18 +22,11 @@ var (
extensions sync.Map // map[string][]string; slice values are append-only.
)
-func clearSyncMap(m *sync.Map) {
- m.Range(func(k, _ any) bool {
- m.Delete(k)
- return true
- })
-}
-
// setMimeTypes is used by initMime's non-test path, and by tests.
func setMimeTypes(lowerExt, mixExt map[string]string) {
- clearSyncMap(&mimeTypes)
- clearSyncMap(&mimeTypesLower)
- clearSyncMap(&extensions)
+ mimeTypes.Clear()
+ mimeTypesLower.Clear()
+ extensions.Clear()
for k, v := range lowerExt {
mimeTypesLower.Store(k, v)
diff --git a/src/net/dnsconfig_unix.go b/src/net/dnsconfig_unix.go
index b0a318279b..0fcf2c6cc3 100644
--- a/src/net/dnsconfig_unix.go
+++ b/src/net/dnsconfig_unix.go
@@ -10,6 +10,7 @@ package net
import (
"internal/bytealg"
+ "internal/stringslite"
"net/netip"
"time"
)
@@ -75,7 +76,7 @@ func dnsReadConfig(filename string) *dnsConfig {
case "options": // magic options
for _, s := range f[1:] {
switch {
- case hasPrefix(s, "ndots:"):
+ case stringslite.HasPrefix(s, "ndots:"):
n, _, _ := dtoi(s[6:])
if n < 0 {
n = 0
@@ -83,13 +84,13 @@ func dnsReadConfig(filename string) *dnsConfig {
n = 15
}
conf.ndots = n
- case hasPrefix(s, "timeout:"):
+ case stringslite.HasPrefix(s, "timeout:"):
n, _, _ := dtoi(s[8:])
if n < 1 {
n = 1
}
conf.timeout = time.Duration(n) * time.Second
- case hasPrefix(s, "attempts:"):
+ case stringslite.HasPrefix(s, "attempts:"):
n, _, _ := dtoi(s[9:])
if n < 1 {
n = 1
@@ -155,10 +156,6 @@ func dnsDefaultSearch() []string {
return nil
}
-func hasPrefix(s, prefix string) bool {
- return len(s) >= len(prefix) && s[:len(prefix)] == prefix
-}
-
func ensureRooted(s string) string {
if len(s) > 0 && s[len(s)-1] == '.' {
return s
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index 8c8116123c..a7deba46e3 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -1559,6 +1559,14 @@ func TestPathValue(t *testing.T) {
"other": "there/is//more",
},
},
+ {
+ "/names/{name}/{other...}",
+ "/names/n/*",
+ map[string]string{
+ "name": "n",
+ "other": "*",
+ },
+ },
} {
mux := NewServeMux()
mux.HandleFunc(test.pattern, func(w ResponseWriter, r *Request) {
diff --git a/src/net/http/routing_tree.go b/src/net/http/routing_tree.go
index 8812ed04e2..fdc58ab692 100644
--- a/src/net/http/routing_tree.go
+++ b/src/net/http/routing_tree.go
@@ -34,8 +34,8 @@ type routingNode struct {
// special children keys:
// "/" trailing slash (resulting from {$})
// "" single wildcard
- // "*" multi wildcard
children mapping[string, *routingNode]
+ multiChild *routingNode // child with multi wildcard
emptyChild *routingNode // optimization: child with key ""
}
@@ -63,7 +63,9 @@ func (n *routingNode) addSegments(segs []segment, p *pattern, h Handler) {
if len(segs) != 1 {
panic("multi wildcard not last")
}
- n.addChild("*").set(p, h)
+ c := &routingNode{}
+ n.multiChild = c
+ c.set(p, h)
} else if seg.wild {
n.addChild("").addSegments(segs[1:], p, h)
} else {
@@ -185,7 +187,7 @@ func (n *routingNode) matchPath(path string, matches []string) (*routingNode, []
}
// Lastly, match the pattern (there can be at most one) that has a multi
// wildcard in this position to the rest of the path.
- if c := n.findChild("*"); c != nil {
+ if c := n.multiChild; c != nil {
// Don't record a match for a nameless wildcard (which arises from a
// trailing slash in the pattern).
if c.pattern.lastSegment().s != "" {
diff --git a/src/net/http/routing_tree_test.go b/src/net/http/routing_tree_test.go
index 3c27308a63..7de6b19507 100644
--- a/src/net/http/routing_tree_test.go
+++ b/src/net/http/routing_tree_test.go
@@ -72,10 +72,10 @@ func TestRoutingAddPattern(t *testing.T) {
"/a/b"
"":
"/a/b/{y}"
- "*":
- "/a/b/{x...}"
"/":
"/a/b/{$}"
+ MULTI:
+ "/a/b/{x...}"
"g":
"":
"j":
@@ -172,6 +172,8 @@ func TestRoutingNodeMatch(t *testing.T) {
"HEAD /headwins", nil},
{"GET", "", "/path/to/file",
"/path/{p...}", []string{"to/file"}},
+ {"GET", "", "/path/*",
+ "/path/{p...}", []string{"*"}},
})
// A pattern ending in {$} should only match URLS with a trailing slash.
@@ -291,4 +293,9 @@ func (n *routingNode) print(w io.Writer, level int) {
n, _ := n.children.find(k)
n.print(w, level+1)
}
+
+ if n.multiChild != nil {
+ fmt.Fprintf(w, "%sMULTI:\n", indent)
+ n.multiChild.print(w, level+1)
+ }
}
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 32b4130c22..cd0303b5b9 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -224,7 +224,7 @@ type CloseNotifier interface {
// that the channel receives a value.
//
// If the protocol is HTTP/1.1 and CloseNotify is called while
- // processing an idempotent request (such a GET) while
+ // processing an idempotent request (such as GET) while
// HTTP/1.1 pipelining is in use, the arrival of a subsequent
// pipelined request may cause a value to be sent on the
// returned channel. In practice HTTP/1.1 pipelining is not
@@ -1102,9 +1102,9 @@ func (w *response) Header() Header {
// maxPostHandlerReadBytes is the max number of Request.Body bytes not
// consumed by a handler that the server will read from the client
-// in order to keep a connection alive. If there are more bytes than
-// this then the server to be paranoid instead sends a "Connection:
-// close" response.
+// in order to keep a connection alive. If there are more bytes
+// than this, the server, to be paranoid, instead sends a
+// "Connection close" response.
//
// This number is approximately what a typical machine's TCP buffer
// size is anyway. (if we have the bytes on the machine, we might as
diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go
index 2532a0e967..588174b1fc 100644
--- a/src/net/lookup_plan9.go
+++ b/src/net/lookup_plan9.go
@@ -109,17 +109,11 @@ func queryDNS(ctx context.Context, addr string, typ string) (res []string, err e
func handlePlan9DNSError(err error, name string) error {
if stringsHasSuffix(err.Error(), "dns: name does not exist") ||
stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode 0") ||
- stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode") {
- return &DNSError{
- Err: errNoSuchHost.Error(),
- Name: name,
- IsNotFound: true,
- }
- }
- return &DNSError{
- Err: err.Error(),
- Name: name,
+ stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode") ||
+ stringsHasSuffix(err.Error(), "dns failure") {
+ err = errNoSuchHost
}
+ return newDNSError(err, name, "")
}
// toLower returns a lower-case version of in. Restricting us to
@@ -169,10 +163,7 @@ func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, e
// host names in local network (e.g. from /lib/ndb/local)
lines, err := queryCS(ctx, "net", host, "1")
if err != nil {
- if stringsHasSuffix(err.Error(), "dns failure") {
- err = errNoSuchHost
- }
- return nil, newDNSError(err, host, "")
+ return nil, handlePlan9DNSError(err, host)
}
loop:
for _, line := range lines {
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index bd58498fbc..97b37f2841 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -1634,6 +1634,10 @@ func TestLookupNoSuchHost(t *testing.T) {
}
func TestDNSErrorUnwrap(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ // The Plan 9 implementation of the resolver doesn't use the Dial function yet. See https://go.dev/cl/409234
+ t.Skip("skipping on plan9")
+ }
rDeadlineExcceeded := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
return nil, context.DeadlineExceeded
}}
diff --git a/src/net/sendfile_linux.go b/src/net/sendfile_linux.go
index 9a7d005803..f8a7bec8d3 100644
--- a/src/net/sendfile_linux.go
+++ b/src/net/sendfile_linux.go
@@ -10,6 +10,8 @@ import (
"os"
)
+const supportsSendfile = true
+
// sendFile copies the contents of r to c using the sendfile
// system call to minimize copies.
//
diff --git a/src/net/sendfile_stub.go b/src/net/sendfile_stub.go
index a4fdd99ffe..7f31cc63e1 100644
--- a/src/net/sendfile_stub.go
+++ b/src/net/sendfile_stub.go
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build aix || js || netbsd || openbsd || ios || wasip1
+//go:build !(linux || (darwin && !ios) || dragonfly || freebsd || solaris || windows)
package net
import "io"
+const supportsSendfile = false
+
func sendFile(c *netFD, r io.Reader) (n int64, err error, handled bool) {
return 0, nil, false
}
diff --git a/src/net/sendfile_test.go b/src/net/sendfile_test.go
index 8fadb47c15..4f3411565b 100644
--- a/src/net/sendfile_test.go
+++ b/src/net/sendfile_test.go
@@ -11,6 +11,7 @@ import (
"encoding/hex"
"errors"
"fmt"
+ "internal/poll"
"io"
"os"
"runtime"
@@ -26,6 +27,48 @@ const (
newtonSHA256 = "d4a9ac22462b35e7821a4f2706c211093da678620a8f9997989ee7cf8d507bbd"
)
+// expectSendfile runs f, and verifies that internal/poll.SendFile successfully handles
+// a write to wantConn during f's execution.
+//
+// On platforms where supportsSendfile is false, expectSendfile runs f but does not
+// expect a call to SendFile.
+func expectSendfile(t *testing.T, wantConn Conn, f func()) {
+ t.Helper()
+ if !supportsSendfile {
+ f()
+ return
+ }
+ orig := poll.TestHookDidSendFile
+ defer func() {
+ poll.TestHookDidSendFile = orig
+ }()
+ var (
+ called bool
+ gotHandled bool
+ gotFD *poll.FD
+ )
+ poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
+ if called {
+ t.Error("internal/poll.SendFile called multiple times, want one call")
+ }
+ called = true
+ gotHandled = handled
+ gotFD = dstFD
+ }
+ f()
+ if !called {
+ t.Error("internal/poll.SendFile was not called, want it to be")
+ return
+ }
+ if !gotHandled {
+ t.Error("internal/poll.SendFile did not handle the write, want it to")
+ return
+ }
+ if &wantConn.(*TCPConn).fd.pfd != gotFD {
+ t.Error("internal.poll.SendFile called with unexpected FD")
+ }
+}
+
func TestSendfile(t *testing.T) {
ln := newLocalListener(t, "tcp")
defer ln.Close()
@@ -53,7 +96,17 @@ func TestSendfile(t *testing.T) {
// Return file data using io.Copy, which should use
// sendFile if available.
- sbytes, err := io.Copy(conn, f)
+ var sbytes int64
+ switch runtime.GOOS {
+ case "windows":
+ // Windows is not using sendfile for some reason:
+ // https://go.dev/issue/67042
+ sbytes, err = io.Copy(conn, f)
+ default:
+ expectSendfile(t, conn, func() {
+ sbytes, err = io.Copy(conn, f)
+ })
+ }
if err != nil {
errc <- err
return
@@ -121,7 +174,9 @@ func TestSendfileParts(t *testing.T) {
for i := 0; i < 3; i++ {
// Return file data using io.CopyN, which should use
// sendFile if available.
- _, err = io.CopyN(conn, f, 3)
+ expectSendfile(t, conn, func() {
+ _, err = io.CopyN(conn, f, 3)
+ })
if err != nil {
errc <- err
return
@@ -180,7 +235,9 @@ func TestSendfileSeeked(t *testing.T) {
return
}
- _, err = io.CopyN(conn, f, sendSize)
+ expectSendfile(t, conn, func() {
+ _, err = io.CopyN(conn, f, sendSize)
+ })
if err != nil {
errc <- err
return
@@ -240,6 +297,10 @@ func TestSendfilePipe(t *testing.T) {
return
}
defer conn.Close()
+ // The comment above states that this should call into sendfile,
+ // but empirically it doesn't seem to do so at this time.
+ // If it does, or does on some platforms, this CopyN should be wrapped
+ // in expectSendfile.
_, err = io.CopyN(conn, r, 1)
if err != nil {
t.Error(err)
@@ -333,6 +394,10 @@ func TestSendfileOnWriteTimeoutExceeded(t *testing.T) {
}
defer f.Close()
+ // We expect this to use sendfile, but as of the time this comment was written
+ // poll.SendFile on an FD past its timeout can return an error indicating that
+ // it didn't handle the operation, resulting in a non-sendfile retry.
+ // So don't use expectSendfile here.
_, err = io.Copy(conn, f)
if errors.Is(err, os.ErrDeadlineExceeded) {
return nil
diff --git a/src/net/sendfile_unix_alt.go b/src/net/sendfile_unix_alt.go
index 5cb65ee767..9e46c4e607 100644
--- a/src/net/sendfile_unix_alt.go
+++ b/src/net/sendfile_unix_alt.go
@@ -9,9 +9,12 @@ package net
import (
"internal/poll"
"io"
- "os"
+ "io/fs"
+ "syscall"
)
+const supportsSendfile = true
+
// sendFile copies the contents of r to c using the sendfile
// system call to minimize copies.
//
@@ -34,7 +37,13 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, true
}
}
- f, ok := r.(*os.File)
+ // r might be an *os.File or an os.fileWithoutWriteTo.
+ // Type assert to an interface rather than *os.File directly to handle the latter case.
+ f, ok := r.(interface {
+ fs.File
+ io.Seeker
+ syscall.Conn
+ })
if !ok {
return 0, nil, false
}
diff --git a/src/net/sendfile_windows.go b/src/net/sendfile_windows.go
index 59b1b0d5c1..0377a485da 100644
--- a/src/net/sendfile_windows.go
+++ b/src/net/sendfile_windows.go
@@ -11,6 +11,8 @@ import (
"syscall"
)
+const supportsSendfile = true
+
// sendFile copies the contents of r to c using the TransmitFile
// system call to minimize copies.
//
diff --git a/src/net/url/url.go b/src/net/url/url.go
index f362958edd..7cd6913ad7 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -814,6 +814,22 @@ func validOptionalPort(port string) bool {
// - if u.Fragment is empty, #fragment is omitted.
func (u *URL) String() string {
var buf strings.Builder
+
+ n := len(u.Scheme)
+ if u.Opaque != "" {
+ n += len(u.Opaque)
+ } else {
+ if !u.OmitHost && (u.Scheme != "" || u.Host != "" || u.User != nil) {
+ username := u.User.Username()
+ password, _ := u.User.Password()
+ n += len(username) + len(password) + len(u.Host)
+ }
+ n += len(u.Path)
+ }
+ n += len(u.RawQuery) + len(u.RawFragment)
+ n += len(":" + "//" + "//" + ":" + "@" + "/" + "./" + "?" + "#")
+ buf.Grow(n)
+
if u.Scheme != "" {
buf.WriteString(u.Scheme)
buf.WriteByte(':')
diff --git a/src/os/dir.go b/src/os/dir.go
index dcc18e1814..d75ac17193 100644
--- a/src/os/dir.go
+++ b/src/os/dir.go
@@ -5,7 +5,7 @@
package os
import (
- "internal/safefilepath"
+ "internal/filepathlite"
"io"
"io/fs"
"sort"
@@ -146,7 +146,7 @@ func CopyFS(dir string, fsys fs.FS) error {
return err
}
- fpath, err := safefilepath.Localize(path)
+ fpath, err := filepathlite.Localize(path)
if err != nil {
return err
}
@@ -157,7 +157,7 @@ func CopyFS(dir string, fsys fs.FS) error {
// TODO(panjf2000): handle symlinks with the help of fs.ReadLinkFS
// once https://go.dev/issue/49580 is done.
- // we also need safefilepath.IsLocal from https://go.dev/cl/564295.
+ // we also need filepathlite.IsLocal from https://go.dev/cl/564295.
if !d.Type().IsRegular() {
return &PathError{Op: "CopyFS", Path: path, Err: ErrInvalid}
}
diff --git a/src/os/executable_procfs.go b/src/os/executable_procfs.go
index 94e674e364..6a2cd10be7 100644
--- a/src/os/executable_procfs.go
+++ b/src/os/executable_procfs.go
@@ -8,6 +8,7 @@ package os
import (
"errors"
+ "internal/stringslite"
"runtime"
)
@@ -25,13 +26,5 @@ func executable() (string, error) {
// When the executable has been deleted then Readlink returns a
// path appended with " (deleted)".
- return stringsTrimSuffix(path, " (deleted)"), err
-}
-
-// stringsTrimSuffix is the same as strings.TrimSuffix.
-func stringsTrimSuffix(s, suffix string) string {
- if len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix {
- return s[:len(s)-len(suffix)]
- }
- return s
+ return stringslite.TrimSuffix(path, " (deleted)"), err
}
diff --git a/src/os/export_linux_test.go b/src/os/export_linux_test.go
index 942b48a17d..78c2d144be 100644
--- a/src/os/export_linux_test.go
+++ b/src/os/export_linux_test.go
@@ -7,6 +7,5 @@ package os
var (
PollCopyFileRangeP = &pollCopyFileRange
PollSpliceFile = &pollSplice
- PollSendFile = &pollSendFile
GetPollFDAndNetwork = getPollFDAndNetwork
)
diff --git a/src/os/file.go b/src/os/file.go
index ec8ad70660..c3ee31583e 100644
--- a/src/os/file.go
+++ b/src/os/file.go
@@ -45,8 +45,8 @@ package os
import (
"errors"
+ "internal/filepathlite"
"internal/poll"
- "internal/safefilepath"
"internal/testlog"
"io"
"io/fs"
@@ -766,7 +766,7 @@ func (dir dirFS) join(name string) (string, error) {
if dir == "" {
return "", errors.New("os: DirFS with empty root")
}
- name, err := safefilepath.Localize(name)
+ name, err := filepathlite.Localize(name)
if err != nil {
return "", ErrInvalid
}
diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go
index fc9c89f09a..ef277deccc 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -7,6 +7,7 @@ package os
import (
"internal/bytealg"
"internal/poll"
+ "internal/stringslite"
"io"
"runtime"
"sync"
@@ -387,14 +388,9 @@ func Remove(name string) error {
return nil
}
-// hasPrefix from the strings package.
-func hasPrefix(s, prefix string) bool {
- return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
-}
-
func rename(oldname, newname string) error {
dirname := oldname[:bytealg.LastIndexByteString(oldname, '/')+1]
- if hasPrefix(newname, dirname) {
+ if stringslite.HasPrefix(newname, dirname) {
newname = newname[len(dirname):]
} else {
return &LinkError{"rename", oldname, newname, ErrInvalid}
diff --git a/src/os/file_windows.go b/src/os/file_windows.go
index d883eb5cb2..cf652ca1bb 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -6,6 +6,7 @@ package os
import (
"errors"
+ "internal/filepathlite"
"internal/godebug"
"internal/poll"
"internal/syscall/windows"
@@ -287,14 +288,14 @@ func Link(oldname, newname string) error {
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
// '/' does not work in link's content
- oldname = fromSlash(oldname)
+ oldname = filepathlite.FromSlash(oldname)
// need the exact location of the oldname when it's relative to determine if it's a directory
destpath := oldname
- if v := volumeName(oldname); v == "" {
+ if v := filepathlite.VolumeName(oldname); v == "" {
if len(oldname) > 0 && IsPathSeparator(oldname[0]) {
// oldname is relative to the volume containing newname.
- if v = volumeName(newname); v != "" {
+ if v = filepathlite.VolumeName(newname); v != "" {
// Prepend the volume explicitly, because it may be different from the
// volume of the current working directory.
destpath = v + oldname
@@ -313,7 +314,7 @@ func Symlink(oldname, newname string) error {
return &LinkError{"symlink", oldname, newname, err}
}
var o *uint16
- if isAbs(oldname) {
+ if filepathlite.IsAbs(oldname) {
o, err = syscall.UTF16PtrFromString(fixLongPath(oldname))
} else {
// Do not use fixLongPath on oldname for relative symlinks,
diff --git a/src/os/path.go b/src/os/path.go
index a46c20bfd2..42de603ae1 100644
--- a/src/os/path.go
+++ b/src/os/path.go
@@ -5,6 +5,7 @@
package os
import (
+ "internal/filepathlite"
"syscall"
)
@@ -43,7 +44,7 @@ func MkdirAll(path string, perm FileMode) error {
// If there is a parent directory, and it is not the volume name,
// recurse to ensure parent directory exists.
- if parent := path[:i]; len(parent) > len(volumeName(path)) {
+ if parent := path[:i]; len(parent) > len(filepathlite.VolumeName(path)) {
err = MkdirAll(parent, perm)
if err != nil {
return err
diff --git a/src/os/path_plan9.go b/src/os/path_plan9.go
index f1c9dbc048..b09b53a3d8 100644
--- a/src/os/path_plan9.go
+++ b/src/os/path_plan9.go
@@ -13,7 +13,3 @@ const (
func IsPathSeparator(c uint8) bool {
return PathSeparator == c
}
-
-func volumeName(p string) string {
- return ""
-}
diff --git a/src/os/path_unix.go b/src/os/path_unix.go
index 1c80fa91f8..062c07c91e 100644
--- a/src/os/path_unix.go
+++ b/src/os/path_unix.go
@@ -69,7 +69,3 @@ func splitPath(path string) (string, string) {
return dirname, basename
}
-
-func volumeName(p string) string {
- return ""
-}
diff --git a/src/os/path_windows.go b/src/os/path_windows.go
index e908d3ddf5..4d7bdb2fa2 100644
--- a/src/os/path_windows.go
+++ b/src/os/path_windows.go
@@ -5,6 +5,7 @@
package os
import (
+ "internal/filepathlite"
"internal/syscall/windows"
"syscall"
)
@@ -44,80 +45,8 @@ func basename(name string) string {
return name
}
-func isAbs(path string) (b bool) {
- v := volumeName(path)
- if v == "" {
- return false
- }
- path = path[len(v):]
- if path == "" {
- return false
- }
- return IsPathSeparator(path[0])
-}
-
-func volumeName(path string) (v string) {
- if len(path) < 2 {
- return ""
- }
- // with drive letter
- c := path[0]
- if path[1] == ':' &&
- ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
- 'A' <= c && c <= 'Z') {
- return path[:2]
- }
- // is it UNC
- if l := len(path); l >= 5 && IsPathSeparator(path[0]) && IsPathSeparator(path[1]) &&
- !IsPathSeparator(path[2]) && path[2] != '.' {
- // first, leading `\\` and next shouldn't be `\`. its server name.
- for n := 3; n < l-1; n++ {
- // second, next '\' shouldn't be repeated.
- if IsPathSeparator(path[n]) {
- n++
- // third, following something characters. its share name.
- if !IsPathSeparator(path[n]) {
- if path[n] == '.' {
- break
- }
- for ; n < l; n++ {
- if IsPathSeparator(path[n]) {
- break
- }
- }
- return path[:n]
- }
- break
- }
- }
- }
- return ""
-}
-
-func fromSlash(path string) string {
- // Replace each '/' with '\\' if present
- var pathbuf []byte
- var lastSlash int
- for i, b := range path {
- if b == '/' {
- if pathbuf == nil {
- pathbuf = make([]byte, len(path))
- }
- copy(pathbuf[lastSlash:], path[lastSlash:i])
- pathbuf[i] = '\\'
- lastSlash = i + 1
- }
- }
- if pathbuf == nil {
- return path
- }
-
- copy(pathbuf[lastSlash:], path[lastSlash:])
- return string(pathbuf)
-}
-
func dirname(path string) string {
- vol := volumeName(path)
+ vol := filepathlite.VolumeName(path)
i := len(path) - 1
for i >= len(vol) && !IsPathSeparator(path[i]) {
i--
@@ -174,7 +103,7 @@ func addExtendedPrefix(path string) string {
// The MSDN docs appear to say that a normal path that is 248 bytes long
// will work; empirically the path must be less then 248 bytes long.
pathLength := len(path)
- if !isAbs(path) {
+ if !filepathlite.IsAbs(path) {
// If the path is relative, we need to prepend the working directory
// plus a separator to the path before we can determine if it's too long.
// We don't want to call syscall.Getwd here, as that call is expensive to do
@@ -236,8 +165,6 @@ func addExtendedPrefix(path string) string {
if n <= uint32(len(buf)-len(prefix)) {
buf = buf[:n+uint32(len(prefix))]
break
- } else {
- continue
}
}
if isUNC {
diff --git a/src/os/types_windows.go b/src/os/types_windows.go
index 16042fea51..c83fa4c353 100644
--- a/src/os/types_windows.go
+++ b/src/os/types_windows.go
@@ -5,6 +5,7 @@
package os
import (
+ "internal/filepathlite"
"internal/godebug"
"internal/syscall/windows"
"sync"
@@ -338,7 +339,7 @@ func (fs *fileStat) loadFileId() error {
// and set name from path.
func (fs *fileStat) saveInfoFromPath(path string) error {
fs.path = path
- if !isAbs(fs.path) {
+ if !filepathlite.IsAbs(fs.path) {
var err error
fs.path, err = syscall.FullPath(fs.path)
if err != nil {
diff --git a/src/os/writeto_linux_test.go b/src/os/writeto_linux_test.go
index 5ffab88a2a..e3900631ba 100644
--- a/src/os/writeto_linux_test.go
+++ b/src/os/writeto_linux_test.go
@@ -109,8 +109,18 @@ func newSendFileTest(t *testing.T, proto string, size int64) (net.Conn, *File, n
func hookSendFile(t *testing.T) *sendFileHook {
h := new(sendFileHook)
- h.install()
- t.Cleanup(h.uninstall)
+ orig := poll.TestHookDidSendFile
+ t.Cleanup(func() {
+ poll.TestHookDidSendFile = orig
+ })
+ poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) {
+ h.called = true
+ h.dstfd = dstFD.Sysfd
+ h.srcfd = src
+ h.written = written
+ h.err = err
+ h.handled = handled
+ }
return h
}
@@ -118,29 +128,10 @@ type sendFileHook struct {
called bool
dstfd int
srcfd int
- remain int64
written int64
handled bool
err error
-
- original func(dst *poll.FD, src int, remain int64) (int64, error, bool)
-}
-
-func (h *sendFileHook) install() {
- h.original = *PollSendFile
- *PollSendFile = func(dst *poll.FD, src int, remain int64) (int64, error, bool) {
- h.called = true
- h.dstfd = dst.Sysfd
- h.srcfd = src
- h.remain = remain
- h.written, h.err, h.handled = h.original(dst, src, remain)
- return h.written, h.err, h.handled
- }
-}
-
-func (h *sendFileHook) uninstall() {
- *PollSendFile = h.original
}
func createTempFile(t *testing.T, size int64) (*File, []byte) {
diff --git a/src/os/zero_copy_linux.go b/src/os/zero_copy_linux.go
index 70a05ffa1e..0afc19e125 100644
--- a/src/os/zero_copy_linux.go
+++ b/src/os/zero_copy_linux.go
@@ -13,7 +13,6 @@ import (
var (
pollCopyFileRange = poll.CopyFileRange
pollSplice = poll.Splice
- pollSendFile = poll.SendFile
)
// wrapSyscallError takes an error and a syscall name. If the error is
@@ -38,7 +37,7 @@ func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) {
}
rerr := sc.Read(func(fd uintptr) (done bool) {
- written, err, handled = pollSendFile(pfd, int(fd), 1<<63-1)
+ written, err, handled = poll.SendFile(pfd, int(fd), 1<<63-1)
return true
})
diff --git a/src/path/filepath/match.go b/src/path/filepath/match.go
index 12f0bfa7d3..67124796db 100644
--- a/src/path/filepath/match.go
+++ b/src/path/filepath/match.go
@@ -6,6 +6,7 @@ package filepath
import (
"errors"
+ "internal/filepathlite"
"os"
"runtime"
"sort"
@@ -307,7 +308,7 @@ func cleanGlobPath(path string) string {
// cleanGlobPathWindows is windows version of cleanGlobPath.
func cleanGlobPathWindows(path string) (prefixLen int, cleaned string) {
- vollen := volumeNameLen(path)
+ vollen := filepathlite.VolumeNameLen(path)
switch {
case path == "":
return 0, "."
diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go
index 6c8a0aa8b3..b0f3cbbfe9 100644
--- a/src/path/filepath/path.go
+++ b/src/path/filepath/path.go
@@ -13,58 +13,13 @@ package filepath
import (
"errors"
- "internal/safefilepath"
+ "internal/bytealg"
+ "internal/filepathlite"
"io/fs"
"os"
- "slices"
"sort"
- "strings"
)
-// A lazybuf is a lazily constructed path buffer.
-// It supports append, reading previously appended bytes,
-// and retrieving the final string. It does not allocate a buffer
-// to hold the output until that output diverges from s.
-type lazybuf struct {
- path string
- buf []byte
- w int
- volAndPath string
- volLen int
-}
-
-func (b *lazybuf) index(i int) byte {
- if b.buf != nil {
- return b.buf[i]
- }
- return b.path[i]
-}
-
-func (b *lazybuf) append(c byte) {
- if b.buf == nil {
- if b.w < len(b.path) && b.path[b.w] == c {
- b.w++
- return
- }
- b.buf = make([]byte, len(b.path))
- copy(b.buf, b.path[:b.w])
- }
- b.buf[b.w] = c
- b.w++
-}
-
-func (b *lazybuf) prepend(prefix ...byte) {
- b.buf = slices.Insert(b.buf, 0, prefix...)
- b.w += len(prefix)
-}
-
-func (b *lazybuf) string() string {
- if b.buf == nil {
- return b.volAndPath[:b.volLen+b.w]
- }
- return b.volAndPath[:b.volLen] + string(b.buf[:b.w])
-}
-
const (
Separator = os.PathSeparator
ListSeparator = os.PathListSeparator
@@ -98,78 +53,7 @@ const (
// Getting Dot-Dot Right,”
// https://9p.io/sys/doc/lexnames.html
func Clean(path string) string {
- originalPath := path
- volLen := volumeNameLen(path)
- path = path[volLen:]
- if path == "" {
- if volLen > 1 && os.IsPathSeparator(originalPath[0]) && os.IsPathSeparator(originalPath[1]) {
- // should be UNC
- return FromSlash(originalPath)
- }
- return originalPath + "."
- }
- rooted := os.IsPathSeparator(path[0])
-
- // Invariants:
- // reading from path; r is index of next byte to process.
- // writing to buf; w is index of next byte to write.
- // dotdot is index in buf where .. must stop, either because
- // it is the leading slash or it is a leading ../../.. prefix.
- n := len(path)
- out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
- r, dotdot := 0, 0
- if rooted {
- out.append(Separator)
- r, dotdot = 1, 1
- }
-
- for r < n {
- switch {
- case os.IsPathSeparator(path[r]):
- // empty path element
- r++
- case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
- // . element
- r++
- case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
- // .. element: remove to last separator
- r += 2
- switch {
- case out.w > dotdot:
- // can backtrack
- out.w--
- for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
- out.w--
- }
- case !rooted:
- // cannot backtrack, but not rooted, so append .. element.
- if out.w > 0 {
- out.append(Separator)
- }
- out.append('.')
- out.append('.')
- dotdot = out.w
- }
- default:
- // real path element.
- // add slash if needed
- if rooted && out.w != 1 || !rooted && out.w != 0 {
- out.append(Separator)
- }
- // copy element
- for ; r < n && !os.IsPathSeparator(path[r]); r++ {
- out.append(path[r])
- }
- }
- }
-
- // Turn empty string into "."
- if out.w == 0 {
- out.append('.')
- }
-
- postClean(&out) // avoid creating absolute paths on Windows
- return FromSlash(out.string())
+ return filepathlite.Clean(path)
}
// IsLocal reports whether path, using lexical analysis only, has all of these properties:
@@ -187,29 +71,7 @@ func Clean(path string) string {
// In particular, it does not account for the effect of any symbolic links
// that may exist in the filesystem.
func IsLocal(path string) bool {
- return isLocal(path)
-}
-
-func unixIsLocal(path string) bool {
- if IsAbs(path) || path == "" {
- return false
- }
- hasDots := false
- for p := path; p != ""; {
- var part string
- part, p, _ = strings.Cut(p, "/")
- if part == "." || part == ".." {
- hasDots = true
- break
- }
- }
- if hasDots {
- path = Clean(path)
- }
- if path == ".." || strings.HasPrefix(path, "../") {
- return false
- }
- return true
+ return filepathlite.IsLocal(path)
}
// Localize converts a slash-separated path into an operating system path.
@@ -221,17 +83,14 @@ func unixIsLocal(path string) bool {
//
// The path returned by Localize will always be local, as reported by IsLocal.
func Localize(path string) (string, error) {
- return safefilepath.Localize(path)
+ return filepathlite.Localize(path)
}
// ToSlash returns the result of replacing each separator character
// in path with a slash ('/') character. Multiple separators are
// replaced by multiple slashes.
func ToSlash(path string) string {
- if Separator == '/' {
- return path
- }
- return strings.ReplaceAll(path, string(Separator), "/")
+ return filepathlite.ToSlash(path)
}
// FromSlash returns the result of replacing each slash ('/') character
@@ -241,10 +100,7 @@ func ToSlash(path string) string {
// See also the Localize function, which converts a slash-separated path
// as used by the io/fs package to an operating system path.
func FromSlash(path string) string {
- if Separator == '/' {
- return path
- }
- return strings.ReplaceAll(path, "/", string(Separator))
+ return filepathlite.FromSlash(path)
}
// SplitList splits a list of paths joined by the OS-specific [ListSeparator],
@@ -261,12 +117,7 @@ func SplitList(path string) []string {
// and file set to path.
// The returned values have the property that path = dir+file.
func Split(path string) (dir, file string) {
- vol := VolumeName(path)
- i := len(path) - 1
- for i >= len(vol) && !os.IsPathSeparator(path[i]) {
- i--
- }
- return path[:i+1], path[i+1:]
+ return filepathlite.Split(path)
}
// Join joins any number of path elements into a single path,
@@ -285,12 +136,7 @@ func Join(elem ...string) string {
// in the final element of path; it is empty if there is
// no dot.
func Ext(path string) string {
- for i := len(path) - 1; i >= 0 && !os.IsPathSeparator(path[i]); i-- {
- if path[i] == '.' {
- return path[i:]
- }
- }
- return ""
+ return filepathlite.Ext(path)
}
// EvalSymlinks returns the path name after the evaluation of any symbolic
@@ -302,6 +148,11 @@ func EvalSymlinks(path string) (string, error) {
return evalSymlinks(path)
}
+// IsAbs reports whether the path is absolute.
+func IsAbs(path string) bool {
+ return filepathlite.IsAbs(path)
+}
+
// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path. The absolute
@@ -342,7 +193,7 @@ func Rel(basepath, targpath string) (string, error) {
targ = targ[len(targVol):]
if base == "." {
base = ""
- } else if base == "" && volumeNameLen(baseVol) > 2 /* isUNC */ {
+ } else if base == "" && filepathlite.VolumeNameLen(baseVol) > 2 /* isUNC */ {
// Treat any targetpath matching `\\host\share` basepath as absolute path.
base = string(Separator)
}
@@ -381,7 +232,7 @@ func Rel(basepath, targpath string) (string, error) {
}
if b0 != bl {
// Base elements left. Must go up before going down.
- seps := strings.Count(base[b0:bl], string(Separator))
+ seps := bytealg.CountString(base[b0:bl], Separator)
size := 2 + seps*3
if tl != t0 {
size += 1 + tl - t0
@@ -602,28 +453,7 @@ func readDirNames(dirname string) ([]string, error) {
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Base returns a single separator.
func Base(path string) string {
- if path == "" {
- return "."
- }
- // Strip trailing slashes.
- for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {
- path = path[0 : len(path)-1]
- }
- // Throw away volume name
- path = path[len(VolumeName(path)):]
- // Find the last element
- i := len(path) - 1
- for i >= 0 && !os.IsPathSeparator(path[i]) {
- i--
- }
- if i >= 0 {
- path = path[i+1:]
- }
- // If empty now, it had only slashes.
- if path == "" {
- return string(Separator)
- }
- return path
+ return filepathlite.Base(path)
}
// Dir returns all but the last element of path, typically the path's directory.
@@ -633,17 +463,7 @@ func Base(path string) string {
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
func Dir(path string) string {
- vol := VolumeName(path)
- i := len(path) - 1
- for i >= len(vol) && !os.IsPathSeparator(path[i]) {
- i--
- }
- dir := Clean(path[len(vol) : i+1])
- if dir == "." && len(vol) > 2 {
- // must be UNC
- return vol
- }
- return vol + dir
+ return filepathlite.Dir(path)
}
// VolumeName returns leading volume name.
@@ -651,5 +471,5 @@ func Dir(path string) string {
// Given "\\host\share\foo" it returns "\\host\share".
// On other platforms it returns "".
func VolumeName(path string) string {
- return FromSlash(path[:volumeNameLen(path)])
+ return filepathlite.VolumeName(path)
}
diff --git a/src/path/filepath/path_plan9.go b/src/path/filepath/path_plan9.go
index 453206aee3..0e5147b90b 100644
--- a/src/path/filepath/path_plan9.go
+++ b/src/path/filepath/path_plan9.go
@@ -4,22 +4,9 @@
package filepath
-import "strings"
-
-func isLocal(path string) bool {
- return unixIsLocal(path)
-}
-
-// IsAbs reports whether the path is absolute.
-func IsAbs(path string) bool {
- return strings.HasPrefix(path, "/") || strings.HasPrefix(path, "#")
-}
-
-// volumeNameLen returns length of the leading volume name on Windows.
-// It returns 0 elsewhere.
-func volumeNameLen(path string) int {
- return 0
-}
+import (
+ "strings"
+)
// HasPrefix exists for historical compatibility and should not be used.
//
diff --git a/src/path/filepath/path_unix.go b/src/path/filepath/path_unix.go
index 57e6217434..6bc974db3f 100644
--- a/src/path/filepath/path_unix.go
+++ b/src/path/filepath/path_unix.go
@@ -6,22 +6,9 @@
package filepath
-import "strings"
-
-func isLocal(path string) bool {
- return unixIsLocal(path)
-}
-
-// IsAbs reports whether the path is absolute.
-func IsAbs(path string) bool {
- return strings.HasPrefix(path, "/")
-}
-
-// volumeNameLen returns length of the leading volume name on Windows.
-// It returns 0 elsewhere.
-func volumeNameLen(path string) int {
- return 0
-}
+import (
+ "strings"
+)
// HasPrefix exists for historical compatibility and should not be used.
//
diff --git a/src/path/filepath/path_windows.go b/src/path/filepath/path_windows.go
index 6adb7d4bc4..d53f87f1ac 100644
--- a/src/path/filepath/path_windows.go
+++ b/src/path/filepath/path_windows.go
@@ -1,179 +1,11 @@
-// Copyright 2010 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 filepath
import (
- "internal/safefilepath"
"os"
"strings"
"syscall"
)
-func isSlash(c uint8) bool {
- return c == '\\' || c == '/'
-}
-
-func toUpper(c byte) byte {
- if 'a' <= c && c <= 'z' {
- return c - ('a' - 'A')
- }
- return c
-}
-
-func isLocal(path string) bool {
- if path == "" {
- return false
- }
- if isSlash(path[0]) {
- // Path rooted in the current drive.
- return false
- }
- if strings.IndexByte(path, ':') >= 0 {
- // Colons are only valid when marking a drive letter ("C:foo").
- // Rejecting any path with a colon is conservative but safe.
- return false
- }
- hasDots := false // contains . or .. path elements
- for p := path; p != ""; {
- var part string
- part, p, _ = cutPath(p)
- if part == "." || part == ".." {
- hasDots = true
- }
- if safefilepath.IsReservedName(part) {
- return false
- }
- }
- if hasDots {
- path = Clean(path)
- }
- if path == ".." || strings.HasPrefix(path, `..\`) {
- return false
- }
- return true
-}
-
-// IsAbs reports whether the path is absolute.
-func IsAbs(path string) (b bool) {
- l := volumeNameLen(path)
- if l == 0 {
- return false
- }
- // If the volume name starts with a double slash, this is an absolute path.
- if isSlash(path[0]) && isSlash(path[1]) {
- return true
- }
- path = path[l:]
- if path == "" {
- return false
- }
- return isSlash(path[0])
-}
-
-// volumeNameLen returns length of the leading volume name on Windows.
-// It returns 0 elsewhere.
-//
-// See:
-// https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
-// https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
-func volumeNameLen(path string) int {
- switch {
- case len(path) >= 2 && path[1] == ':':
- // Path starts with a drive letter.
- //
- // Not all Windows functions necessarily enforce the requirement that
- // drive letters be in the set A-Z, and we don't try to here.
- //
- // We don't handle the case of a path starting with a non-ASCII character,
- // in which case the "drive letter" might be multiple bytes long.
- return 2
-
- case len(path) == 0 || !isSlash(path[0]):
- // Path does not have a volume component.
- return 0
-
- case pathHasPrefixFold(path, `\\.\UNC`):
- // We're going to treat the UNC host and share as part of the volume
- // prefix for historical reasons, but this isn't really principled;
- // Windows's own GetFullPathName will happily remove the first
- // component of the path in this space, converting
- // \\.\unc\a\b\..\c into \\.\unc\a\c.
- return uncLen(path, len(`\\.\UNC\`))
-
- case pathHasPrefixFold(path, `\\.`) ||
- pathHasPrefixFold(path, `\\?`) || pathHasPrefixFold(path, `\??`):
- // Path starts with \\.\, and is a Local Device path; or
- // path starts with \\?\ or \??\ and is a Root Local Device path.
- //
- // We treat the next component after the \\.\ prefix as
- // part of the volume name, which means Clean(`\\?\c:\`)
- // won't remove the trailing \. (See #64028.)
- if len(path) == 3 {
- return 3 // exactly \\.
- }
- _, rest, ok := cutPath(path[4:])
- if !ok {
- return len(path)
- }
- return len(path) - len(rest) - 1
-
- case len(path) >= 2 && isSlash(path[1]):
- // Path starts with \\, and is a UNC path.
- return uncLen(path, 2)
- }
- return 0
-}
-
-// pathHasPrefixFold tests whether the path s begins with prefix,
-// ignoring case and treating all path separators as equivalent.
-// If s is longer than prefix, then s[len(prefix)] must be a path separator.
-func pathHasPrefixFold(s, prefix string) bool {
- if len(s) < len(prefix) {
- return false
- }
- for i := 0; i < len(prefix); i++ {
- if isSlash(prefix[i]) {
- if !isSlash(s[i]) {
- return false
- }
- } else if toUpper(prefix[i]) != toUpper(s[i]) {
- return false
- }
- }
- if len(s) > len(prefix) && !isSlash(s[len(prefix)]) {
- return false
- }
- return true
-}
-
-// uncLen returns the length of the volume prefix of a UNC path.
-// prefixLen is the prefix prior to the start of the UNC host;
-// for example, for "//host/share", the prefixLen is len("//")==2.
-func uncLen(path string, prefixLen int) int {
- count := 0
- for i := prefixLen; i < len(path); i++ {
- if isSlash(path[i]) {
- count++
- if count == 2 {
- return i
- }
- }
- }
- return len(path)
-}
-
-// cutPath slices path around the first path separator.
-func cutPath(path string) (before, after string, found bool) {
- for i := range path {
- if isSlash(path[i]) {
- return path[:i], path[i+1:], true
- }
- }
- return path, "", false
-}
-
// HasPrefix exists for historical compatibility and should not be used.
//
// Deprecated: HasPrefix does not respect path boundaries and
@@ -237,7 +69,7 @@ func join(elem []string) string {
switch {
case b.Len() == 0:
// Add the first non-empty path element unchanged.
- case isSlash(lastChar):
+ case os.IsPathSeparator(lastChar):
// If the path ends in a slash, strip any leading slashes from the next
// path element to avoid creating a UNC path (any path starting with "\\")
// from non-UNC elements.
@@ -245,13 +77,13 @@ func join(elem []string) string {
// The correct behavior for Join when the first element is an incomplete UNC
// path (for example, "\\") is underspecified. We currently join subsequent
// elements so Join("\\", "host", "share") produces "\\host\share".
- for len(e) > 0 && isSlash(e[0]) {
+ for len(e) > 0 && os.IsPathSeparator(e[0]) {
e = e[1:]
}
// If the path is \ and the next path element is ??,
// add an extra .\ to create \.\?? rather than \??\
// (a Root Local Device path).
- if b.Len() == 1 && pathHasPrefixFold(e, "??") {
+ if b.Len() == 1 && strings.HasPrefix(e, "??") && (len(e) == len("??") || os.IsPathSeparator(e[2])) {
b.WriteString(`.\`)
}
case lastChar == ':':
@@ -280,29 +112,3 @@ func join(elem []string) string {
func sameWord(a, b string) bool {
return strings.EqualFold(a, b)
}
-
-// postClean adjusts the results of Clean to avoid turning a relative path
-// into an absolute or rooted one.
-func postClean(out *lazybuf) {
- if out.volLen != 0 || out.buf == nil {
- return
- }
- // If a ':' appears in the path element at the start of a path,
- // insert a .\ at the beginning to avoid converting relative paths
- // like a/../c: into c:.
- for _, c := range out.buf {
- if os.IsPathSeparator(c) {
- break
- }
- if c == ':' {
- out.prepend('.', Separator)
- return
- }
- }
- // If a path begins with \??\, insert a \. at the beginning
- // to avoid converting paths like \a\..\??\c:\x into \??\c:\x
- // (equivalent to c:\x).
- if len(out.buf) >= 3 && os.IsPathSeparator(out.buf[0]) && out.buf[1] == '?' && out.buf[2] == '?' {
- out.prepend(Separator, '.')
- }
-}
diff --git a/src/path/filepath/symlink.go b/src/path/filepath/symlink.go
index f9435e0d5b..a6047ae444 100644
--- a/src/path/filepath/symlink.go
+++ b/src/path/filepath/symlink.go
@@ -6,6 +6,7 @@ package filepath
import (
"errors"
+ "internal/filepathlite"
"io/fs"
"os"
"runtime"
@@ -13,7 +14,7 @@ import (
)
func walkSymlinks(path string) (string, error) {
- volLen := volumeNameLen(path)
+ volLen := filepathlite.VolumeNameLen(path)
pathSeparator := string(os.PathSeparator)
if volLen < len(path) && os.IsPathSeparator(path[volLen]) {
@@ -34,7 +35,7 @@ func walkSymlinks(path string) (string, error) {
// On Windows, "." can be a symlink.
// We look it up, and use the value if it is absolute.
// If not, we just return ".".
- isWindowsDot := runtime.GOOS == "windows" && path[volumeNameLen(path):] == "."
+ isWindowsDot := runtime.GOOS == "windows" && path[filepathlite.VolumeNameLen(path):] == "."
// The next path component is in path[start:end].
if end == start {
@@ -73,7 +74,7 @@ func walkSymlinks(path string) (string, error) {
// Ordinary path component. Add it to result.
- if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
+ if len(dest) > filepathlite.VolumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
dest += pathSeparator
}
@@ -113,7 +114,7 @@ func walkSymlinks(path string) (string, error) {
path = link + path[end:]
- v := volumeNameLen(link)
+ v := filepathlite.VolumeNameLen(link)
if v > 0 {
// Symlink to drive name is an absolute path.
if v < len(link) && os.IsPathSeparator(link[v]) {
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 4b936bf5bb..06f2c2b7da 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -1591,7 +1591,7 @@ func (v Value) IsZero() bool {
// v.ptr doesn't escape, as Equal functions are compiler generated
// and never escape. The escape analysis doesn't know, as it is a
// function pointer call.
- return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&zeroVal[0]))
+ return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&abi.ZeroVal[0]))
}
if typ.TFlag&abi.TFlagRegularMemory != 0 {
// For some types where the zero value is a value where all bits of this type are 0
@@ -1617,7 +1617,7 @@ func (v Value) IsZero() bool {
// If the type is comparable, then compare directly with zero.
if typ.Equal != nil && typ.Size() <= abi.ZeroValSize {
// See noescape justification above.
- return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&zeroVal[0]))
+ return typ.Equal(abi.NoEscape(v.ptr), unsafe.Pointer(&abi.ZeroVal[0]))
}
if typ.TFlag&abi.TFlagRegularMemory != 0 {
// For some types where the zero value is a value where all bits of this type are 0
@@ -2312,7 +2312,7 @@ func (v Value) Set(x Value) {
}
x = x.assignTo("reflect.Set", v.typ(), target)
if x.flag&flagIndir != 0 {
- if x.ptr == unsafe.Pointer(&zeroVal[0]) {
+ if x.ptr == unsafe.Pointer(&abi.ZeroVal[0]) {
typedmemclr(v.typ(), v.ptr)
} else {
typedmemmove(v.typ(), v.ptr, x.ptr)
@@ -3280,7 +3280,7 @@ func Zero(typ Type) Value {
if t.IfaceIndir() {
var p unsafe.Pointer
if t.Size() <= abi.ZeroValSize {
- p = unsafe.Pointer(&zeroVal[0])
+ p = unsafe.Pointer(&abi.ZeroVal[0])
} else {
p = unsafe_New(t)
}
@@ -3289,9 +3289,6 @@ func Zero(typ Type) Value {
return Value{t, nil, fl}
}
-//go:linkname zeroVal runtime.zeroVal
-var zeroVal [abi.ZeroValSize]byte
-
// New returns a Value representing a pointer to a new zero value
// for the specified type. That is, the returned Value's Type is [PointerTo](typ).
func New(typ Type) Value {
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index 1071d270c1..cb21629a84 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -456,6 +456,10 @@ goodm:
PUSHQ AX // open up space for fn's arg spill slot
MOVQ 0(DX), R12
CALL R12 // fn(g)
+ // The Windows native stack unwinder incorrectly classifies the next instruction
+ // as part of the function epilogue, producing a wrong call stack.
+ // Add a NOP to work around this issue. See go.dev/issue/67007.
+ BYTE $0x90
POPQ AX
JMP runtime·badmcall2(SB)
RET
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index bb2f03b1ce..63950c3b5f 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -198,9 +198,8 @@ It is a comma-separated list of name=val pairs setting these named variables:
tracebackancestors: setting tracebackancestors=N extends tracebacks with the stacks at
which goroutines were created, where N limits the number of ancestor goroutines to
- report. This also extends the information returned by runtime.Stack. Ancestor's goroutine
- IDs will refer to the ID of the goroutine at the time of creation; it's possible for this
- ID to be reused for another goroutine. Setting N to 0 will report no ancestry information.
+ report. This also extends the information returned by runtime.Stack.
+ Setting N to 0 will report no ancestry information.
tracefpunwindoff: setting tracefpunwindoff=1 forces the execution tracer to
use the runtime's default stack unwinder instead of frame pointer unwinding.
diff --git a/src/runtime/iface.go b/src/runtime/iface.go
index e280180665..28eb8fb5ec 100644
--- a/src/runtime/iface.go
+++ b/src/runtime/iface.go
@@ -391,7 +391,7 @@ func convT64(val uint64) (x unsafe.Pointer) {
func convTstring(val string) (x unsafe.Pointer) {
if val == "" {
- x = unsafe.Pointer(&zeroVal[0])
+ x = unsafe.Pointer(&abi.ZeroVal[0])
} else {
x = mallocgc(unsafe.Sizeof(val), stringType, true)
*(*string)(x) = val
@@ -402,7 +402,7 @@ func convTstring(val string) (x unsafe.Pointer) {
func convTslice(val []byte) (x unsafe.Pointer) {
// Note: this must work for any element type, not just byte.
if (*slice)(unsafe.Pointer(&val)).array == nil {
- x = unsafe.Pointer(&zeroVal[0])
+ x = unsafe.Pointer(&abi.ZeroVal[0])
} else {
x = mallocgc(unsafe.Sizeof(val), sliceType, true)
*(*[]byte)(x) = val
diff --git a/src/runtime/map.go b/src/runtime/map.go
index d97e209deb..9e8ae67a35 100644
--- a/src/runtime/map.go
+++ b/src/runtime/map.go
@@ -402,7 +402,7 @@ func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
if err := mapKeyError(t, key); err != nil {
panic(err) // see issue 23734
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -443,7 +443,7 @@ bucketloop:
}
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool) {
@@ -463,7 +463,7 @@ func mapaccess2(t *maptype, h *hmap, key unsafe.Pointer) (unsafe.Pointer, bool)
if err := mapKeyError(t, key); err != nil {
panic(err) // see issue 23734
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -504,7 +504,7 @@ bucketloop:
}
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
// returns both key and elem. Used by map iterator.
@@ -553,7 +553,7 @@ bucketloop:
func mapaccess1_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) unsafe.Pointer {
e := mapaccess1(t, h, key)
- if e == unsafe.Pointer(&zeroVal[0]) {
+ if e == unsafe.Pointer(&abi.ZeroVal[0]) {
return zero
}
return e
@@ -561,7 +561,7 @@ func mapaccess1_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) unsafe.Pointe
func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Pointer, bool) {
e := mapaccess1(t, h, key)
- if e == unsafe.Pointer(&zeroVal[0]) {
+ if e == unsafe.Pointer(&abi.ZeroVal[0]) {
return zero, false
}
return e, true
diff --git a/src/runtime/map_fast32.go b/src/runtime/map_fast32.go
index 7e52240e77..06dcbcabc4 100644
--- a/src/runtime/map_fast32.go
+++ b/src/runtime/map_fast32.go
@@ -16,7 +16,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_fast32))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -47,7 +47,7 @@ func mapaccess1_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
}
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
@@ -56,7 +56,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_fast32))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -87,7 +87,7 @@ func mapaccess2_fast32(t *maptype, h *hmap, key uint32) (unsafe.Pointer, bool) {
}
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
func mapassign_fast32(t *maptype, h *hmap, key uint32) unsafe.Pointer {
diff --git a/src/runtime/map_fast64.go b/src/runtime/map_fast64.go
index 2c365183cb..c8b34dd41b 100644
--- a/src/runtime/map_fast64.go
+++ b/src/runtime/map_fast64.go
@@ -16,7 +16,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_fast64))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -47,7 +47,7 @@ func mapaccess1_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
}
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
@@ -56,7 +56,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_fast64))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -87,7 +87,7 @@ func mapaccess2_fast64(t *maptype, h *hmap, key uint64) (unsafe.Pointer, bool) {
}
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer {
diff --git a/src/runtime/map_faststr.go b/src/runtime/map_faststr.go
index d989190f71..38841aee4b 100644
--- a/src/runtime/map_faststr.go
+++ b/src/runtime/map_faststr.go
@@ -16,7 +16,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess1_faststr))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -39,7 +39,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
return add(unsafe.Pointer(b), dataOffset+abi.MapBucketCount*2*goarch.PtrSize+i*uintptr(t.ValueSize))
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
// long key, try not to do more comparisons than necessary
keymaybe := uintptr(abi.MapBucketCount)
@@ -74,7 +74,7 @@ func mapaccess1_faststr(t *maptype, h *hmap, ky string) unsafe.Pointer {
return add(unsafe.Pointer(b), dataOffset+abi.MapBucketCount*2*goarch.PtrSize+keymaybe*uintptr(t.ValueSize))
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
dohash:
hash := t.Hasher(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
@@ -102,7 +102,7 @@ dohash:
}
}
}
- return unsafe.Pointer(&zeroVal[0])
+ return unsafe.Pointer(&abi.ZeroVal[0])
}
func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
@@ -111,7 +111,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapaccess2_faststr))
}
if h == nil || h.count == 0 {
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
if h.flags&hashWriting != 0 {
fatal("concurrent map read and map write")
@@ -134,7 +134,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
return add(unsafe.Pointer(b), dataOffset+abi.MapBucketCount*2*goarch.PtrSize+i*uintptr(t.ValueSize)), true
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
// long key, try not to do more comparisons than necessary
keymaybe := uintptr(abi.MapBucketCount)
@@ -169,7 +169,7 @@ func mapaccess2_faststr(t *maptype, h *hmap, ky string) (unsafe.Pointer, bool) {
return add(unsafe.Pointer(b), dataOffset+abi.MapBucketCount*2*goarch.PtrSize+keymaybe*uintptr(t.ValueSize)), true
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
dohash:
hash := t.Hasher(noescape(unsafe.Pointer(&ky)), uintptr(h.hash0))
@@ -197,7 +197,7 @@ dohash:
}
}
}
- return unsafe.Pointer(&zeroVal[0]), false
+ return unsafe.Pointer(&abi.ZeroVal[0]), false
}
func mapassign_faststr(t *maptype, h *hmap, s string) unsafe.Pointer {
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index d65e0c91f4..4aabc29644 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -43,6 +43,7 @@ const (
//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll"
//go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 "kernel32.dll"
+//go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 "kernel32.dll"
//go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll"
//go:cgo_import_dynamic runtime._RtlLookupFunctionEntry RtlLookupFunctionEntry%3 "kernel32.dll"
@@ -100,6 +101,7 @@ var (
_LoadLibraryW,
_PostQueuedCompletionStatus,
_QueryPerformanceCounter,
+ _QueryPerformanceFrequency,
_RaiseFailFastException,
_ResumeThread,
_RtlLookupFunctionEntry,
@@ -246,6 +248,20 @@ func windowsLoadSystemLib(name []uint16) uintptr {
return stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
}
+//go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter
+func windows_QueryPerformanceCounter() int64 {
+ var counter int64
+ stdcall1(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&counter)))
+ return counter
+}
+
+//go:linkname windows_QueryPerformanceFrequency internal/syscall/windows.QueryPerformanceFrequency
+func windows_QueryPerformanceFrequency() int64 {
+ var frequency int64
+ stdcall1(_QueryPerformanceFrequency, uintptr(unsafe.Pointer(&frequency)))
+ return frequency
+}
+
func loadOptionalSyscalls() {
bcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])
if bcryptPrimitives == 0 {
diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go
index 6ec0369a7e..cc6f03d2a0 100644
--- a/src/runtime/runtime.go
+++ b/src/runtime/runtime.go
@@ -5,7 +5,6 @@
package runtime
import (
- "internal/abi"
"internal/runtime/atomic"
"unsafe"
)
@@ -297,5 +296,3 @@ func setCrashFD(fd uintptr) uintptr {
var auxv []uintptr
func getAuxv() []uintptr { return auxv } // accessed from x/sys/cpu; see issue 57336
-
-var zeroVal [abi.ZeroValSize]byte
diff --git a/src/slices/slices.go b/src/slices/slices.go
index d96dd8d37c..857ab46314 100644
--- a/src/slices/slices.go
+++ b/src/slices/slices.go
@@ -358,17 +358,21 @@ func Compact[S ~[]E, E comparable](s S) S {
if len(s) < 2 {
return s
}
- i := 1
for k := 1; k < len(s); k++ {
- if s[k] != s[k-1] {
- if i != k {
- s[i] = s[k]
+ if s[k] == s[k-1] {
+ s2 := s[k:]
+ for k2 := 1; k2 < len(s2); k2++ {
+ if s2[k2] != s2[k2-1] {
+ s[k] = s2[k2]
+ k++
+ }
}
- i++
+
+ clear(s[k:]) // zero/nil out the obsolete elements, for GC
+ return s[:k]
}
}
- clear(s[i:]) // zero/nil out the obsolete elements, for GC
- return s[:i]
+ return s
}
// CompactFunc is like [Compact] but uses an equality function to compare elements.
@@ -378,17 +382,21 @@ func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
if len(s) < 2 {
return s
}
- i := 1
for k := 1; k < len(s); k++ {
- if !eq(s[k], s[k-1]) {
- if i != k {
- s[i] = s[k]
+ if eq(s[k], s[k-1]) {
+ s2 := s[k:]
+ for k2 := 1; k2 < len(s2); k2++ {
+ if !eq(s2[k2], s2[k2-1]) {
+ s[k] = s2[k2]
+ k++
+ }
}
- i++
+
+ clear(s[k:]) // zero/nil out the obsolete elements, for GC
+ return s[:k]
}
}
- clear(s[i:]) // zero/nil out the obsolete elements, for GC
- return s[:i]
+ return s
}
// Grow increases the slice's capacity, if necessary, to guarantee space for
diff --git a/src/slices/slices_test.go b/src/slices/slices_test.go
index 55de2f57d0..68c8a3adc2 100644
--- a/src/slices/slices_test.go
+++ b/src/slices/slices_test.go
@@ -763,7 +763,7 @@ var compactTests = []struct {
[]int{1, 2, 3},
},
{
- "1 item",
+ "2 items",
[]int{1, 1, 2},
[]int{1, 2},
},
@@ -802,12 +802,26 @@ func BenchmarkCompact(b *testing.B) {
}
func BenchmarkCompact_Large(b *testing.B) {
- type Large [4 * 1024]byte
-
- ss := make([]Large, 1024)
- for i := 0; i < b.N; i++ {
- _ = Compact(ss)
- }
+ type Large [16]int
+ const N = 1024
+
+ b.Run("all_dup", func(b *testing.B) {
+ ss := make([]Large, N)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = Compact(ss)
+ }
+ })
+ b.Run("no_dup", func(b *testing.B) {
+ ss := make([]Large, N)
+ for i := range ss {
+ ss[i][0] = i
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = Compact(ss)
+ }
+ })
}
func TestCompactFunc(t *testing.T) {
@@ -873,15 +887,42 @@ func TestCompactFuncClearTail(t *testing.T) {
}
}
-func BenchmarkCompactFunc_Large(b *testing.B) {
- type Large [4 * 1024]byte
-
- ss := make([]Large, 1024)
- for i := 0; i < b.N; i++ {
- _ = CompactFunc(ss, func(a, b Large) bool { return a == b })
+func BenchmarkCompactFunc(b *testing.B) {
+ for _, c := range compactTests {
+ b.Run(c.name, func(b *testing.B) {
+ ss := make([]int, 0, 64)
+ for k := 0; k < b.N; k++ {
+ ss = ss[:0]
+ ss = append(ss, c.s...)
+ _ = CompactFunc(ss, func(a, b int) bool { return a == b })
+ }
+ })
}
}
+func BenchmarkCompactFunc_Large(b *testing.B) {
+ type Element = int
+ const N = 1024 * 1024
+
+ b.Run("all_dup", func(b *testing.B) {
+ ss := make([]Element, N)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = CompactFunc(ss, func(a, b Element) bool { return a == b })
+ }
+ })
+ b.Run("no_dup", func(b *testing.B) {
+ ss := make([]Element, N)
+ for i := range ss {
+ ss[i] = i
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _ = CompactFunc(ss, func(a, b Element) bool { return a == b })
+ }
+ })
+}
+
func TestGrow(t *testing.T) {
s1 := []int{1, 2, 3}
diff --git a/src/strconv/atoc.go b/src/strconv/atoc.go
index 8cf975d3e1..560bd7920d 100644
--- a/src/strconv/atoc.go
+++ b/src/strconv/atoc.go
@@ -4,6 +4,8 @@
package strconv
+import "internal/stringslite"
+
const fnParseComplex = "ParseComplex"
// convErr splits an error returned by parseFloatPrefix
@@ -11,7 +13,7 @@ const fnParseComplex = "ParseComplex"
func convErr(err error, s string) (syntax, range_ error) {
if x, ok := err.(*NumError); ok {
x.Func = fnParseComplex
- x.Num = cloneString(s)
+ x.Num = stringslite.Clone(s)
if x.Err == ErrRange {
return nil, x
}
diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go
index 45341820cd..599ad9b895 100644
--- a/src/strconv/atoi.go
+++ b/src/strconv/atoi.go
@@ -4,7 +4,10 @@
package strconv
-import "errors"
+import (
+ "errors"
+ "internal/stringslite"
+)
// lower(c) is a lower-case letter if and only if
// c is either that lower-case letter or the equivalent upper-case letter.
@@ -33,8 +36,6 @@ func (e *NumError) Error() string {
func (e *NumError) Unwrap() error { return e.Err }
-// cloneString returns a string copy of x.
-//
// All ParseXXX functions allow the input string to escape to the error value.
// This hurts strconv.ParseXXX(string(b)) calls where b is []byte since
// the conversion from []byte must allocate a string on the heap.
@@ -42,27 +43,21 @@ func (e *NumError) Unwrap() error { return e.Err }
// back to the output by copying it first. This allows the compiler to call
// strconv.ParseXXX without a heap allocation for most []byte to string
// conversions, since it can now prove that the string cannot escape Parse.
-//
-// TODO: Use strings.Clone instead? However, we cannot depend on "strings"
-// since it incurs a transitive dependency on "unicode".
-// Either move strings.Clone to an internal/bytealg or make the
-// "strings" to "unicode" dependency lighter (see https://go.dev/issue/54098).
-func cloneString(x string) string { return string([]byte(x)) }
func syntaxError(fn, str string) *NumError {
- return &NumError{fn, cloneString(str), ErrSyntax}
+ return &NumError{fn, stringslite.Clone(str), ErrSyntax}
}
func rangeError(fn, str string) *NumError {
- return &NumError{fn, cloneString(str), ErrRange}
+ return &NumError{fn, stringslite.Clone(str), ErrRange}
}
func baseError(fn, str string, base int) *NumError {
- return &NumError{fn, cloneString(str), errors.New("invalid base " + Itoa(base))}
+ return &NumError{fn, stringslite.Clone(str), errors.New("invalid base " + Itoa(base))}
}
func bitSizeError(fn, str string, bitSize int) *NumError {
- return &NumError{fn, cloneString(str), errors.New("invalid bit size " + Itoa(bitSize))}
+ return &NumError{fn, stringslite.Clone(str), errors.New("invalid bit size " + Itoa(bitSize))}
}
const intSize = 32 << (^uint(0) >> 63)
@@ -221,7 +216,7 @@ func ParseInt(s string, base int, bitSize int) (i int64, err error) {
un, err = ParseUint(s, base, bitSize)
if err != nil && err.(*NumError).Err != ErrRange {
err.(*NumError).Func = fnParseInt
- err.(*NumError).Num = cloneString(s0)
+ err.(*NumError).Num = stringslite.Clone(s0)
return 0, err
}
diff --git a/src/strings/clone.go b/src/strings/clone.go
index d14df11d49..f965b5963a 100644
--- a/src/strings/clone.go
+++ b/src/strings/clone.go
@@ -5,7 +5,7 @@
package strings
import (
- "unsafe"
+ "internal/stringslite"
)
// Clone returns a fresh copy of s.
@@ -19,10 +19,5 @@ import (
// For strings of length zero the string "" will be returned
// and no allocation is made.
func Clone(s string) string {
- if len(s) == 0 {
- return ""
- }
- b := make([]byte, len(s))
- copy(b, s)
- return unsafe.String(&b[0], len(b))
+ return stringslite.Clone(s)
}
diff --git a/src/strings/strings.go b/src/strings/strings.go
index f53ae1f9a7..95180828f6 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -9,6 +9,7 @@ package strings
import (
"internal/bytealg"
+ "internal/stringslite"
"unicode"
"unicode/utf8"
)
@@ -115,7 +116,7 @@ func LastIndex(s, substr string) int {
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
func IndexByte(s string, c byte) int {
- return bytealg.IndexByteString(s, c)
+ return stringslite.IndexByte(s, c)
}
// IndexRune returns the index of the first instance of the Unicode code point
@@ -460,12 +461,12 @@ func Join(elems []string, sep string) string {
// HasPrefix reports whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
- return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
+ return stringslite.HasPrefix(s, prefix)
}
// HasSuffix reports whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
- return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
+ return stringslite.HasSuffix(s, suffix)
}
// Map returns a copy of the string s with all its characters modified
@@ -569,7 +570,7 @@ func Repeat(s string, count int) string {
if count < 0 {
panic("strings: negative Repeat count")
}
- if len(s) >= maxInt/count {
+ if len(s) > maxInt/count {
panic("strings: Repeat output length overflow")
}
n := len(s) * count
@@ -1074,19 +1075,13 @@ func TrimSpace(s string) string {
// TrimPrefix returns s without the provided leading prefix string.
// If s doesn't start with prefix, s is returned unchanged.
func TrimPrefix(s, prefix string) string {
- if HasPrefix(s, prefix) {
- return s[len(prefix):]
- }
- return s
+ return stringslite.TrimPrefix(s, prefix)
}
// TrimSuffix returns s without the provided trailing suffix string.
// If s doesn't end with suffix, s is returned unchanged.
func TrimSuffix(s, suffix string) string {
- if HasSuffix(s, suffix) {
- return s[:len(s)-len(suffix)]
- }
- return s
+ return stringslite.TrimSuffix(s, suffix)
}
// Replace returns a copy of the string s with the first n
@@ -1225,83 +1220,7 @@ hasUnicode:
// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
func Index(s, substr string) int {
- n := len(substr)
- switch {
- case n == 0:
- return 0
- case n == 1:
- return IndexByte(s, substr[0])
- case n == len(s):
- if substr == s {
- return 0
- }
- return -1
- case n > len(s):
- return -1
- case n <= bytealg.MaxLen:
- // Use brute force when s and substr both are small
- if len(s) <= bytealg.MaxBruteForce {
- return bytealg.IndexString(s, substr)
- }
- c0 := substr[0]
- c1 := substr[1]
- i := 0
- t := len(s) - n + 1
- fails := 0
- for i < t {
- if s[i] != c0 {
- // IndexByte is faster than bytealg.IndexString, so use it as long as
- // we're not getting lots of false positives.
- o := IndexByte(s[i+1:t], c0)
- if o < 0 {
- return -1
- }
- i += o + 1
- }
- if s[i+1] == c1 && s[i:i+n] == substr {
- return i
- }
- fails++
- i++
- // Switch to bytealg.IndexString when IndexByte produces too many false positives.
- if fails > bytealg.Cutover(i) {
- r := bytealg.IndexString(s[i:], substr)
- if r >= 0 {
- return r + i
- }
- return -1
- }
- }
- return -1
- }
- c0 := substr[0]
- c1 := substr[1]
- i := 0
- t := len(s) - n + 1
- fails := 0
- for i < t {
- if s[i] != c0 {
- o := IndexByte(s[i+1:t], c0)
- if o < 0 {
- return -1
- }
- i += o + 1
- }
- if s[i+1] == c1 && s[i:i+n] == substr {
- return i
- }
- i++
- fails++
- if fails >= 4+i>>4 && i < t {
- // See comment in ../bytes/bytes.go.
- j := bytealg.IndexRabinKarp(s[i:], substr)
- if j < 0 {
- return -1
- }
- return i + j
- }
- }
- return -1
+ return stringslite.Index(s, substr)
}
// Cut slices s around the first instance of sep,
@@ -1309,10 +1228,7 @@ func Index(s, substr string) int {
// The found result reports whether sep appears in s.
// If sep does not appear in s, cut returns s, "", false.
func Cut(s, sep string) (before, after string, found bool) {
- if i := Index(s, sep); i >= 0 {
- return s[:i], s[i+len(sep):], true
- }
- return s, "", false
+ return stringslite.Cut(s, sep)
}
// CutPrefix returns s without the provided leading prefix string
@@ -1320,10 +1236,7 @@ func Cut(s, sep string) (before, after string, found bool) {
// If s doesn't start with prefix, CutPrefix returns s, false.
// If prefix is the empty string, CutPrefix returns s, true.
func CutPrefix(s, prefix string) (after string, found bool) {
- if !HasPrefix(s, prefix) {
- return s, false
- }
- return s[len(prefix):], true
+ return stringslite.CutPrefix(s, prefix)
}
// CutSuffix returns s without the provided ending suffix string
@@ -1331,8 +1244,5 @@ func CutPrefix(s, prefix string) (after string, found bool) {
// If s doesn't end with suffix, CutSuffix returns s, false.
// If suffix is the empty string, CutSuffix returns s, true.
func CutSuffix(s, suffix string) (before string, found bool) {
- if !HasSuffix(s, suffix) {
- return s, false
- }
- return s[:len(s)-len(suffix)], true
+ return stringslite.CutSuffix(s, suffix)
}
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index ac493c7dcd..4bd3a3c202 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -1170,33 +1170,48 @@ func repeat(s string, count int) (err error) {
// See Issue golang.org/issue/16237
func TestRepeatCatchesOverflow(t *testing.T) {
- tests := [...]struct {
+ type testCase struct {
s string
count int
errStr string
- }{
+ }
+
+ runTestCases := func(prefix string, tests []testCase) {
+ for i, tt := range tests {
+ err := repeat(tt.s, tt.count)
+ if tt.errStr == "" {
+ if err != nil {
+ t.Errorf("#%d panicked %v", i, err)
+ }
+ continue
+ }
+
+ if err == nil || !Contains(err.Error(), tt.errStr) {
+ t.Errorf("%s#%d got %q want %q", prefix, i, err, tt.errStr)
+ }
+ }
+ }
+
+ const maxInt = int(^uint(0) >> 1)
+
+ runTestCases("", []testCase{
0: {"--", -2147483647, "negative"},
- 1: {"", int(^uint(0) >> 1), ""},
+ 1: {"", maxInt, ""},
2: {"-", 10, ""},
3: {"gopher", 0, ""},
4: {"-", -1, "negative"},
5: {"--", -102, "negative"},
6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
- }
-
- for i, tt := range tests {
- err := repeat(tt.s, tt.count)
- if tt.errStr == "" {
- if err != nil {
- t.Errorf("#%d panicked %v", i, err)
- }
- continue
- }
+ })
- if err == nil || !Contains(err.Error(), tt.errStr) {
- t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
- }
+ const is64Bit = 1<<(^uintptr(0)>>63)/2 != 0
+ if !is64Bit {
+ return
}
+
+ runTestCases("64-bit", []testCase{
+ 0: {"-", maxInt, "out of range"},
+ })
}
func runesEqual(a, b []rune) bool {
diff --git a/src/syscall/fs_wasip1.go b/src/syscall/fs_wasip1.go
index 4d3d7d72c6..f19e8f3b3c 100644
--- a/src/syscall/fs_wasip1.go
+++ b/src/syscall/fs_wasip1.go
@@ -7,6 +7,7 @@
package syscall
import (
+ "internal/stringslite"
"runtime"
"unsafe"
)
@@ -468,19 +469,11 @@ func joinPath(dir, file string) string {
}
func isAbs(path string) bool {
- return hasPrefix(path, "/")
+ return stringslite.HasPrefix(path, "/")
}
func isDir(path string) bool {
- return hasSuffix(path, "/")
-}
-
-func hasPrefix(s, p string) bool {
- return len(s) >= len(p) && s[:len(p)] == p
-}
-
-func hasSuffix(s, x string) bool {
- return len(s) >= len(x) && s[len(s)-len(x):] == x
+ return stringslite.HasSuffix(path, "/")
}
// preparePath returns the preopen file descriptor of the directory to perform
@@ -500,7 +493,7 @@ func preparePath(path string) (int32, unsafe.Pointer, size) {
path = joinPath(dir, path)
for _, p := range preopens {
- if len(p.name) > len(dirName) && hasPrefix(path, p.name) {
+ if len(p.name) > len(dirName) && stringslite.HasPrefix(path, p.name) {
dirFd, dirName = p.fd, p.name
}
}
diff --git a/src/syscall/syscall_openbsd_libc.go b/src/syscall/syscall_openbsd_libc.go
index ddf62f4d3f..5dea268c3e 100644
--- a/src/syscall/syscall_openbsd_libc.go
+++ b/src/syscall/syscall_openbsd_libc.go
@@ -16,26 +16,40 @@ func init() {
execveOpenBSD = execve
}
-//sys directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) = SYS_syscall
-
func syscallInternal(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
- return syscall6X(abi.FuncPCABI0(libc_syscall_trampoline), trap, a1, a2, a3, 0, 0)
+ // OpenBSD 7.5+ no longer supports indirect syscalls. A number of Go
+ // packages make use of syscall.Syscall with SYS_IOCTL since it is
+ // not well supported by golang.org/x/sys/unix. Reroute this system
+ // call number to the respective libc stub so that it continues to
+ // work for the time being. See #63900 for further details.
+ if trap == SYS_IOCTL {
+ return syscallX(abi.FuncPCABI0(libc_ioctl_trampoline), a1, a2, a3)
+ }
+ return 0, 0, ENOSYS
}
func syscall6Internal(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
- return syscall10X(abi.FuncPCABI0(libc_syscall_trampoline), trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
+ // OpenBSD 7.5+ no longer supports indirect syscalls. A number of Go
+ // packages make use of syscall.Syscall with SYS___SYSCTL since it is
+ // not well supported by golang.org/x/sys/unix. Reroute this system
+ // call number to the respective libc stub so that it continues to
+ // work for the time being. See #63900 for further details.
+ if trap == SYS___SYSCTL {
+ return syscall6X(abi.FuncPCABI0(libc_sysctl_trampoline), a1, a2, a3, a4, a5, a6)
+ }
+ return 0, 0, ENOSYS
}
func rawSyscallInternal(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
- return rawSyscall6X(abi.FuncPCABI0(libc_syscall_trampoline), trap, a1, a2, a3, 0, 0)
+ return 0, 0, ENOSYS
}
func rawSyscall6Internal(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
- return rawSyscall10X(abi.FuncPCABI0(libc_syscall_trampoline), trap, a1, a2, a3, a4, a5, a6, 0, 0, 0)
+ return 0, 0, ENOSYS
}
func syscall9Internal(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) {
- return rawSyscall10X(abi.FuncPCABI0(libc_syscall_trampoline), trap, a1, a2, a3, a4, a5, a6, a7, a8, a9)
+ return 0, 0, ENOSYS
}
// Implemented in the runtime package (runtime/sys_openbsd3.go)
diff --git a/src/syscall/zsyscall_openbsd_386.go b/src/syscall/zsyscall_openbsd_386.go
index 084b4b78e5..d2bd3ea012 100644
--- a/src/syscall/zsyscall_openbsd_386.go
+++ b/src/syscall/zsyscall_openbsd_386.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_386.s b/src/syscall/zsyscall_openbsd_386.s
index 319ad205c0..9a820e9f3e 100644
--- a/src/syscall/zsyscall_openbsd_386.s
+++ b/src/syscall/zsyscall_openbsd_386.s
@@ -213,8 +213,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
JMP libc_getfsstat(SB)
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
JMP libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- JMP libc_syscall(SB)
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
JMP libc_lseek(SB)
TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_openbsd_amd64.go b/src/syscall/zsyscall_openbsd_amd64.go
index 5a7b4c1122..170a74b457 100644
--- a/src/syscall/zsyscall_openbsd_amd64.go
+++ b/src/syscall/zsyscall_openbsd_amd64.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6X(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_amd64.s b/src/syscall/zsyscall_openbsd_amd64.s
index c0e397728a..9b70dc096e 100644
--- a/src/syscall/zsyscall_openbsd_amd64.s
+++ b/src/syscall/zsyscall_openbsd_amd64.s
@@ -213,8 +213,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
JMP libc_getfsstat(SB)
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
JMP libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- JMP libc_syscall(SB)
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
JMP libc_lseek(SB)
TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_openbsd_arm.go b/src/syscall/zsyscall_openbsd_arm.go
index 66a3227175..e75bd0b443 100644
--- a/src/syscall/zsyscall_openbsd_arm.go
+++ b/src/syscall/zsyscall_openbsd_arm.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_arm.s b/src/syscall/zsyscall_openbsd_arm.s
index 73b6a092ef..0333377b8b 100644
--- a/src/syscall/zsyscall_openbsd_arm.s
+++ b/src/syscall/zsyscall_openbsd_arm.s
@@ -213,8 +213,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
JMP libc_getfsstat(SB)
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
JMP libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- JMP libc_syscall(SB)
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
JMP libc_lseek(SB)
TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_openbsd_arm64.go b/src/syscall/zsyscall_openbsd_arm64.go
index a90f144369..bc027b4475 100644
--- a/src/syscall/zsyscall_openbsd_arm64.go
+++ b/src/syscall/zsyscall_openbsd_arm64.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6X(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_arm64.s b/src/syscall/zsyscall_openbsd_arm64.s
index 66656695d5..654e6c69a3 100644
--- a/src/syscall/zsyscall_openbsd_arm64.s
+++ b/src/syscall/zsyscall_openbsd_arm64.s
@@ -213,8 +213,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
JMP libc_getfsstat(SB)
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
JMP libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- JMP libc_syscall(SB)
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
JMP libc_lseek(SB)
TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_openbsd_ppc64.go b/src/syscall/zsyscall_openbsd_ppc64.go
index 661c8959a6..6808092a5a 100644
--- a/src/syscall/zsyscall_openbsd_ppc64.go
+++ b/src/syscall/zsyscall_openbsd_ppc64.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6X(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_ppc64.s b/src/syscall/zsyscall_openbsd_ppc64.s
index 8f3ff9a28c..86a5745c0a 100644
--- a/src/syscall/zsyscall_openbsd_ppc64.s
+++ b/src/syscall/zsyscall_openbsd_ppc64.s
@@ -319,9 +319,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
CALL libc_utimensat(SB)
RET
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- CALL libc_syscall(SB)
- RET
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
CALL libc_lseek(SB)
RET
diff --git a/src/syscall/zsyscall_openbsd_riscv64.go b/src/syscall/zsyscall_openbsd_riscv64.go
index a24fcba113..2979ff78c2 100644
--- a/src/syscall/zsyscall_openbsd_riscv64.go
+++ b/src/syscall/zsyscall_openbsd_riscv64.go
@@ -1729,21 +1729,6 @@ func libc_utimensat_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func directSyscall(trap uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr, a5 uintptr) (ret uintptr, err error) {
- r0, _, e1 := syscall6X(abi.FuncPCABI0(libc_syscall_trampoline), uintptr(trap), uintptr(a1), uintptr(a2), uintptr(a3), uintptr(a4), uintptr(a5))
- ret = uintptr(r0)
- if e1 != 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func libc_syscall_trampoline()
-
-//go:cgo_import_dynamic libc_syscall syscall "libc.so"
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
r0, _, e1 := syscall(abi.FuncPCABI0(libc_read_trampoline), uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf))
n = int(r0)
diff --git a/src/syscall/zsyscall_openbsd_riscv64.s b/src/syscall/zsyscall_openbsd_riscv64.s
index 4f787ee275..c8728190e5 100644
--- a/src/syscall/zsyscall_openbsd_riscv64.s
+++ b/src/syscall/zsyscall_openbsd_riscv64.s
@@ -213,8 +213,6 @@ TEXT ·libc_getfsstat_trampoline(SB),NOSPLIT,$0-0
JMP libc_getfsstat(SB)
TEXT ·libc_utimensat_trampoline(SB),NOSPLIT,$0-0
JMP libc_utimensat(SB)
-TEXT ·libc_syscall_trampoline(SB),NOSPLIT,$0-0
- JMP libc_syscall(SB)
TEXT ·libc_lseek_trampoline(SB),NOSPLIT,$0-0
JMP libc_lseek(SB)
TEXT ·libc_getcwd_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go
index 9491213ef1..db91c1478e 100644
--- a/src/testing/benchmark.go
+++ b/src/testing/benchmark.go
@@ -123,7 +123,7 @@ func (b *B) StartTimer() {
runtime.ReadMemStats(&memStats)
b.startAllocs = memStats.Mallocs
b.startBytes = memStats.TotalAlloc
- b.start = time.Now()
+ b.start = highPrecisionTimeNow()
b.timerOn = true
}
}
@@ -133,7 +133,7 @@ func (b *B) StartTimer() {
// want to measure.
func (b *B) StopTimer() {
if b.timerOn {
- b.duration += time.Since(b.start)
+ b.duration += highPrecisionTimeSince(b.start)
runtime.ReadMemStats(&memStats)
b.netAllocs += memStats.Mallocs - b.startAllocs
b.netBytes += memStats.TotalAlloc - b.startBytes
@@ -156,7 +156,7 @@ func (b *B) ResetTimer() {
runtime.ReadMemStats(&memStats)
b.startAllocs = memStats.Mallocs
b.startBytes = memStats.TotalAlloc
- b.start = time.Now()
+ b.start = highPrecisionTimeNow()
}
b.duration = 0
b.netAllocs = 0
@@ -325,7 +325,7 @@ func (b *B) launch() {
func (b *B) Elapsed() time.Duration {
d := b.duration
if b.timerOn {
- d += time.Since(b.start)
+ d += highPrecisionTimeSince(b.start)
}
return d
}
diff --git a/src/testing/export_test.go b/src/testing/export_test.go
index 0022491ecd..10a5b04aee 100644
--- a/src/testing/export_test.go
+++ b/src/testing/export_test.go
@@ -5,3 +5,7 @@
package testing
var PrettyPrint = prettyPrint
+
+type HighPrecisionTime = highPrecisionTime
+
+var HighPrecisionTimeNow = highPrecisionTimeNow
diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go
index baf1c7243c..d561225b3c 100644
--- a/src/testing/fuzz.go
+++ b/src/testing/fuzz.go
@@ -674,7 +674,7 @@ func fRunner(f *F, fn func(*F)) {
}
for root := &f.common; root.parent != nil; root = root.parent {
root.mu.Lock()
- root.duration += time.Since(root.start)
+ root.duration += highPrecisionTimeSince(root.start)
d := root.duration
root.mu.Unlock()
root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
@@ -687,7 +687,7 @@ func fRunner(f *F, fn func(*F)) {
}
// No panic or inappropriate Goexit.
- f.duration += time.Since(f.start)
+ f.duration += highPrecisionTimeSince(f.start)
if len(f.sub) > 0 {
// Unblock inputs that called T.Parallel while running the seed corpus.
@@ -700,9 +700,9 @@ func fRunner(f *F, fn func(*F)) {
for _, sub := range f.sub {
<-sub.signal
}
- cleanupStart := time.Now()
+ cleanupStart := highPrecisionTimeNow()
err := f.runCleanup(recoverAndReturnPanic)
- f.duration += time.Since(cleanupStart)
+ f.duration += highPrecisionTimeSince(cleanupStart)
if err != nil {
doPanic(err)
}
@@ -719,7 +719,7 @@ func fRunner(f *F, fn func(*F)) {
}
}()
- f.start = time.Now()
+ f.start = highPrecisionTimeNow()
f.resetRaces()
fn(f)
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 9c1325a609..60f0c23137 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -72,14 +72,15 @@
// A sample benchmark function looks like this:
//
// func BenchmarkRandInt(b *testing.B) {
-// for i := 0; i < b.N; i++ {
+// for range b.N {
// rand.Int()
// }
// }
//
// The benchmark function must run the target code b.N times.
-// During benchmark execution, b.N is adjusted until the benchmark function lasts
-// long enough to be timed reliably. The output
+// It is called multiple times with b.N adjusted until the
+// benchmark function lasts long enough to be timed reliably.
+// The output
//
// BenchmarkRandInt-8 68453040 17.8 ns/op
//
@@ -91,7 +92,7 @@
// func BenchmarkBigLen(b *testing.B) {
// big := NewBig()
// b.ResetTimer()
-// for i := 0; i < b.N; i++ {
+// for range b.N {
// big.Len()
// }
// }
@@ -615,10 +616,10 @@ type common struct {
isParallel bool // Whether the test is parallel.
parent *common
- level int // Nesting depth of test or benchmark.
- creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run.
- name string // Name of test or benchmark.
- start time.Time // Time test or benchmark started
+ level int // Nesting depth of test or benchmark.
+ creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run.
+ name string // Name of test or benchmark.
+ start highPrecisionTime // Time test or benchmark started
duration time.Duration
barrier chan bool // To signal parallel subtests they may start. Nil when T.Parallel is not present (B) or not usable (when fuzzing).
signal chan bool // To signal a test is done.
@@ -1457,7 +1458,7 @@ func (t *T) Parallel() {
// We don't want to include the time we spend waiting for serial tests
// in the test duration. Record the elapsed time thus far and reset the
// timer afterwards.
- t.duration += time.Since(t.start)
+ t.duration += highPrecisionTimeSince(t.start)
// Add to the list of tests to be released by the parent.
t.parent.sub = append(t.parent.sub, t)
@@ -1486,8 +1487,8 @@ func (t *T) Parallel() {
if t.chatty != nil {
t.chatty.Updatef(t.name, "=== CONT %s\n", t.name)
}
- running.Store(t.name, time.Now())
- t.start = time.Now()
+ running.Store(t.name, highPrecisionTimeNow())
+ t.start = highPrecisionTimeNow()
// Reset the local race counter to ignore any races that happened while this
// goroutine was blocked, such as in the parent test or in other parallel
@@ -1619,7 +1620,7 @@ func tRunner(t *T, fn func(t *T)) {
// Flush the output log up to the root before dying.
for root := &t.common; root.parent != nil; root = root.parent {
root.mu.Lock()
- root.duration += time.Since(root.start)
+ root.duration += highPrecisionTimeSince(root.start)
d := root.duration
root.mu.Unlock()
root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
@@ -1634,7 +1635,7 @@ func tRunner(t *T, fn func(t *T)) {
doPanic(err)
}
- t.duration += time.Since(t.start)
+ t.duration += highPrecisionTimeSince(t.start)
if len(t.sub) > 0 {
// Run parallel subtests.
@@ -1652,10 +1653,10 @@ func tRunner(t *T, fn func(t *T)) {
// Run any cleanup callbacks, marking the test as running
// in case the cleanup hangs.
- cleanupStart := time.Now()
+ cleanupStart := highPrecisionTimeNow()
running.Store(t.name, cleanupStart)
err := t.runCleanup(recoverAndReturnPanic)
- t.duration += time.Since(cleanupStart)
+ t.duration += highPrecisionTimeSince(cleanupStart)
if err != nil {
doPanic(err)
}
@@ -1684,7 +1685,7 @@ func tRunner(t *T, fn func(t *T)) {
}
}()
- t.start = time.Now()
+ t.start = highPrecisionTimeNow()
t.resetRaces()
fn(t)
@@ -1732,7 +1733,7 @@ func (t *T) Run(name string, f func(t *T)) bool {
if t.chatty != nil {
t.chatty.Updatef(t.name, "=== RUN %s\n", t.name)
}
- running.Store(t.name, time.Now())
+ running.Store(t.name, highPrecisionTimeNow())
// Instead of reducing the running count of this test before calling the
// tRunner and increasing it afterwards, we rely on tRunner keeping the
@@ -2372,7 +2373,7 @@ func (m *M) startAlarm() time.Time {
func runningList() []string {
var list []string
running.Range(func(k, v any) bool {
- list = append(list, fmt.Sprintf("%s (%v)", k.(string), time.Since(v.(time.Time)).Round(time.Second)))
+ list = append(list, fmt.Sprintf("%s (%v)", k.(string), highPrecisionTimeSince(v.(highPrecisionTime)).Round(time.Second)))
return true
})
sort.Strings(list)
diff --git a/src/testing/testing_other.go b/src/testing/testing_other.go
index 99a6276a4a..f91e3b4a2c 100644
--- a/src/testing/testing_other.go
+++ b/src/testing/testing_other.go
@@ -6,8 +6,26 @@
package testing
+import "time"
+
// isWindowsRetryable reports whether err is a Windows error code
// that may be fixed by retrying a failed filesystem operation.
func isWindowsRetryable(err error) bool {
return false
}
+
+// highPrecisionTime represents a single point in time.
+// On all systems except Windows, using time.Time is fine.
+type highPrecisionTime struct {
+ now time.Time
+}
+
+// highPrecisionTimeNow returns high precision time for benchmarking.
+func highPrecisionTimeNow() highPrecisionTime {
+ return highPrecisionTime{now: time.Now()}
+}
+
+// highPrecisionTimeSince returns duration since b.
+func highPrecisionTimeSince(b highPrecisionTime) time.Duration {
+ return time.Since(b.now)
+}
diff --git a/src/testing/testing_windows.go b/src/testing/testing_windows.go
index fd48ae9579..ebe4e01d23 100644
--- a/src/testing/testing_windows.go
+++ b/src/testing/testing_windows.go
@@ -9,7 +9,9 @@ package testing
import (
"errors"
"internal/syscall/windows"
+ "math/bits"
"syscall"
+ "time"
)
// isWindowsRetryable reports whether err is a Windows error code
@@ -30,3 +32,39 @@ func isWindowsRetryable(err error) bool {
}
return false
}
+
+// highPrecisionTime represents a single point in time with query performance counter.
+// time.Time on Windows has low system granularity, which is not suitable for
+// measuring short time intervals.
+//
+// TODO: If Windows runtime implements high resolution timing then highPrecisionTime
+// can be removed.
+type highPrecisionTime struct {
+ now int64
+}
+
+// highPrecisionTimeNow returns high precision time for benchmarking.
+func highPrecisionTimeNow() highPrecisionTime {
+ var t highPrecisionTime
+ // This should always succeed for Windows XP and above.
+ t.now = windows.QueryPerformanceCounter()
+ return t
+}
+
+func (a highPrecisionTime) sub(b highPrecisionTime) time.Duration {
+ delta := a.now - b.now
+
+ if queryPerformanceFrequency == 0 {
+ queryPerformanceFrequency = windows.QueryPerformanceFrequency()
+ }
+ hi, lo := bits.Mul64(uint64(delta), uint64(time.Second)/uint64(time.Nanosecond))
+ quo, _ := bits.Div64(hi, lo, uint64(queryPerformanceFrequency))
+ return time.Duration(quo)
+}
+
+var queryPerformanceFrequency int64
+
+// highPrecisionTimeSince returns duration since a.
+func highPrecisionTimeSince(a highPrecisionTime) time.Duration {
+ return highPrecisionTimeNow().sub(a)
+}
diff --git a/src/testing/testing_windows_test.go b/src/testing/testing_windows_test.go
new file mode 100644
index 0000000000..e75232dede
--- /dev/null
+++ b/src/testing/testing_windows_test.go
@@ -0,0 +1,25 @@
+// Copyright 2024 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 testing_test
+
+import (
+ "testing"
+ "time"
+)
+
+var sink time.Time
+var sinkHPT testing.HighPrecisionTime
+
+func BenchmarkTimeNow(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink = time.Now()
+ }
+}
+
+func BenchmarkHighPrecisionTimeNow(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sinkHPT = testing.HighPrecisionTimeNow()
+ }
+}
diff --git a/src/time/format.go b/src/time/format.go
index 9115609f60..875fb36df8 100644
--- a/src/time/format.go
+++ b/src/time/format.go
@@ -4,7 +4,10 @@
package time
-import "errors"
+import (
+ "errors"
+ "internal/stringslite"
+)
// These are predefined layouts for use in [Time.Format] and [time.Parse].
// The reference time used in these layouts is the specific time stamp:
@@ -827,17 +830,11 @@ type ParseError struct {
// newParseError creates a new ParseError.
// The provided value and valueElem are cloned to avoid escaping their values.
func newParseError(layout, value, layoutElem, valueElem, message string) *ParseError {
- valueCopy := cloneString(value)
- valueElemCopy := cloneString(valueElem)
+ valueCopy := stringslite.Clone(value)
+ valueElemCopy := stringslite.Clone(valueElem)
return &ParseError{layout, valueCopy, layoutElem, valueElemCopy, message}
}
-// cloneString returns a string copy of s.
-// Do not use strings.Clone to avoid dependency on strings package.
-func cloneString(s string) string {
- return string([]byte(s))
-}
-
// These are borrowed from unicode/utf8 and strconv and replicate behavior in
// that package, since we can't take a dependency on either.
const (
@@ -1368,7 +1365,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
}
// Otherwise create fake zone to record offset.
- zoneNameCopy := cloneString(zoneName) // avoid leaking the input value
+ zoneNameCopy := stringslite.Clone(zoneName) // avoid leaking the input value
t.setLoc(FixedZone(zoneNameCopy, zoneOffset))
return t, nil
}
@@ -1389,7 +1386,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error)
offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
offset *= 3600
}
- zoneNameCopy := cloneString(zoneName) // avoid leaking the input value
+ zoneNameCopy := stringslite.Clone(zoneName) // avoid leaking the input value
t.setLoc(FixedZone(zoneNameCopy, offset))
return t, nil
}
diff --git a/src/time/time.go b/src/time/time.go
index 8c24e1c481..0bbdeaecf5 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -53,7 +53,10 @@
//
// On some systems the monotonic clock will stop if the computer goes to sleep.
// On such a system, t.Sub(u) may not accurately reflect the actual
-// time that passed between t and u.
+// time that passed between t and u. The same applies to other functions and
+// methods that subtract times, such as [Since], [Until], [Before], [After],
+// [Add], [Sub], [Equal] and [Compare]. In some cases, you may need to strip
+// the monotonic clock to get accurate results.
//
// Because the monotonic clock reading has no meaning outside
// the current process, the serialized forms generated by t.GobEncode,
diff --git a/src/unique/clone.go b/src/unique/clone.go
index b30d44e393..36ced14ece 100644
--- a/src/unique/clone.go
+++ b/src/unique/clone.go
@@ -6,6 +6,7 @@ package unique
import (
"internal/abi"
+ "internal/stringslite"
"unsafe"
)
@@ -20,7 +21,7 @@ import (
func clone[T comparable](value T, seq *cloneSeq) T {
for _, offset := range seq.stringOffsets {
ps := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&value)) + offset))
- *ps = cloneString(*ps)
+ *ps = stringslite.Clone(*ps)
}
return value
}
@@ -86,15 +87,3 @@ func buildArrayCloneSeq(typ *abi.Type, seq *cloneSeq, baseOffset uintptr) {
offset = (offset + align - 1) &^ (align - 1)
}
}
-
-// cloneString is a copy of strings.Clone, because we can't depend on the strings
-// package here. Several packages that might make use of unique, like net, explicitly
-// forbid depending on unicode, which strings depends on.
-func cloneString(s string) string {
- if len(s) == 0 {
- return ""
- }
- b := make([]byte, len(s))
- copy(b, s)
- return unsafe.String(&b[0], len(b))
-}
diff --git a/src/unique/handle.go b/src/unique/handle.go
index d98f8022d7..4d9669162f 100644
--- a/src/unique/handle.go
+++ b/src/unique/handle.go
@@ -102,7 +102,7 @@ var (
cleanupMu sync.Mutex
cleanupFuncsMu sync.Mutex
cleanupFuncs []func()
- cleanupNotify []func() // One-time notifcations when cleanups finish.
+ cleanupNotify []func() // One-time notifications when cleanups finish.
)
type uniqueMap[T comparable] struct {
diff --git a/test/codegen/shift.go b/test/codegen/shift.go
index 50d60426d0..5bd7acc063 100644
--- a/test/codegen/shift.go
+++ b/test/codegen/shift.go
@@ -453,6 +453,27 @@ func checkMergedShifts32(a [256]uint32, b [256]uint64, u uint32, v uint32) {
b[2] = b[v>>25]
}
+func checkMergedShifts64(a [256]uint32, b [256]uint64, v uint64) {
+ // ppc64x: -"CLRLSLDI", "RLWNM\t[$]10, R[0-9]+, [$]22, [$]29, R[0-9]+"
+ a[0] = a[uint8(v>>24)]
+ // ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
+ a[1] = a[uint8(v>>25)]
+ // ppc64x: -"CLRLSLDI", "RLWNM\t[$]9, R[0-9]+, [$]23, [$]29, R[0-9]+"
+ a[2] = a[v>>25&0x7F]
+ // ppc64x: -"CLRLSLDI", "RLWNM\t[$]3, R[0-9]+, [$]29, [$]29, R[0-9]+"
+ a[3] = a[(v>>31)&0x01]
+ // ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
+ a[4] = a[(v>>30)&0x07]
+ // ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
+ a[5] = a[(v>>32)&0x01]
+ // ppc64x: "SRD", "CLRLSLDI", -"RLWNM"
+ a[5] = a[(v>>34)&0x03]
+ // ppc64x: -"CLRLSLDI", "RLWNM\t[$]12, R[0-9]+, [$]21, [$]28, R[0-9]+"
+ b[0] = b[uint8(v>>23)]
+ // ppc64x: -"CLRLSLDI", "RLWNM\t[$]15, R[0-9]+, [$]21, [$]28, R[0-9]+"
+ b[1] = b[(v>>20)&0xFF]
+}
+
// 128 bit shifts
func check128bitShifts(x, y uint64, bits uint) (uint64, uint64) {