diff --git a/api/go1.23.txt b/api/go1.23.txt index dc92d3fe56b..9363bd41f3c 100644 --- a/api/go1.23.txt +++ b/api/go1.23.txt @@ -18,7 +18,7 @@ pkg crypto/tls, type Config struct, EncryptedClientHelloRejectionVerify func(Con pkg crypto/tls, type ConnectionState struct, ECHAccepted bool #63369 pkg crypto/tls, type ECHRejectionError struct #63369 pkg crypto/tls, type ECHRejectionError struct, RetryConfigList []uint8 #63369 -pkg crypto/tls, type QUICConfig struct, EnableStoreSessionEvent bool #63691 +pkg crypto/tls, type QUICConfig struct, EnableSessionEvents bool #63691 pkg crypto/tls, type QUICEvent struct, SessionState *SessionState #63691 pkg crypto/tls, type QUICSessionTicketOptions struct, Extra [][]uint8 #63691 pkg crypto/x509, func ParseOID(string) (OID, error) #66249 diff --git a/doc/next/1-intro.md b/doc/next/1-intro.md deleted file mode 100644 index 585c6c8e52c..00000000000 --- a/doc/next/1-intro.md +++ /dev/null @@ -1,14 +0,0 @@ - - - - -## DRAFT RELEASE NOTES — Introduction to Go 1.23 {#introduction} - -**Go 1.23 is not yet released. These are work-in-progress release notes. -Go 1.23 is expected to be released in August 2024.** diff --git a/doc/next/2-language.md b/doc/next/2-language.md deleted file mode 100644 index 83e3a51437e..00000000000 --- a/doc/next/2-language.md +++ /dev/null @@ -1,18 +0,0 @@ -## Changes to the language {#language} - - -Go 1.23 makes the (Go 1.22) ["range-over-func" experiment](/wiki/RangefuncExperiment) a part of the language. -The "range" clause in a "for-range" loop now accepts iterator functions of the following types - - func(func() bool) - func(func(K) bool) - func(func(K, V) bool) - -as range expressions. -Calls of the iterator argument function produce the iteration values for the "for-range" loop. -For details see the [iter] package documentation and the [language spec](/ref/spec#For_range). -For motivation see the 2022 ["range-over-func" discussion](/issue/56413). - - -Go 1.23 includes preview support for [generic type aliases](/issue/46477). -Building the toolchain with `GOEXPERIMENT=aliastypeparams` enables this feature. diff --git a/doc/next/3-tools.md b/doc/next/3-tools.md deleted file mode 100644 index 7ae651e0b4e..00000000000 --- a/doc/next/3-tools.md +++ /dev/null @@ -1,84 +0,0 @@ -## Tools {#tools} - -### Telemetry - - -Starting in Go 1.23, the Go toolchain can collect usage and breakage -statistics that help the Go team understand how the Go toolchain is -used and how well it is working. We refer to these statistics as -[Go telemetry](/doc/telemetry). - -Go telemetry is an _opt-in system_, controlled by the -[`go` `telemetry` command](/cmd/go/#hdr-Manage_telemetry_data_and_settings). -By default, the toolchain programs -collect statistics in counter files that can be inspected locally -but are otherwise unused (`go` `telemetry` `local`). - -To help us keep Go working well and understand Go usage, -please consider opting in to Go telemetry by running -`go` `telemetry` `on`. -In that mode, -anonymous counter reports are uploaded to -[telemetry.go.dev](https://telemetry.go.dev) weekly, -where they are aggregated into graphs and also made -available for download by any Go contributors or users -wanting to analyze the data. -See “[Go Telemetry](/doc/telemetry)” for more details -about the Go Telemetry system. - -### Go command {#go-command} - -Setting the `GOROOT_FINAL` environment variable no longer has an effect -([#62047](/issue/62047)). -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. - - -The new `go` `env` `-changed` flag causes the command to print only -those settings whose effective value differs from the default value -that would be obtained in an empty environment with no prior uses of the `-w` flag. - - -The new `go` `mod` `tidy` `-diff` flag causes the command not to modify -the files but instead print the necessary changes as a unified diff. -It exits with a non-zero code if updates are needed. - - -The `go` `list` `-m` `-json` command now includes new `Sum` and `GoModSum` fields. -This is similar to the existing behavior of the `go` `mod` `download` `-json` command. - - -The new `godebug` directive in `go.mod` and `go.work` declares a -[GODEBUG setting](/doc/godebug) to apply for the work module or workspace in use. - -### Vet {#vet} - - -The `go vet` subcommand now includes the -[stdversion](https://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](/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} - - -[cmd/cgo] supports the new `-ldflags` flag for passing flags to the C linker. -The `go` command uses it automatically, avoiding "argument list too long" -errors with a very large `CGO_LDFLAGS`. - -### Trace {#trace} - - -The `trace` tool now better tolerates partially broken traces by attempting to -recover what trace data it can. This functionality is particularly helpful when -viewing a trace that was collected during a program crash, since the trace data -leading up to the crash will now [be recoverable](/issue/65319) under most -circumstances. diff --git a/doc/next/4-runtime.md b/doc/next/4-runtime.md deleted file mode 100644 index 7553154a162..00000000000 --- a/doc/next/4-runtime.md +++ /dev/null @@ -1,7 +0,0 @@ -## Runtime {#runtime} - -The traceback printed by the runtime after an unhandled panic or other -fatal error now indents the second and subsequent lines of the error -message (for example, the argument to panic) by a single tab, so that -it can be unambiguously distinguished from the stack trace of the -first goroutine. See [#64590](/issue/64590) for discussion. diff --git a/doc/next/5-toolchain.md b/doc/next/5-toolchain.md deleted file mode 100644 index 51a1dbfbdf3..00000000000 --- a/doc/next/5-toolchain.md +++ /dev/null @@ -1,38 +0,0 @@ -## Compiler {#compiler} - -The build time overhead to building with [Profile Guided Optimization](/doc/pgo) has been reduced significantly. -Previously, large builds could see 100%+ build time increase from enabling PGO. -In Go 1.23, overhead should be in the single digit percentages. - - -The compiler in Go 1.23 can now overlap the stack frame slots of local variables -accessed in disjoint regions of a function, which reduces stack usage -for Go applications. - - -For 386 and amd64, the compiler will use information from PGO to align certain -hot blocks in loops. This improves performance an additional 1-1.5% at -a cost of an additional 0.1% text and binary size. This is currently only implemented -on 386 and amd64 because it has not shown an improvement on other platforms. -Hot block alignment can be disabled with `-gcflags=[=]-d=alignhot=0` - -## Assembler {#assembler} - -## Linker {#linker} - - -The linker now disallows using a `//go:linkname` directive to refer to -internal symbols in the standard library (including the runtime) that -are not marked with `//go:linkname` on their definitions. -Similarly, the linker disallows references to such symbols from assembly -code. -For backward compatibility, existing usages of `//go:linkname` found in -a large open-source code corpus remain supported. -Any new references to standard library internal symbols will be disallowed. - -A linker command line flag `-checklinkname=0` can be used to disable -this check, for debugging and experimenting purposes. - - -When building a dynamically linked ELF binary (including PIE binary), the -new `-bindnow` flag enables immediate function binding. diff --git a/doc/next/6-stdlib/0-heading.md b/doc/next/6-stdlib/0-heading.md deleted file mode 100644 index 02351ce1c09..00000000000 --- a/doc/next/6-stdlib/0-heading.md +++ /dev/null @@ -1,2 +0,0 @@ -## Core library {#library} - diff --git a/doc/next/6-stdlib/1-time.md b/doc/next/6-stdlib/1-time.md deleted file mode 100644 index 6046ac53501..00000000000 --- a/doc/next/6-stdlib/1-time.md +++ /dev/null @@ -1,32 +0,0 @@ -### Timer changes - -Go 1.23 makes two significant changes to the implementation of -[time.Timer] and [time.Ticker]. - - -First, `Timer`s and `Ticker`s that are no longer referred to by the program -become eligible for garbage collection immediately, even if their -`Stop` methods have not been called. -Earlier versions of Go did not collect unstopped `Timer`s until after -they had fired and never collected unstopped `Ticker`s. - - -Second, the timer channel associated with a `Timer` or `Ticker` is -now unbuffered, with capacity 0. -The main effect of this change is that Go now guarantees -that for any call to a `Reset` or `Stop` method, no stale values -prepared before that call will be sent or received after the call. -Earlier versions of Go used channels with a one-element buffer, -making it difficult to use `Reset` and `Stop` correctly. -A visible effect of this change is that `len` and `cap` of timer channels -now returns 0 instead of 1, which may affect programs that -poll the length to decide whether a receive on the timer channel -will succeed. -Such code should use a non-blocking receive instead. - -These new behaviors are only enabled when the main Go program -is in a module with a `go.mod` `go` line using Go 1.23.0 or later. -When Go 1.23 builds older programs, the old behaviors remain in effect. -The new [GODEBUG setting](/doc/godebug) [`asynctimerchan=1`](/pkg/time/#NewTimer) -can be used to revert back to asynchronous channel behaviors -even when a program names Go 1.23.0 or later in its `go.mod` file. diff --git a/doc/next/6-stdlib/2-unique.md b/doc/next/6-stdlib/2-unique.md deleted file mode 100644 index 42737a52fa3..00000000000 --- a/doc/next/6-stdlib/2-unique.md +++ /dev/null @@ -1,13 +0,0 @@ -### New unique package - -The new [unique] package provides facilities for -canonicalizing values (like "interning" or "hash-consing"). - -Any value of comparable type may be canonicalized with the new -`Make[T]` function, which produces a reference to a canonical copy of -the value in the form of a `Handle[T]`. -Two `Handle[T]` are equal if and only if the values used to produce the -handles are equal, allowing programs to deduplicate values and reduce -their memory footprint. -Comparing two `Handle[T]` values is efficient, reducing down to a simple -pointer comparison. diff --git a/doc/next/6-stdlib/3-iter.md b/doc/next/6-stdlib/3-iter.md deleted file mode 100644 index bbb3bbbd8f2..00000000000 --- a/doc/next/6-stdlib/3-iter.md +++ /dev/null @@ -1,29 +0,0 @@ -### Iterators - -The new [iter] package provides the basic definitions for working with -user-defined iterators. - -The [slices] package adds several functions that work with iterators: -- [All](/pkg/slices#All) returns an iterator over slice indexes and values. -- [Values](/pkg/slices#Values) returns an iterator over slice elements. -- [Backward](/pkg/slices#Backward) returns an iterator that loops over - a slice backward. -- [Collect](/pkg/slices#Collect) collects values from an iterator into - a new slice. -- [AppendSeq](/pkg/slices#AppendSeq) appends values from an iterator to - an existing slice. -- [Sorted](/pkg/slices#Sorted) collects values from an iterator into a - new slice, and then sorts the slice. -- [SortedFunc](/pkg/slices#SortedFunc) is like `Sorted` but with a - comparison function. -- [SortedStableFunc](/pkg/slices#SortedStableFunc) is like `SortFunc` - but uses a stable sort algorithm. -- [Chunk](/pkg/slices#Chunk) returns an iterator over consecutive - sub-slices of up to n elements of a slice. - -The [maps] package adds several functions that work with iterators: -- [All](/pkg/maps#All) returns an iterator over key-value pairs from a map. -- [Keys](/pkg/maps#Keys) returns an iterator over keys in a map. -- [Values](/pkg/maps#Values) returns an iterator over values in a map. -- [Insert](/pkg/maps#Insert) adds the key-value pairs from an iterator to an existing map. -- [Collect](/pkg/maps#Collect) collects key-value pairs from an iterator into a new map and returns it. diff --git a/doc/next/6-stdlib/4-structs.md b/doc/next/6-stdlib/4-structs.md deleted file mode 100644 index adf42f1b375..00000000000 --- a/doc/next/6-stdlib/4-structs.md +++ /dev/null @@ -1,11 +0,0 @@ -### New structs package - -The new [structs] package provides -types for struct fields that modify properties of -the containing struct type such as memory layout. - -In this release, the only such type is -[`HostLayout`](/pkg/structs#HostLayout) -which indicates that a structure with a field of that -type has a layout that conforms to host platform -expectations. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/0-heading.md b/doc/next/6-stdlib/99-minor/0-heading.md deleted file mode 100644 index a98105e8ccb..00000000000 --- a/doc/next/6-stdlib/99-minor/0-heading.md +++ /dev/null @@ -1,3 +0,0 @@ -### Minor changes to the library {#minor_library_changes} - - diff --git a/doc/next/6-stdlib/99-minor/README b/doc/next/6-stdlib/99-minor/README deleted file mode 100644 index fac778de050..00000000000 --- a/doc/next/6-stdlib/99-minor/README +++ /dev/null @@ -1 +0,0 @@ -API changes and other small changes to the standard library go here. diff --git a/doc/next/6-stdlib/99-minor/archive/tar/50102.md b/doc/next/6-stdlib/99-minor/archive/tar/50102.md deleted file mode 100644 index ed8675f6933..00000000000 --- a/doc/next/6-stdlib/99-minor/archive/tar/50102.md +++ /dev/null @@ -1,4 +0,0 @@ -If the argument to [FileInfoHeader] implements the new [FileInfoNames] -interface, then the interface methods will be used to set the Uname/Gname -of the file header. This allows applications to override the system-dependent -Uname/Gname lookup. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/63369.md b/doc/next/6-stdlib/99-minor/crypto/tls/63369.md deleted file mode 100644 index 6ec5b5bdf62..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/tls/63369.md +++ /dev/null @@ -1,3 +0,0 @@ -The TLS client now supports the Encrypted Client Hello [draft specification](https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html). -This feature can be enabled by setting the [Config.EncryptedClientHelloConfigList] -field to an encoded ECHConfigList for the host that is being connected to. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/63691.md b/doc/next/6-stdlib/99-minor/crypto/tls/63691.md deleted file mode 100644 index 67ed04cf00d..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/tls/63691.md +++ /dev/null @@ -1,3 +0,0 @@ -The [QUICConn] type used by QUIC implementations includes new events -reporting on the state of session resumption, and provides a way for -the QUIC layer to add data to session tickets and session cache entries. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/66214.md b/doc/next/6-stdlib/99-minor/crypto/tls/66214.md deleted file mode 100644 index ebe1b490f57..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/tls/66214.md +++ /dev/null @@ -1,3 +0,0 @@ -3DES cipher suites were removed from the default list used when -[Config.CipherSuites] is nil. The default can be reverted by adding `tls3des=1` to -the GODEBUG environment variable. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/67061.md b/doc/next/6-stdlib/99-minor/crypto/tls/67061.md deleted file mode 100644 index fe3620ed6d2..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/tls/67061.md +++ /dev/null @@ -1,3 +0,0 @@ -The experimental post-quantum key exchange mechanism X25519Kyber768Draft00 -is now enabled by default when [Config.CurvePreferences] is nil. -The default can be reverted by adding `tlskyber=0` to the GODEBUG environment variable. diff --git a/doc/next/6-stdlib/99-minor/crypto/tls/67065.md b/doc/next/6-stdlib/99-minor/crypto/tls/67065.md deleted file mode 100644 index 3fcc8d53093..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/tls/67065.md +++ /dev/null @@ -1,3 +0,0 @@ -Go 1.23 changed the behavior of [X509KeyPair] and [LoadX509KeyPair] -to populate the [Certificate.Leaf] field of the returned [Certificate]. -The new `x509keypairleaf` [GODEBUG setting](/doc/godebug) is added for this behavior. diff --git a/doc/next/6-stdlib/99-minor/crypto/x509/45990.md b/doc/next/6-stdlib/99-minor/crypto/x509/45990.md deleted file mode 100644 index 2eda8476a9a..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/x509/45990.md +++ /dev/null @@ -1,3 +0,0 @@ -[CreateCertificateRequest] now correct supports RSA-PSS signature algorithms. - -[CreateCertificateRequest] and [CreateRevocationList] now verify the generated signature using the signer’s public key. If the signature is invalid, an error is returned. This has been the behavior of [CreateCertificate] since Go 1.16. diff --git a/doc/next/6-stdlib/99-minor/crypto/x509/62048.md b/doc/next/6-stdlib/99-minor/crypto/x509/62048.md deleted file mode 100644 index aaa8c5e3506..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/x509/62048.md +++ /dev/null @@ -1,4 +0,0 @@ -The [`x509sha1` GODEBUG setting](/pkg/crypto/x509#InsecureAlgorithmError) will -be removed in the next Go major release (Go 1.24). This will mean that crypto/x509 -will no longer support verifying signatures on certificates that use SHA-1 based -signature algorithms. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/crypto/x509/66249.md b/doc/next/6-stdlib/99-minor/crypto/x509/66249.md deleted file mode 100644 index d449e74d663..00000000000 --- a/doc/next/6-stdlib/99-minor/crypto/x509/66249.md +++ /dev/null @@ -1,3 +0,0 @@ -The new [ParseOID] function parses a dot-encoded ASN.1 Object Identifier string. -The [OID] type now implements the [encoding.BinaryMarshaler], -[encoding.BinaryUnmarshaler], [encoding.TextMarshaler], [encoding.TextUnmarshaler] interfaces. diff --git a/doc/next/6-stdlib/99-minor/database/sql/64707.md b/doc/next/6-stdlib/99-minor/database/sql/64707.md deleted file mode 100644 index 17d4516ba8a..00000000000 --- a/doc/next/6-stdlib/99-minor/database/sql/64707.md +++ /dev/null @@ -1,3 +0,0 @@ -Errors returned by [driver.Valuer] implementations are now wrapped for -improved error handling during operations like [DB.Query], [DB.Exec], -and [DB.QueryRow]. diff --git a/doc/next/6-stdlib/99-minor/debug/elf/66054.md b/doc/next/6-stdlib/99-minor/debug/elf/66054.md deleted file mode 100644 index 0b3443f7d40..00000000000 --- a/doc/next/6-stdlib/99-minor/debug/elf/66054.md +++ /dev/null @@ -1,3 +0,0 @@ -The `debug/elf` package now defines [PT_OPENBSD_NOBTCFI]. This [ProgType] is -used to disable Branch Tracking Control Flow Integrity (BTCFI) enforcement -on OpenBSD binaries. diff --git a/doc/next/6-stdlib/99-minor/debug/elf/66836.md b/doc/next/6-stdlib/99-minor/debug/elf/66836.md deleted file mode 100644 index 4aad5798dd4..00000000000 --- a/doc/next/6-stdlib/99-minor/debug/elf/66836.md +++ /dev/null @@ -1,2 +0,0 @@ -Now defines the symbol type constants [STT_RELC], [STT_SRELC], and -[STT_GNU_IFUNC]. diff --git a/doc/next/6-stdlib/99-minor/encoding/binary/60023.md b/doc/next/6-stdlib/99-minor/encoding/binary/60023.md deleted file mode 100644 index 015bfc38031..00000000000 --- a/doc/next/6-stdlib/99-minor/encoding/binary/60023.md +++ /dev/null @@ -1,3 +0,0 @@ -The new [Encode] and [Decode] functions are byte slice equivalents -to [Read] and [Write]. -[Append] allows marshaling multiple data into the same byte slice. diff --git a/doc/next/6-stdlib/99-minor/go/ast/66339.md b/doc/next/6-stdlib/99-minor/go/ast/66339.md deleted file mode 100644 index 0eec51ecd69..00000000000 --- a/doc/next/6-stdlib/99-minor/go/ast/66339.md +++ /dev/null @@ -1,2 +0,0 @@ -The new [Preorder] function returns a convenient iterator over all the -nodes of a syntax tree. diff --git a/doc/next/6-stdlib/99-minor/go/types/46477.md b/doc/next/6-stdlib/99-minor/go/types/46477.md deleted file mode 100644 index 7f744dc6ae4..00000000000 --- a/doc/next/6-stdlib/99-minor/go/types/46477.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/go/types/65772.md b/doc/next/6-stdlib/99-minor/go/types/65772.md deleted file mode 100644 index ec7f0b0e59b..00000000000 --- a/doc/next/6-stdlib/99-minor/go/types/65772.md +++ /dev/null @@ -1,3 +0,0 @@ -The [Func] type, which represents a function or method symbol, now -has a [Func.Signature] method that returns the function's type, which -is always a `Signature`. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/go/types/66559.md b/doc/next/6-stdlib/99-minor/go/types/66559.md deleted file mode 100644 index e3884594fa2..00000000000 --- a/doc/next/6-stdlib/99-minor/go/types/66559.md +++ /dev/null @@ -1,3 +0,0 @@ -The [Alias] type now has an [Rhs] method that returns the type on the -right-hand side of its declaration: given `type A = B`, the `Rhs` of A -is B. ([#66559](/issue/66559)) diff --git a/doc/next/6-stdlib/99-minor/go/types/67143.md b/doc/next/6-stdlib/99-minor/go/types/67143.md deleted file mode 100644 index 405c679378e..00000000000 --- a/doc/next/6-stdlib/99-minor/go/types/67143.md +++ /dev/null @@ -1,2 +0,0 @@ -The methods [Alias.Origin], [Alias.SetTypeParams], [Alias.TypeParams], -and [Alias.TypeArgs] have been added. They are needed for generic alias types. diff --git a/doc/next/6-stdlib/99-minor/go/types/cl-577715.md b/doc/next/6-stdlib/99-minor/go/types/cl-577715.md deleted file mode 100644 index 939d5ac46c6..00000000000 --- a/doc/next/6-stdlib/99-minor/go/types/cl-577715.md +++ /dev/null @@ -1,4 +0,0 @@ - -By default, go/types now produces [Alias] type nodes for type aliases. -This behavior can be controlled by the `GODEBUG` `gotypesalias` flag. -Its default has changed from 0 in Go 1.22 to 1 in Go 1.23. diff --git a/doc/next/6-stdlib/99-minor/iter/61897.md b/doc/next/6-stdlib/99-minor/iter/61897.md deleted file mode 100644 index 02d77cd11df..00000000000 --- a/doc/next/6-stdlib/99-minor/iter/61897.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/6-stdlib/99-minor/maps/61900.md b/doc/next/6-stdlib/99-minor/maps/61900.md deleted file mode 100644 index 02d77cd11df..00000000000 --- a/doc/next/6-stdlib/99-minor/maps/61900.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/6-stdlib/99-minor/math/rand/v2/61716.md b/doc/next/6-stdlib/99-minor/math/rand/v2/61716.md deleted file mode 100644 index 68ff614ee5c..00000000000 --- a/doc/next/6-stdlib/99-minor/math/rand/v2/61716.md +++ /dev/null @@ -1,2 +0,0 @@ -The [Uint] function and [Rand.Uint] method have been added. -They were inadvertently left out of Go 1.22. diff --git a/doc/next/6-stdlib/99-minor/math/rand/v2/67059.md b/doc/next/6-stdlib/99-minor/math/rand/v2/67059.md deleted file mode 100644 index c66110c7a44..00000000000 --- a/doc/next/6-stdlib/99-minor/math/rand/v2/67059.md +++ /dev/null @@ -1 +0,0 @@ -The new [ChaCha8.Read] method implements the [io.Reader] interface. diff --git a/doc/next/6-stdlib/99-minor/net/62254.md b/doc/next/6-stdlib/99-minor/net/62254.md deleted file mode 100644 index 639140bbec1..00000000000 --- a/doc/next/6-stdlib/99-minor/net/62254.md +++ /dev/null @@ -1,3 +0,0 @@ -The new type [KeepAliveConfig] permits fine-tuning the keep-alive -options for TCP connections, via a new [TCPConn.SetKeepAliveConfig] -method and new KeepAliveConfig fields for [Dialer] and [ListenConfig]. diff --git a/doc/next/6-stdlib/99-minor/net/63116.md b/doc/next/6-stdlib/99-minor/net/63116.md deleted file mode 100644 index 87a09837492..00000000000 --- a/doc/next/6-stdlib/99-minor/net/63116.md +++ /dev/null @@ -1,3 +0,0 @@ -The [DNSError] type now wraps errors caused by timeouts or cancellation. -For example, `errors.Is(someDNSErr, context.DeadlineExceedeed)` -will now report whether a DNS error was caused by a timeout. diff --git a/doc/next/6-stdlib/99-minor/net/67925.md b/doc/next/6-stdlib/99-minor/net/67925.md deleted file mode 100644 index e43f0cd644e..00000000000 --- a/doc/next/6-stdlib/99-minor/net/67925.md +++ /dev/null @@ -1,3 +0,0 @@ -The new `GODEBUG` setting `netedns0=0` disables sending EDNS0 -additional headers on DNS requests, as they reportedly break the DNS -server on some modems. diff --git a/doc/next/6-stdlib/99-minor/net/http/46443.md b/doc/next/6-stdlib/99-minor/net/http/46443.md deleted file mode 100644 index 37ceae9eddd..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/46443.md +++ /dev/null @@ -1,3 +0,0 @@ -[Cookie] now preserves double quotes surrounding a cookie value. -The new [Cookie.Quoted] field indicates whether the [Cookie.Value] -was originally quoted. diff --git a/doc/next/6-stdlib/99-minor/net/http/61472.md b/doc/next/6-stdlib/99-minor/net/http/61472.md deleted file mode 100644 index b3c2fd54094..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/61472.md +++ /dev/null @@ -1 +0,0 @@ -The new [Request.CookiesNamed] method retrieves all cookies that match the given name. diff --git a/doc/next/6-stdlib/99-minor/net/http/62490.md b/doc/next/6-stdlib/99-minor/net/http/62490.md deleted file mode 100644 index 891eb45daea..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/62490.md +++ /dev/null @@ -1 +0,0 @@ -The new [Cookie.Partitioned] field identifies cookies with the Partitioned attribute. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/net/http/64910.md b/doc/next/6-stdlib/99-minor/net/http/64910.md deleted file mode 100644 index 28452ee9327..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/64910.md +++ /dev/null @@ -1,2 +0,0 @@ -The patterns used by [ServeMux] now allow one or more spaces or tabs after the method name. -Previously, only a single space was permitted. diff --git a/doc/next/6-stdlib/99-minor/net/http/66008.md b/doc/next/6-stdlib/99-minor/net/http/66008.md deleted file mode 100644 index e8603707ef3..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/66008.md +++ /dev/null @@ -1,7 +0,0 @@ -The new [ParseCookie] function parses a Cookie header value and -returns all the cookies which were set in it. Since the same cookie -name can appear multiple times the returned Values can contain -more than one value for a given key. - -The new [ParseSetCookie] function parses a Set-Cookie header value and -returns a cookie. It returns an error on syntax error. diff --git a/doc/next/6-stdlib/99-minor/net/http/66343.md b/doc/next/6-stdlib/99-minor/net/http/66343.md deleted file mode 100644 index b39e8624e7d..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/66343.md +++ /dev/null @@ -1,16 +0,0 @@ -[ServeContent], [ServeFile], and [ServeFileFS] now remove -the `Cache-Control`, `Content-Encoding`, `Etag`, and `Last-Modified` -headers when serving an error. These headers usually apply to the -non-error content, but not to the text of errors. - -Middleware which wraps a [ResponseWriter] and applies on-the-fly -encoding, such as `Content-Encoding: gzip`, will not function after -this change. The previous behavior of [ServeContent], [ServeFile], -and [ServeFileFS] may be restored by setting -`GODEBUG=httpservecontentkeepheaders=1`. - -Note that middleware which changes the size of the served content -(such as by compressing it) already does not function properly when -[ServeContent] handles a Range request. On-the-fly compression -should use the `Transfer-Encoding` header instead of `Content-Encoding`. - diff --git a/doc/next/6-stdlib/99-minor/net/http/66405.md b/doc/next/6-stdlib/99-minor/net/http/66405.md deleted file mode 100644 index c827b4b2192..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/66405.md +++ /dev/null @@ -1,3 +0,0 @@ -For inbound requests, the new [Request.Pattern] field contains the [ServeMux] -pattern (if any) that matched the request. This field is not set when -`GODEBUG=httpmuxgo121=1` is set. diff --git a/doc/next/6-stdlib/99-minor/net/http/httptest/59473.md b/doc/next/6-stdlib/99-minor/net/http/httptest/59473.md deleted file mode 100644 index a640bbd0e4b..00000000000 --- a/doc/next/6-stdlib/99-minor/net/http/httptest/59473.md +++ /dev/null @@ -1,2 +0,0 @@ -The new [NewRequestWithContext] method creates an incoming request with -a [context.Context]. diff --git a/doc/next/6-stdlib/99-minor/os/33357.md b/doc/next/6-stdlib/99-minor/os/33357.md deleted file mode 100644 index bd542d9d77a..00000000000 --- a/doc/next/6-stdlib/99-minor/os/33357.md +++ /dev/null @@ -1,3 +0,0 @@ -The [Stat] function now sets the [ModeSocket] bit for -files that are Unix sockets on Windows. These files are identified -by having a reparse tag set to `IO_REPARSE_TAG_AF_UNIX`. diff --git a/doc/next/6-stdlib/99-minor/os/61893.md b/doc/next/6-stdlib/99-minor/os/61893.md deleted file mode 100644 index d22060c8698..00000000000 --- a/doc/next/6-stdlib/99-minor/os/61893.md +++ /dev/null @@ -1,7 +0,0 @@ -On Windows, the mode bits reported by [Lstat] and [Stat] for -reparse points changed. Mount points no longer have [ModeSymlink] set, -and reparse points that are not symlinks, Unix sockets, or dedup files -now always have [ModeIrregular] set. -This behavior is controlled by the `winsymlink` setting. -For Go 1.23, it defaults to `winsymlink=1`. -Previous versions default to `winsymlink=0`. diff --git a/doc/next/6-stdlib/99-minor/os/62484.md b/doc/next/6-stdlib/99-minor/os/62484.md deleted file mode 100644 index c99801bb6c7..00000000000 --- a/doc/next/6-stdlib/99-minor/os/62484.md +++ /dev/null @@ -1 +0,0 @@ -The [CopyFS] function copies an [io/fs.FS] into the local filesystem. diff --git a/doc/next/6-stdlib/99-minor/os/63703.md b/doc/next/6-stdlib/99-minor/os/63703.md deleted file mode 100644 index 3fbb2594cf7..00000000000 --- a/doc/next/6-stdlib/99-minor/os/63703.md +++ /dev/null @@ -1,5 +0,0 @@ -On Windows, [Readlink] no longer tries to normalize volumes -to drive letters, which was not always even possible. -This behavior is controlled by the `winreadlinkvolume` setting. -For Go 1.23, it defaults to `winreadlinkvolume=1`. -Previous versions default to `winreadlinkvolume=0`. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/path/filepath/57151.md b/doc/next/6-stdlib/99-minor/path/filepath/57151.md deleted file mode 100644 index 5a99e3a4f2c..00000000000 --- a/doc/next/6-stdlib/99-minor/path/filepath/57151.md +++ /dev/null @@ -1,2 +0,0 @@ -The new [Localize] function safely converts a slash-separated -path into an operating system path. diff --git a/doc/next/6-stdlib/99-minor/path/filepath/63703.md b/doc/next/6-stdlib/99-minor/path/filepath/63703.md deleted file mode 100644 index da2b132d8b3..00000000000 --- a/doc/next/6-stdlib/99-minor/path/filepath/63703.md +++ /dev/null @@ -1,11 +0,0 @@ -On Windows, [EvalSymlinks] no longer evaluates mount points, -which was a source of many inconsistencies and bugs. -This behavior is controlled by the `winsymlink` setting. -For Go 1.23, it defaults to `winsymlink=1`. -Previous versions default to `winsymlink=0`. - -On Windows, [EvalSymlinks] no longer tries to normalize -volumes to drive letters, which was not always even possible. -This behavior is controlled by the `winreadlinkvolume` setting. -For Go 1.23, it defaults to `winreadlinkvolume=1`. -Previous versions default to `winreadlinkvolume=0`. \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/reflect/60427.md b/doc/next/6-stdlib/99-minor/reflect/60427.md deleted file mode 100644 index bca13607346..00000000000 --- a/doc/next/6-stdlib/99-minor/reflect/60427.md +++ /dev/null @@ -1,6 +0,0 @@ -The new methods synonymous with the method of the same name -in [Value] are added to [Type]: -1. [Type.OverflowComplex] -2. [Type.OverflowFloat] -3. [Type.OverflowInt] -4. [Type.OverflowUint] diff --git a/doc/next/6-stdlib/99-minor/reflect/61308.md b/doc/next/6-stdlib/99-minor/reflect/61308.md deleted file mode 100644 index ec24655dce2..00000000000 --- a/doc/next/6-stdlib/99-minor/reflect/61308.md +++ /dev/null @@ -1,3 +0,0 @@ -The new [SliceAt] function is analogous to [NewAt], but for slices. - -The [Value.Pointer] and [Value.UnsafePointer] methods now support values of kind [String]. diff --git a/doc/next/6-stdlib/99-minor/reflect/66056.md b/doc/next/6-stdlib/99-minor/reflect/66056.md deleted file mode 100644 index b5f39349df1..00000000000 --- a/doc/next/6-stdlib/99-minor/reflect/66056.md +++ /dev/null @@ -1,4 +0,0 @@ -The new methods [Value.Seq] and [Value.Seq2] return sequences that iterate over the value -as though it were used in a for/range loop. -The new methods [Type.CanSeq] and [Type.CanSeq2] report whether calling -[Value.Seq] and [Value.Seq2], respectively, will succeed without panicking. diff --git a/doc/next/6-stdlib/99-minor/runtime/debug/42888.md b/doc/next/6-stdlib/99-minor/runtime/debug/42888.md deleted file mode 100644 index f10753d25c2..00000000000 --- a/doc/next/6-stdlib/99-minor/runtime/debug/42888.md +++ /dev/null @@ -1,5 +0,0 @@ -The [SetCrashOutput] function allows the user to specify an alternate -file to which the runtime should write its fatal crash report. -It may be used to construct an automated reporting mechanism for all -unexpected crashes, not just those in goroutines that explicitly use -`recover`. diff --git a/doc/next/6-stdlib/99-minor/runtime/debug/67182.md b/doc/next/6-stdlib/99-minor/runtime/debug/67182.md deleted file mode 100644 index d83864a3dbb..00000000000 --- a/doc/next/6-stdlib/99-minor/runtime/debug/67182.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/doc/next/6-stdlib/99-minor/runtime/pprof/43669.md b/doc/next/6-stdlib/99-minor/runtime/pprof/43669.md deleted file mode 100644 index 119308b46a2..00000000000 --- a/doc/next/6-stdlib/99-minor/runtime/pprof/43669.md +++ /dev/null @@ -1,2 +0,0 @@ -The maximum stack depth for alloc, mutex, block, threadcreate and goroutine -profiles has been raised from 32 to 128 frames. diff --git a/doc/next/6-stdlib/99-minor/runtime/trace/65319.md b/doc/next/6-stdlib/99-minor/runtime/trace/65319.md deleted file mode 100644 index b180368e00b..00000000000 --- a/doc/next/6-stdlib/99-minor/runtime/trace/65319.md +++ /dev/null @@ -1,4 +0,0 @@ - -The runtime now explicitly flushes trace data when a program crashes due to an -uncaught panic. This means that more complete trace data will be available in a -trace if the program crashes while tracing is active. diff --git a/doc/next/6-stdlib/99-minor/slices/53987.md b/doc/next/6-stdlib/99-minor/slices/53987.md deleted file mode 100644 index 02d77cd11df..00000000000 --- a/doc/next/6-stdlib/99-minor/slices/53987.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/6-stdlib/99-minor/slices/61899.md b/doc/next/6-stdlib/99-minor/slices/61899.md deleted file mode 100644 index 02d77cd11df..00000000000 --- a/doc/next/6-stdlib/99-minor/slices/61899.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/6-stdlib/99-minor/slices/65238.md b/doc/next/6-stdlib/99-minor/slices/65238.md deleted file mode 100644 index 34ef66a2dd1..00000000000 --- a/doc/next/6-stdlib/99-minor/slices/65238.md +++ /dev/null @@ -1,2 +0,0 @@ -The [Repeat] function returns a new slice that repeats the -provided slice the given number of times. diff --git a/doc/next/6-stdlib/99-minor/structs/66408.md b/doc/next/6-stdlib/99-minor/structs/66408.md deleted file mode 100644 index 810a09e3ca8..00000000000 --- a/doc/next/6-stdlib/99-minor/structs/66408.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/6-stdlib/99-minor/sync/61696.md b/doc/next/6-stdlib/99-minor/sync/61696.md deleted file mode 100644 index 59584301f3d..00000000000 --- a/doc/next/6-stdlib/99-minor/sync/61696.md +++ /dev/null @@ -1,2 +0,0 @@ -The [Map.Clear] method deletes all the entries, resulting in -an empty [Map]. It is analogous to `clear`. diff --git a/doc/next/6-stdlib/99-minor/sync/atomic/61395.md b/doc/next/6-stdlib/99-minor/sync/atomic/61395.md deleted file mode 100644 index 8aea9d6bf3a..00000000000 --- a/doc/next/6-stdlib/99-minor/sync/atomic/61395.md +++ /dev/null @@ -1,3 +0,0 @@ - -The new [And] and [Or] operators apply a bitwise `AND` or `OR` to -the given input, returning the old value. diff --git a/doc/next/6-stdlib/99-minor/syscall/62254.md b/doc/next/6-stdlib/99-minor/syscall/62254.md deleted file mode 100644 index 1d463e5ea81..00000000000 --- a/doc/next/6-stdlib/99-minor/syscall/62254.md +++ /dev/null @@ -1 +0,0 @@ -The syscall package now defines [WSAENOPROTOOPT] on Windows. diff --git a/doc/next/6-stdlib/99-minor/syscall/65817.md b/doc/next/6-stdlib/99-minor/syscall/65817.md deleted file mode 100644 index 9a2d12c0741..00000000000 --- a/doc/next/6-stdlib/99-minor/syscall/65817.md +++ /dev/null @@ -1 +0,0 @@ -The [GetsockoptInt] function is now supported on Windows. diff --git a/doc/next/6-stdlib/99-minor/testing/fstest/63675.md b/doc/next/6-stdlib/99-minor/testing/fstest/63675.md deleted file mode 100644 index cea9ae3dc2e..00000000000 --- a/doc/next/6-stdlib/99-minor/testing/fstest/63675.md +++ /dev/null @@ -1,3 +0,0 @@ -[TestFS] now returns a structured error that can be unwrapped -(via method `Unwrap() []error`). This allows inspecting errors -using [errors.Is] or [errors.As]. diff --git a/doc/next/6-stdlib/99-minor/text/template/57646.md b/doc/next/6-stdlib/99-minor/text/template/57646.md deleted file mode 100644 index 5b8f031432d..00000000000 --- a/doc/next/6-stdlib/99-minor/text/template/57646.md +++ /dev/null @@ -1 +0,0 @@ -Templates now support the new "else with" action, which reduces template complexity in some use cases. diff --git a/doc/next/6-stdlib/99-minor/time/67470.md b/doc/next/6-stdlib/99-minor/time/67470.md deleted file mode 100644 index 8cfcc0aa9da..00000000000 --- a/doc/next/6-stdlib/99-minor/time/67470.md +++ /dev/null @@ -1,2 +0,0 @@ -[Parse] and [ParseInLocation] now return an error if the time zone -offset is out of range. diff --git a/doc/next/6-stdlib/99-minor/unicode/utf16/44940.md b/doc/next/6-stdlib/99-minor/unicode/utf16/44940.md deleted file mode 100644 index e0667845883..00000000000 --- a/doc/next/6-stdlib/99-minor/unicode/utf16/44940.md +++ /dev/null @@ -1,3 +0,0 @@ -The [RuneLen] function returns the number of 16-bit words in -the UTF-16 encoding of the rune. It returns -1 if the rune -is not a valid value to encode in UTF-16. diff --git a/doc/next/6-stdlib/99-minor/unique/62483.md b/doc/next/6-stdlib/99-minor/unique/62483.md deleted file mode 100644 index d281ab290e3..00000000000 --- a/doc/next/6-stdlib/99-minor/unique/62483.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/next/7-ports.md b/doc/next/7-ports.md deleted file mode 100644 index 51e0ac7441a..00000000000 --- a/doc/next/7-ports.md +++ /dev/null @@ -1,38 +0,0 @@ -## Ports {#ports} - -### Darwin {#darwin} - - -As [announced](go1.22#darwin) in the Go 1.22 release notes, -Go 1.23 requires macOS 11 Big Sur or later; -support for previous versions has been discontinued. - -### Linux {#linux} - - -Go 1.23 is the last release that requires Linux kernel version 2.6.32 or later. Go 1.24 will require Linux kernel version 3.17 or later, with an exception that systems running 3.10 or later will continue to be supported if the kernel has been patched to support the getrandom system call. - -### OpenBSD {#openbsd} - - -Go 1.23 adds experimental support for OpenBSD on 64-bit RISC-V (`GOOS=openbsd`, `GOARCH=riscv64`). - -### ARM64 {#arm64} - - -Go 1.23 introduces a new `GOARM64` environment variable, which specifies the minimum target version of the ARM64 architecture at compile time. Allowed values are `v8.{0-9}` and `v9.{0-5}`. This may be followed by an option specifying extensions implemented by target hardware. Valid options are `,lse` and `,crypto`. - -The `GOARM64` environment variable defaults to `v8.0`. - -### RISC-V {#riscv} - - -Go 1.23 introduces a new `GORISCV64` environment variable, which selects the [RISC-V user-mode application profile](https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc) for which to compile. Allowed values are `rva20u64` and `rva22u64`. - -The `GORISCV64` environment variable defaults to `rva20u64`. - -### Wasm {#wasm} - - -The `go_wasip1_wasm_exec` script in `GOROOT/misc/wasm` has dropped support -for versions of `wasmtime` < 14.0.0. diff --git a/doc/next/9-todo.md b/doc/next/9-todo.md deleted file mode 100644 index 424780cd7d8..00000000000 --- a/doc/next/9-todo.md +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go index b1ec4e02785..500da717ff3 100644 --- a/src/cmd/addr2line/main.go +++ b/src/cmd/addr2line/main.go @@ -28,7 +28,7 @@ import ( "strings" "cmd/internal/objfile" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func printUsage(w *os.File) { @@ -46,7 +46,7 @@ func usage() { func main() { log.SetFlags(0) log.SetPrefix("addr2line: ") - telemetry.OpenCounters() + counter.Open() // pprof expects this behavior when checking for addr2line if len(os.Args) > 1 && os.Args[1] == "--help" { @@ -56,8 +56,8 @@ func main() { flag.Usage = usage flag.Parse() - telemetry.Inc("addr2line/invocations") - telemetry.CountFlags("addr2line/flag:", *flag.CommandLine) + counter.Inc("addr2line/invocations") + counter.CountFlags("addr2line/flag:", *flag.CommandLine) if flag.NArg() != 1 { usage() } diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s index ecad08b37aa..f12cdaf921f 100644 --- a/src/cmd/asm/internal/asm/testdata/arm64.s +++ b/src/cmd/asm/internal/asm/testdata/arm64.s @@ -961,7 +961,11 @@ again: CASPD (R2, R3), (R2), (R8, R9) // 487c2248 // RET - RET + RET // c0035fd6 + RET R0 // 00005fd6 + RET R6 // c0005fd6 + RET R27 // 60035fd6 + RET R30 // c0035fd6 RET foo(SB) // B/BL/B.cond cases, and canonical names JMP, CALL. diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index ca4e25d047c..9fdb7c49a2b 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -20,20 +20,20 @@ import ( "cmd/internal/bio" "cmd/internal/obj" "cmd/internal/objabi" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func main() { log.SetFlags(0) log.SetPrefix("asm: ") - telemetry.OpenCounters() + counter.Open() buildcfg.Check() GOARCH := buildcfg.GOARCH flags.Parse() - telemetry.Inc("asm/invocations") - telemetry.CountFlags("asm/flag:", *flag.CommandLine) + counter.Inc("asm/invocations") + counter.CountFlags("asm/flag:", *flag.CommandLine) architecture := arch.Set(GOARCH, *flags.Shared || *flags.Dynlink) if architecture == nil { diff --git a/src/cmd/buildid/buildid.go b/src/cmd/buildid/buildid.go index a008122a0aa..a16b96f677a 100644 --- a/src/cmd/buildid/buildid.go +++ b/src/cmd/buildid/buildid.go @@ -12,7 +12,7 @@ import ( "strings" "cmd/internal/buildid" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func usage() { @@ -26,11 +26,11 @@ var wflag = flag.Bool("w", false, "write build ID") func main() { log.SetPrefix("buildid: ") log.SetFlags(0) - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("buildid/invocations") - telemetry.CountFlags("buildid/flag:", *flag.CommandLine) + counter.Inc("buildid/invocations") + counter.CountFlags("buildid/flag:", *flag.CommandLine) if flag.NArg() != 1 { usage() } diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index b596477b0a6..6c23e59adf1 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -1601,8 +1601,8 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr { break } if r.Context == ctxCall2 { - if r.Name.Go == "_CMalloc" { - error_(r.Pos(), "no two-result form for C.malloc") + if builtinDefs[r.Name.Go] != "" { + error_(r.Pos(), "no two-result form for C.%s", r.Name.Go) break } // Invent new Name for the two-result function. diff --git a/src/cmd/cgo/internal/testerrors/errors_test.go b/src/cmd/cgo/internal/testerrors/errors_test.go index 07556007a84..eddfb6583b9 100644 --- a/src/cmd/cgo/internal/testerrors/errors_test.go +++ b/src/cmd/cgo/internal/testerrors/errors_test.go @@ -127,6 +127,7 @@ func TestReportsTypeErrors(t *testing.T) { "issue33061.go", "issue50710.go", "issue67517.go", + "issue67707.go", } { check(t, file) } diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue67707.go b/src/cmd/cgo/internal/testerrors/testdata/issue67707.go new file mode 100644 index 00000000000..4f80de165e3 --- /dev/null +++ b/src/cmd/cgo/internal/testerrors/testdata/issue67707.go @@ -0,0 +1,15 @@ +// 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 p + +import "C" + +func F() *C.char { + s, err := C.CString("hi") // ERROR HERE: no two-result form + if err != nil { + println(err) + } + return s +} diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go index 4900ada1820..85dfd31123b 100644 --- a/src/cmd/cgo/internal/testplugin/plugin_test.go +++ b/src/cmd/cgo/internal/testplugin/plugin_test.go @@ -414,3 +414,11 @@ func TestTextSectionSplit(t *testing.T) { t.Errorf("runtime.text.1 not found, text section not split?") } } + +func TestIssue67976(t *testing.T) { + // Issue 67976: build failure with loading a dynimport variable (the runtime/pprof + // package does this on darwin) in a plugin on darwin/amd64. + // The test program uses runtime/pprof in a plugin. + globalSkip(t) + goCmd(t, "build", "-buildmode=plugin", "-o", "issue67976.so", "./issue67976/plugin.go") +} diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go new file mode 100644 index 00000000000..502ecc5c475 --- /dev/null +++ b/src/cmd/cgo/internal/testplugin/testdata/issue67976/plugin.go @@ -0,0 +1,16 @@ +// 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 + +import ( + "io" + "runtime/pprof" +) + +func main() {} + +func Start() { + pprof.StartCPUProfile(io.Discard) +} diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index bf879be814f..5699cc55be0 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -28,7 +28,7 @@ import ( "cmd/internal/edit" "cmd/internal/notsha256" "cmd/internal/objabi" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) // A Package collects information about the package we're going to write. @@ -258,11 +258,11 @@ var goarch, goos, gomips, gomips64 string var gccBaseCmd []string func main() { - telemetry.OpenCounters() + counter.Open() objabi.AddVersionFlag() // -V objabi.Flagparse(usage) - telemetry.Inc("cgo/invocations") - telemetry.CountFlags("cgo/flag:", *flag.CommandLine) + counter.Inc("cgo/invocations") + counter.CountFlags("cgo/flag:", *flag.CommandLine) if *gccgoDefineCgoIncomplete { if !*gccgo { @@ -343,6 +343,21 @@ func main() { p.addToFlag("LDFLAGS", args) } + // For backward compatibility for Bazel, record CGO_LDFLAGS + // from the environment for external linking. + // This should not happen with cmd/go, which removes CGO_LDFLAGS + // from the environment when invoking cgo. + // This can be removed when we no longer need to support + // older versions of Bazel. See issue #66456 and + // https://github.com/bazelbuild/rules_go/issues/3979. + if envFlags := os.Getenv("CGO_LDFLAGS"); envFlags != "" { + args, err := splitQuoted(envFlags) + if err != nil { + fatalf("bad CGO_LDFLAGS: %q (%s)", envFlags, err) + } + p.addToFlag("LDFLAGS", args) + } + // Need a unique prefix for the global C symbols that // we use to coordinate between gcc and ourselves. // We already put _cgo_ at the beginning, so the main diff --git a/src/cmd/compile/README.md b/src/cmd/compile/README.md index 3fc7ca6ec66..cffb4e7a80f 100644 --- a/src/cmd/compile/README.md +++ b/src/cmd/compile/README.md @@ -63,7 +63,6 @@ Unified IR is also involved in import/export of packages and inlining. ### 4. Middle end -* `cmd/compile/internal/deadcode` (dead code elimination) * `cmd/compile/internal/inline` (function call inlining) * `cmd/compile/internal/devirtualize` (devirtualization of known interface method calls) * `cmd/compile/internal/escape` (escape analysis) @@ -72,6 +71,8 @@ Several optimization passes are performed on the IR representation: dead code elimination, (early) devirtualization, function call inlining, and escape analysis. +The early dead code elimination pass is integrated into the unified IR writer phase. + ### 5. Walk * `cmd/compile/internal/walk` (order of evaluation, desugaring) diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go index 8c17c5f27d8..b296f3666cf 100644 --- a/src/cmd/compile/internal/base/flag.go +++ b/src/cmd/compile/internal/base/flag.go @@ -6,7 +6,7 @@ package base import ( "cmd/internal/cov/covcmd" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "encoding/json" "flag" "fmt" @@ -195,7 +195,7 @@ func ParseFlags() { objabi.AddVersionFlag() // -V registerFlags() objabi.Flagparse(usage) - telemetry.CountFlags("compile/flag:", *flag.CommandLine) + counter.CountFlags("compile/flag:", *flag.CommandLine) if gcd := os.Getenv("GOCOMPILEDEBUG"); gcd != "" { // This will only override the flags set in gcd; diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go index 15256186afe..119f06fbc03 100644 --- a/src/cmd/compile/internal/base/print.go +++ b/src/cmd/compile/internal/base/print.go @@ -14,7 +14,7 @@ import ( "strings" "cmd/internal/src" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) // An errorMsg is a queued error message, waiting to be printed. @@ -195,7 +195,7 @@ func Fatalf(format string, args ...interface{}) { FatalfAt(Pos, format, args...) } -var bugStack = telemetry.NewStackCounter("compile/bug", 16) // 16 is arbitrary; used by gopls and crashmonitor +var bugStack = counter.NewStack("compile/bug", 16) // 16 is arbitrary; used by gopls and crashmonitor // FatalfAt reports a fatal error - an internal problem - at pos and exits. // If other errors have already been printed, then FatalfAt just quietly exits. diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 512d8d22e75..36cc253e827 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go @@ -91,6 +91,9 @@ func Info(fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Sc default: continue } + if !ssa.IsVarWantedForDebug(n) { + continue + } apdecls = append(apdecls, n) if n.Type().Kind() == types.TSSA { // Can happen for TypeInt128 types. This only happens for @@ -194,6 +197,9 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir // DWARF-gen. See issue 48573 for more details. debugInfo := fn.DebugInfo.(*ssa.FuncDebug) for _, n := range debugInfo.RegOutputParams { + if !ssa.IsVarWantedForDebug(n) { + continue + } if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() { panic("invalid ir.Name on debugInfo.RegOutputParams list") } diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 3887d4156d6..f68cf4deaf0 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -30,7 +30,7 @@ import ( "cmd/internal/obj" "cmd/internal/objabi" "cmd/internal/src" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "flag" "fmt" "internal/buildcfg" @@ -59,8 +59,8 @@ func handlePanic() { // code, and finally writes the compiled package definition to disk. func Main(archInit func(*ssagen.ArchInfo)) { base.Timer.Start("fe", "init") - telemetry.OpenCounters() - telemetry.Inc("compile/invocations") + counter.Open() + counter.Inc("compile/invocations") defer handlePanic() diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go index 22d6f71329c..6bc23cc5f92 100644 --- a/src/cmd/compile/internal/noder/unified.go +++ b/src/cmd/compile/internal/noder/unified.go @@ -7,6 +7,7 @@ package noder import ( "fmt" "internal/pkgbits" + "internal/types/errors" "io" "runtime" "sort" @@ -403,7 +404,10 @@ func readPackage(pr *pkgReader, importpkg *types.Pkg, localStub bool) { r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic) pkg := r.pkg() - base.Assertf(pkg == importpkg, "have package %q (%p), want package %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) + if pkg != importpkg { + base.ErrorfAt(base.AutogeneratedPos, errors.BadImportPath, "mismatched import path, have %q (%p), want %q (%p)", pkg.Path, pkg, importpkg.Path, importpkg) + base.ErrorExit() + } r.Bool() // TODO(mdempsky): Remove; was "has init" diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index fe8f8f2a351..8fed138a4a1 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1582,6 +1582,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.stmt(stmt.Init) var iface, tagType types2.Type + var tagTypeIsChan bool if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) { iface = w.p.typeOf(guard.X) @@ -1603,6 +1604,7 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { tv := w.p.typeAndValue(tag) tagType = tv.Type tagValue = tv.Value + _, tagTypeIsChan = tagType.Underlying().(*types2.Chan) } else { tagType = types2.Typ[types2.Bool] tagValue = constant.MakeBool(true) @@ -1655,12 +1657,18 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { // have the same type. If there are any case values that can't be // converted to the tag value's type, then convert everything to // `any` instead. - Outer: - for _, clause := range stmt.Body { - for _, cas := range syntax.UnpackListExpr(clause.Cases) { - if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { - tagType = types2.NewInterfaceType(nil, nil) - break Outer + // + // Except that we need to keep comparisons of channel values from + // being wrapped in any(). See issue #67190. + + if !tagTypeIsChan { + Outer: + for _, clause := range stmt.Body { + for _, cas := range syntax.UnpackListExpr(clause.Cases) { + if casType := w.p.typeOf(cas); !types2.AssignableTo(casType, tagType) { + tagType = types2.NewInterfaceType(nil, nil) + break Outer + } } } } @@ -1696,7 +1704,11 @@ func (w *writer) switchStmt(stmt *syntax.SwitchStmt) { w.Sync(pkgbits.SyncExprs) w.Len(len(cases)) for _, cas := range cases { - w.implicitConvExpr(tagType, cas) + typ := tagType + if tagTypeIsChan { + typ = nil + } + w.implicitConvExpr(typ, cas) } } diff --git a/src/cmd/compile/internal/rangefunc/rewrite.go b/src/cmd/compile/internal/rangefunc/rewrite.go index e5a0b9f8af1..ba2eb8d0fdc 100644 --- a/src/cmd/compile/internal/rangefunc/rewrite.go +++ b/src/cmd/compile/internal/rangefunc/rewrite.go @@ -1137,7 +1137,7 @@ func (r *rewriter) setStateAt(index int, stateVal abi.RF_State) *syntax.AssignSt func (r *rewriter) bodyFunc(body []syntax.Stmt, lhs []syntax.Expr, def bool, ftyp *types2.Signature, start, end syntax.Pos) *syntax.FuncLit { // Starting X(bodyFunc); build up bodyFunc first. var params, results []*types2.Var - results = append(results, types2.NewVar(start, nil, "", r.bool.Type())) + results = append(results, types2.NewVar(start, nil, "#r", r.bool.Type())) bodyFunc := &syntax.FuncLit{ // Note: Type is ignored but needs to be non-nil to avoid panic in syntax.Inspect. Type: &syntax.FuncType{}, diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index 4abe5a98924..04025f78829 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -600,7 +600,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func( state.vars = state.vars[:0] for i, slot := range f.Names { state.slots = append(state.slots, *slot) - if ir.IsSynthetic(slot.N) { + if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) { continue } @@ -620,7 +620,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func( for _, v := range b.Values { if v.Op == OpVarDef { n := v.Aux.(*ir.Name) - if ir.IsSynthetic(n) { + if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) { continue } @@ -665,7 +665,7 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func( state.initializeCache(f, len(state.varParts), len(state.slots)) for i, slot := range f.Names { - if ir.IsSynthetic(slot.N) { + if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) { continue } for _, value := range f.NamedValues[*slot] { @@ -1087,7 +1087,7 @@ func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) switch { case v.Op == OpVarDef: n := v.Aux.(*ir.Name) - if ir.IsSynthetic(n) { + if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) { break } @@ -1835,6 +1835,9 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta // will be sorted out elsewhere continue } + if !IsVarWantedForDebug(inp.Name) { + continue + } addVarSlot(inp.Name, inp.Type) params = append(params, inp) } @@ -1855,6 +1858,9 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta // will be sorted out elsewhere continue } + if !IsVarWantedForDebug(inp.Name) { + continue + } sl := rval.Slots[pidx] n := rval.Vars[pidx] @@ -1948,3 +1954,19 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta pidx++ } } + +// IsVarWantedForDebug returns true if the debug info for the node should +// be generated. +// For example, internal variables for range-over-func loops have little +// value to users, so we don't generate debug info for them. +func IsVarWantedForDebug(n ir.Node) bool { + name := n.Sym().Name + if len(name) > 0 && name[0] == '&' { + name = name[1:] + } + if len(name) > 0 && name[0] == '#' { + // #yield is used by delve. + return strings.HasPrefix(name, "#yield") + } + return true +} diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index aeec2b3768c..5bc03083628 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -1287,6 +1287,11 @@ func areAdjacentOffsets(off1, off2, size int64) bool { // depth limits recursion depth. In AMD64.rules 3 is used as limit, // because it catches same amount of cases as 4. func zeroUpper32Bits(x *Value, depth int) bool { + if x.Type.IsSigned() && x.Type.Size() < 8 { + // If the value is signed, it might get re-sign-extended + // during spill and restore. See issue 68227. + return false + } switch x.Op { case OpAMD64MOVLconst, OpAMD64MOVLload, OpAMD64MOVLQZX, OpAMD64MOVLloadidx1, OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVBload, OpAMD64MOVBloadidx1, @@ -1305,7 +1310,7 @@ func zeroUpper32Bits(x *Value, depth int) bool { case OpArg: // note: but not ArgIntReg // amd64 always loads args from the stack unsigned. // most other architectures load them sign/zero extended based on the type. - return x.Type.Size() == 4 && (x.Type.IsUnsigned() || x.Block.Func.Config.arch == "amd64") + return x.Type.Size() == 4 && x.Block.Func.Config.arch == "amd64" case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. @@ -1325,11 +1330,14 @@ func zeroUpper32Bits(x *Value, depth int) bool { // zeroUpper48Bits is similar to zeroUpper32Bits, but for upper 48 bits. func zeroUpper48Bits(x *Value, depth int) bool { + if x.Type.IsSigned() && x.Type.Size() < 8 { + return false + } switch x.Op { case OpAMD64MOVWQZX, OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVWloadidx2: return true case OpArg: // note: but not ArgIntReg - return x.Type.Size() == 2 && (x.Type.IsUnsigned() || x.Block.Func.Config.arch == "amd64") + return x.Type.Size() == 2 && x.Block.Func.Config.arch == "amd64" case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. @@ -1349,11 +1357,14 @@ func zeroUpper48Bits(x *Value, depth int) bool { // zeroUpper56Bits is similar to zeroUpper32Bits, but for upper 56 bits. func zeroUpper56Bits(x *Value, depth int) bool { + if x.Type.IsSigned() && x.Type.Size() < 8 { + return false + } switch x.Op { case OpAMD64MOVBQZX, OpAMD64MOVBload, OpAMD64MOVBloadidx1: return true case OpArg: // note: but not ArgIntReg - return x.Type.Size() == 1 && (x.Type.IsUnsigned() || x.Block.Func.Config.arch == "amd64") + return x.Type.Size() == 1 && x.Block.Func.Config.arch == "amd64" case OpPhi, OpSelect0, OpSelect1: // Phis can use each-other as an arguments, instead of tracking visited values, // just limit recursion depth. diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go index 7317ed1fec4..91c0a27faf0 100644 --- a/src/cmd/compile/internal/staticinit/sched.go +++ b/src/cmd/compile/internal/staticinit/sched.go @@ -107,6 +107,20 @@ func (s *Schedule) tryStaticInit(n ir.Node) bool { case ir.OAS: n := n.(*ir.AssignStmt) lhs, rhs = []ir.Node{n.X}, n.Y + case ir.OAS2: + // Usually OAS2 has been rewritten to separate OASes by types2. + // What's left here is "var a, b = tmp1, tmp2" as a result from rewriting + // "var a, b = f()" that needs type conversion, which is not static. + n := n.(*ir.AssignListStmt) + for _, rhs := range n.Rhs { + for rhs.Op() == ir.OCONVNOP { + rhs = rhs.(*ir.ConvExpr).X + } + if name, ok := rhs.(*ir.Name); !ok || !name.AutoTemp() { + base.FatalfAt(n.Pos(), "unexpected rhs, not an autotmp: %+v", rhs) + } + } + return false case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV: n := n.(*ir.AssignListStmt) if len(n.Lhs) < 2 || len(n.Rhs) != 1 { diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index b4b9ecd8368..ec849e31540 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -663,14 +663,14 @@ func RewriteMultiValueCall(n ir.InitNode, call ir.Node) { switch n := n.(type) { default: - base.Fatalf("rewriteMultiValueCall %+v", n.Op()) + base.Fatalf("RewriteMultiValueCall %+v", n.Op()) case *ir.CallExpr: n.Args = list case *ir.ReturnStmt: n.Results = list case *ir.AssignListStmt: if n.Op() != ir.OAS2FUNC { - base.Fatalf("rewriteMultiValueCall: invalid op %v", n.Op()) + base.Fatalf("RewriteMultiValueCall: invalid op %v", n.Op()) } as.SetOp(ir.OAS2FUNC) n.SetOp(ir.OAS2) diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go index b4da3c0b915..20e3f52facd 100644 --- a/src/cmd/compile/internal/types2/issues_test.go +++ b/src/cmd/compile/internal/types2/issues_test.go @@ -1092,3 +1092,32 @@ func _() { conf := Config{GoVersion: "go1.17"} mustTypecheck(src, &conf, nil) } + +func TestIssue68334(t *testing.T) { + const src = ` +package p + +func f(x int) { + for i, j := range x { + _, _ = i, j + } + var a, b int + for a, b = range x { + _, _ = a, b + } +} +` + + got := "" + conf := Config{ + GoVersion: "go1.21", // #68334 requires GoVersion <= 1.21 + Error: func(err error) { got += err.Error() + "\n" }, // #68334 requires Error != nil + } + typecheck(src, &conf, nil) // do not crash + + want := "p:5:20: cannot range over x (variable of type int): requires go1.22 or later\n" + + "p:9:19: cannot range over x (variable of type int): requires go1.22 or later\n" + if got != want { + t.Errorf("got: %s want: %s", got, want) + } +} diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index 58783f47c3c..b598a4f0686 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -920,14 +920,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s // initialize lhs iteration variable, if any typ := rhs[i] - if typ == nil { + if typ == nil || typ == Typ[Invalid] { + // typ == Typ[Invalid] can happen if allowVersion fails. obj.typ = Typ[Invalid] obj.used = true // don't complain about unused variable continue } if rangeOverInt { - assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) check.initVar(obj, &x, "range clause") } else { var y operand @@ -957,12 +958,12 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s // assign to lhs iteration variable, if any typ := rhs[i] - if typ == nil { + if typ == nil || typ == Typ[Invalid] { continue } if rangeOverInt { - assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) check.assignVar(lhs, nil, &x, "range clause") // If the assignment succeeded, if x was untyped before, it now // has a type inferred via the assignment. It must be an integer. diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index 9ad064906fb..e22981e1ad0 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -25,8 +25,8 @@ type TypeParam struct { } // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named -// or Signature type by calling SetTypeParams. Setting a type parameter on more -// than one type will result in a panic. +// type by calling SetTypeParams. Setting a type parameter on more than one type +// will result in a panic. // // The constraint argument can be nil, and set later via SetConstraint. If the // constraint is non-nil, it must be fully defined. diff --git a/src/cmd/covdata/covdata.go b/src/cmd/covdata/covdata.go index 48d7b9ed089..122ad28b5ca 100644 --- a/src/cmd/covdata/covdata.go +++ b/src/cmd/covdata/covdata.go @@ -7,7 +7,7 @@ package main import ( "cmd/internal/cov" "cmd/internal/pkgpattern" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "flag" "fmt" "os" @@ -109,7 +109,7 @@ const ( ) func main() { - telemetry.OpenCounters() + counter.Open() // First argument should be mode/subcommand. if len(os.Args) < 2 { @@ -146,8 +146,8 @@ func main() { op.Usage("") } flag.Parse() - telemetry.Inc("covdata/invocations") - telemetry.CountFlags("covdata/flag:", *flag.CommandLine) + counter.Inc("covdata/invocations") + counter.CountFlags("covdata/flag:", *flag.CommandLine) // Mode-independent flag setup dbgtrace(1, "starting mode-independent setup") diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go index 47eebaadd3a..d8bb989bcce 100644 --- a/src/cmd/cover/cover.go +++ b/src/cmd/cover/cover.go @@ -26,7 +26,7 @@ import ( "cmd/internal/edit" "cmd/internal/objabi" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) const usageMessage = "" + @@ -87,13 +87,13 @@ const ( ) func main() { - telemetry.OpenCounters() + counter.Open() objabi.AddVersionFlag() flag.Usage = usage objabi.Flagparse(usage) - telemetry.Inc("cover/invocations") - telemetry.CountFlags("cover/flag:", *flag.CommandLine) + counter.Inc("cover/invocations") + counter.CountFlags("cover/flag:", *flag.CommandLine) // Usage information when no arguments. if flag.NFlag() == 0 && flag.NArg() == 0 { diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 62f96932102..9ca8fc539c4 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -53,6 +53,7 @@ var bootstrapDirs = []string{ "cmd/internal/src", "cmd/internal/sys", "cmd/internal/telemetry", + "cmd/internal/telemetry/counter", "cmd/link", "cmd/link/internal/...", "compress/flate", diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index d7cbadf7b17..0ffcabe4164 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -711,19 +711,6 @@ func (t *tester) registerTests() { }) } - // GOEXPERIMENT=rangefunc tests - if !t.compileOnly { - for _, pkg := range []string{"iter", "slices", "maps"} { - t.registerTest("GOEXPERIMENT=rangefunc", - &goTest{ - variant: pkg, - short: t.short, - env: []string{"GOEXPERIMENT=rangefunc"}, - pkg: pkg, - }) - } - } - // GODEBUG=gcstoptheworld=2 tests. We only run these in long-test // mode (with GO_TEST_SHORT=0) because this is just testing a // non-critical debug setting. diff --git a/src/cmd/distpack/pack.go b/src/cmd/distpack/pack.go index 9ad33ee589c..55252493669 100644 --- a/src/cmd/distpack/pack.go +++ b/src/cmd/distpack/pack.go @@ -45,7 +45,7 @@ import ( "strings" "time" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func usage() { @@ -69,11 +69,11 @@ var ( func main() { log.SetPrefix("distpack: ") log.SetFlags(0) - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("distpack/invocations") - telemetry.CountFlags("distpack/flag:", *flag.CommandLine) + counter.Inc("distpack/invocations") + counter.CountFlags("distpack/flag:", *flag.CommandLine) if flag.NArg() != 0 { usage() } diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go index 4dbddcb79fc..502de097f50 100644 --- a/src/cmd/doc/main.go +++ b/src/cmd/doc/main.go @@ -55,7 +55,7 @@ import ( "path/filepath" "strings" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) var ( @@ -87,7 +87,7 @@ func usage() { func main() { log.SetFlags(0) log.SetPrefix("doc: ") - telemetry.OpenCounters() + counter.Open() dirsInit() err := do(os.Stdout, flag.CommandLine, os.Args[1:]) if err != nil { @@ -108,8 +108,8 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol") flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol") flagSet.Parse(args) - telemetry.Inc("doc/invocations") - telemetry.CountFlags("doc/flag:", *flag.CommandLine) + counter.Inc("doc/invocations") + counter.CountFlags("doc/flag:", *flag.CommandLine) if chdir != "" { if err := os.Chdir(chdir); err != nil { return err diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go index d915ece4ceb..6f1ff120da1 100644 --- a/src/cmd/fix/main.go +++ b/src/cmd/fix/main.go @@ -22,7 +22,7 @@ import ( "sort" "strings" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) var ( @@ -65,11 +65,11 @@ func usage() { } func main() { - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("fix/invocations") - telemetry.CountFlags("fix/flag:", *flag.CommandLine) + counter.Inc("fix/invocations") + counter.CountFlags("fix/flag:", *flag.CommandLine) if !version.IsValid(*goVersion) { report(fmt.Errorf("invalid -go=%s", *goVersion)) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 559ffd1ccdc..f568996fd0b 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -6,10 +6,10 @@ require ( github.com/google/pprof v0.0.0-20240528025155-186aa0362fba golang.org/x/arch v0.8.0 golang.org/x/build v0.0.0-20240603162849-5dfbda438323 - golang.org/x/mod v0.18.0 + golang.org/x/mod v0.19.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.21.0 - golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb + golang.org/x/sys v0.22.0 + golang.org/x/telemetry v0.0.0-20240712210958-268b4a8ec2d7 golang.org/x/term v0.20.0 golang.org/x/tools v0.22.1-0.20240618181713-f2d2ebe43e72 ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 8f9517bc624..f822fa372ad 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -10,14 +10,14 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/build v0.0.0-20240603162849-5dfbda438323 h1:XHj9DzsjpryRW9MnyZq85mQ1dRpSxVC+2TLcMzVZNMo= golang.org/x/build v0.0.0-20240603162849-5dfbda438323/go.mod h1:yz9anu0Z63yrVrqnoOxoJuyBRDwtGUoOFJwtfvs+D+U= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb h1:0Ge50tvTqbHEyuQDgCYypgL2afqNjRNdl4GHPJuN9QY= -golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb/go.mod h1:n38mvGdgc4dA684EC4NwQwoPKSw4jyKw8/DgZHDA1Dk= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240712210958-268b4a8ec2d7 h1:nU8/tAV/21mkPrCjACUeSibjhynTovgRMXc32+Y1Aec= +golang.org/x/telemetry v0.0.0-20240712210958-268b4a8ec2d7/go.mod h1:amNmu/SBSm2GAF3X+9U2C0epLocdh+r5Z+7oMYO5cLM= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 972f9e111f6..75e6d65d0c9 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -276,7 +276,7 @@ // // Usage: // -// go clean [clean flags] [build flags] [packages] +// go clean [-i] [-r] [-cache] [-testcache] [-modcache] [-fuzzcache] [build flags] [packages] // // Clean removes object files from package source directories. // The go command builds most objects in a temporary directory, @@ -1689,8 +1689,7 @@ // // The -r flag searches recursively for modules in the argument // directories, and the use command operates as if each of the directories -// were specified as arguments: namely, use directives will be added for -// directories that exist, and removed for directories that do not exist. +// were specified as arguments. // // See the workspaces reference at https://go.dev/ref/mod#workspaces // for more information. @@ -1994,6 +1993,9 @@ // // //go:build // +// Build constraints can also be used to downgrade the language version +// used to compile a file. +// // Constraints may appear in any kind of source file (not just Go), but // they must appear near the top of the file, preceded // only by blank lines and other comments. These rules mean that in Go @@ -2116,6 +2118,10 @@ // with a "// +build" prefix. The gofmt command will add an equivalent //go:build // constraint when encountering the older syntax. // +// In modules with a Go version of 1.21 or later, if a file's build constraint +// has a term for a Go major release, the language version used when compiling +// the file will be the minimum version implied by the build constraint. +// // # Build modes // // The 'go build' and 'go install' commands take a -buildmode argument which diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go index de2ef9dcb95..3b5924fe13d 100644 --- a/src/cmd/go/internal/clean/clean.go +++ b/src/cmd/go/internal/clean/clean.go @@ -28,7 +28,7 @@ import ( ) var CmdClean = &base.Command{ - UsageLine: "go clean [clean flags] [build flags] [packages]", + UsageLine: "go clean [-i] [-r] [-cache] [-testcache] [-modcache] [-fuzzcache] [build flags] [packages]", Short: "remove object files and cached files", Long: ` Clean removes object files from package source directories. diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go index 98382f2423a..4f2607fef2b 100644 --- a/src/cmd/go/internal/help/help.go +++ b/src/cmd/go/internal/help/help.go @@ -16,10 +16,10 @@ import ( "unicode/utf8" "cmd/go/internal/base" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) -var counterErrorsHelpUnknownTopic = telemetry.NewCounter("go/errors:help-unknown-topic") +var counterErrorsHelpUnknownTopic = counter.New("go/errors:help-unknown-topic") // Help implements the 'help' command. func Help(w io.Writer, args []string) { diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index 791013dbfea..29c0a56321a 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -835,6 +835,9 @@ line comment that begins //go:build +Build constraints can also be used to downgrade the language version +used to compile a file. + Constraints may appear in any kind of source file (not just Go), but they must appear near the top of the file, preceded only by blank lines and other comments. These rules mean that in Go @@ -954,5 +957,9 @@ only when building the package for 32-bit x86. Go versions 1.16 and earlier used a different syntax for build constraints, with a "// +build" prefix. The gofmt command will add an equivalent //go:build constraint when encountering the older syntax. + +In modules with a Go version of 1.21 or later, if a file's build constraint +has a term for a Go major release, the language version used when compiling +the file will be the minimum version implied by the build constraint. `, } diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go index 75b28b9bbc7..02c4833a1b8 100644 --- a/src/cmd/go/internal/modfetch/cache.go +++ b/src/cmd/go/internal/modfetch/cache.go @@ -26,7 +26,7 @@ import ( "cmd/go/internal/modfetch/codehost" "cmd/go/internal/par" "cmd/go/internal/robustio" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "golang.org/x/mod/module" "golang.org/x/mod/semver" @@ -779,7 +779,7 @@ var ( statCacheOnce sync.Once statCacheErr error - counterErrorsGOMODCACHEEntryRelative = telemetry.NewCounter("go/errors:gomodcache-entry-relative") + counterErrorsGOMODCACHEEntryRelative = counter.New("go/errors:gomodcache-entry-relative") ) // checkCacheDir checks if the directory specified by GOMODCACHE exists. An diff --git a/src/cmd/go/internal/telemetrystats/telemetrystats.go b/src/cmd/go/internal/telemetrystats/telemetrystats.go index 610c4a22e85..950453fa951 100644 --- a/src/cmd/go/internal/telemetrystats/telemetrystats.go +++ b/src/cmd/go/internal/telemetrystats/telemetrystats.go @@ -10,7 +10,7 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cfg" "cmd/go/internal/modload" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func Increment() { @@ -22,30 +22,30 @@ func Increment() { // the command is running in. func incrementConfig() { if !modload.WillBeEnabled() { - telemetry.Inc("go/mode:gopath") + counter.Inc("go/mode:gopath") } else if workfile := modload.FindGoWork(base.Cwd()); workfile != "" { - telemetry.Inc("go/mode:workspace") + counter.Inc("go/mode:workspace") } else { - telemetry.Inc("go/mode:module") + counter.Inc("go/mode:module") } - telemetry.Inc("go/platform/target/goos:" + cfg.Goos) - telemetry.Inc("go/platform/target/goarch:" + cfg.Goarch) + counter.Inc("go/platform/target/goos:" + cfg.Goos) + counter.Inc("go/platform/target/goarch:" + cfg.Goarch) switch cfg.Goarch { case "386": - telemetry.Inc("go/platform/target/go386:" + cfg.GO386) + counter.Inc("go/platform/target/go386:" + cfg.GO386) case "amd64": - telemetry.Inc("go/platform/target/goamd64:" + cfg.GOAMD64) + counter.Inc("go/platform/target/goamd64:" + cfg.GOAMD64) case "arm": - telemetry.Inc("go/platform/target/goarm:" + cfg.GOARM) + counter.Inc("go/platform/target/goarm:" + cfg.GOARM) case "arm64": - telemetry.Inc("go/platform/target/goarm64:" + cfg.GOARM64) + counter.Inc("go/platform/target/goarm64:" + cfg.GOARM64) case "mips": - telemetry.Inc("go/platform/target/gomips:" + cfg.GOMIPS) + counter.Inc("go/platform/target/gomips:" + cfg.GOMIPS) case "ppc64": - telemetry.Inc("go/platform/target/goppc64:" + cfg.GOPPC64) + counter.Inc("go/platform/target/goppc64:" + cfg.GOPPC64) case "riscv64": - telemetry.Inc("go/platform/target/goriscv64:" + cfg.GORISCV64) + counter.Inc("go/platform/target/goriscv64:" + cfg.GORISCV64) case "wasm": - telemetry.Inc("go/platform/target/gowasm:" + cfg.GOWASM) + counter.Inc("go/platform/target/gowasm:" + cfg.GOWASM) } } diff --git a/src/cmd/go/internal/telemetrystats/version_other.go b/src/cmd/go/internal/telemetrystats/version_other.go index b20294e2237..efce5fcf362 100644 --- a/src/cmd/go/internal/telemetrystats/version_other.go +++ b/src/cmd/go/internal/telemetrystats/version_other.go @@ -6,8 +6,8 @@ package telemetrystats -import "cmd/internal/telemetry" +import "cmd/internal/telemetry/counter" func incrementVersionCounters() { - telemetry.Inc("go/platform:version-not-supported") + counter.Inc("go/platform:version-not-supported") } diff --git a/src/cmd/go/internal/telemetrystats/version_unix.go b/src/cmd/go/internal/telemetrystats/version_unix.go index 08259b7c896..517e7829b68 100644 --- a/src/cmd/go/internal/telemetrystats/version_unix.go +++ b/src/cmd/go/internal/telemetrystats/version_unix.go @@ -12,7 +12,7 @@ import ( "runtime" "strings" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "golang.org/x/sys/unix" ) @@ -29,7 +29,7 @@ func incrementVersionCounters() { var v unix.Utsname err := unix.Uname(&v) if err != nil { - telemetry.Inc(fmt.Sprintf("go/platform/host/%s/version:unknown-uname-error", runtime.GOOS)) + counter.Inc(fmt.Sprintf("go/platform/host/%s/version:unknown-uname-error", runtime.GOOS)) return } major, minor, ok := majorMinor(convert(v.Release[:])) @@ -37,11 +37,11 @@ func incrementVersionCounters() { major, minor, ok = convert(v.Version[:]), convert(v.Release[:]), true } if !ok { - telemetry.Inc(fmt.Sprintf("go/platform/host/%s/version:unknown-bad-format", runtime.GOOS)) + counter.Inc(fmt.Sprintf("go/platform/host/%s/version:unknown-bad-format", runtime.GOOS)) return } - telemetry.Inc(fmt.Sprintf("go/platform/host/%s/major-version:%s", runtime.GOOS, major)) - telemetry.Inc(fmt.Sprintf("go/platform/host/%s/version:%s-%s", runtime.GOOS, major, minor)) + counter.Inc(fmt.Sprintf("go/platform/host/%s/major-version:%s", runtime.GOOS, major)) + counter.Inc(fmt.Sprintf("go/platform/host/%s/version:%s-%s", runtime.GOOS, major, minor)) } func majorMinor(v string) (string, string, bool) { diff --git a/src/cmd/go/internal/telemetrystats/version_windows.go b/src/cmd/go/internal/telemetrystats/version_windows.go index e6a33e00cdc..7de87193c67 100644 --- a/src/cmd/go/internal/telemetrystats/version_windows.go +++ b/src/cmd/go/internal/telemetrystats/version_windows.go @@ -9,14 +9,14 @@ package telemetrystats import ( "fmt" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "golang.org/x/sys/windows" ) func incrementVersionCounters() { v := windows.RtlGetVersion() - telemetry.Inc(fmt.Sprintf("go/platform/host/windows/major-version:%d", v.MajorVersion)) - telemetry.Inc(fmt.Sprintf("go/platform/host/windows/version:%d-%d", v.MajorVersion, v.MinorVersion)) - telemetry.Inc(fmt.Sprintf("go/platform/host/windows/build:%d", v.BuildNumber)) + counter.Inc(fmt.Sprintf("go/platform/host/windows/major-version:%d", v.MajorVersion)) + counter.Inc(fmt.Sprintf("go/platform/host/windows/version:%d-%d", v.MajorVersion, v.MinorVersion)) + counter.Inc(fmt.Sprintf("go/platform/host/windows/build:%d", v.BuildNumber)) } diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go index da219f041cf..77cee564b3d 100644 --- a/src/cmd/go/internal/tool/tool.go +++ b/src/cmd/go/internal/tool/tool.go @@ -6,7 +6,7 @@ package tool import ( - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "context" "encoding/json" "flag" @@ -58,7 +58,7 @@ func init() { func runTool(ctx context.Context, cmd *base.Command, args []string) { if len(args) == 0 { - telemetry.Inc("go/subcommand:tool") + counter.Inc("go/subcommand:tool") listTools() return } @@ -86,17 +86,17 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) { if impersonateDistList(args[2:]) { // If it becomes necessary, we could increment an additional counter to indicate // that we're impersonating dist list if knowing that becomes important? - telemetry.Inc("go/subcommand:tool-dist") + counter.Inc("go/subcommand:tool-dist") return } } - telemetry.Inc("go/subcommand:tool-unknown") + counter.Inc("go/subcommand:tool-unknown") // Emit the usual error for the missing tool. _ = base.Tool(toolName) } else { // Increment a counter for the tool subcommand with the tool name. - telemetry.Inc("go/subcommand:tool-" + toolName) + counter.Inc("go/subcommand:tool-" + toolName) } if toolN { diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go index d4787a844f0..8e93e6c9033 100644 --- a/src/cmd/go/internal/toolchain/select.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -26,7 +26,7 @@ import ( "cmd/go/internal/modload" "cmd/go/internal/run" "cmd/go/internal/work" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "golang.org/x/mod/module" ) @@ -82,7 +82,7 @@ func FilterEnv(env []string) []string { return out } -var counterErrorsInvalidToolchainInFile = telemetry.NewCounter("go/errors:invalid-toolchain-in-file") +var counterErrorsInvalidToolchainInFile = counter.New("go/errors:invalid-toolchain-in-file") // Select invokes a different Go toolchain if directed by // the GOTOOLCHAIN environment variable or the user's configuration @@ -253,7 +253,7 @@ func Select() { Exec(gotoolchain) } -var counterSelectExec = telemetry.NewCounter("go/toolchain/select-exec") +var counterSelectExec = counter.New("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 ba1e6973cf4..37c1bcdcbec 100644 --- a/src/cmd/go/internal/toolchain/switch.go +++ b/src/cmd/go/internal/toolchain/switch.go @@ -16,7 +16,7 @@ import ( "cmd/go/internal/cfg" "cmd/go/internal/gover" "cmd/go/internal/modfetch" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) // A Switcher collects errors to be reported and then decides @@ -104,7 +104,7 @@ func (s *Switcher) Switch(ctx context.Context) { panic("unreachable") } -var counterSwitchExec = telemetry.NewCounter("go/toolchain/switch-exec") +var counterSwitchExec = counter.New("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/buildid.go b/src/cmd/go/internal/work/buildid.go index 889cc6ca508..4ee43e24369 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -19,7 +19,7 @@ import ( "cmd/go/internal/str" "cmd/internal/buildid" "cmd/internal/quoted" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) // Build IDs @@ -406,11 +406,11 @@ func (b *Builder) fileHash(file string) string { } var ( - counterCacheHit = telemetry.NewCounter("go/buildcache/hit") - counterCacheMiss = telemetry.NewCounter("go/buildcache/miss") + counterCacheHit = counter.New("go/buildcache/hit") + counterCacheMiss = counter.New("go/buildcache/miss") onceIncStdlibRecompiled sync.Once - stdlibRecompiled = telemetry.NewCounter("go/buildcache/stdlib-recompiled") + stdlibRecompiled = counter.New("go/buildcache/stdlib-recompiled") ) // useCache tries to satisfy the action a, which has action ID actionHash, diff --git a/src/cmd/go/internal/workcmd/use.go b/src/cmd/go/internal/workcmd/use.go index 55477119d46..0cdbed6b18b 100644 --- a/src/cmd/go/internal/workcmd/use.go +++ b/src/cmd/go/internal/workcmd/use.go @@ -42,8 +42,7 @@ thing that go work use does. The -r flag searches recursively for modules in the argument directories, and the use command operates as if each of the directories -were specified as arguments: namely, use directives will be added for -directories that exist, and removed for directories that do not exist. +were specified as arguments. diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index eb33df1ad4c..1c58232a662 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -45,6 +45,7 @@ import ( "cmd/go/internal/work" "cmd/go/internal/workcmd" "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) func init() { @@ -91,20 +92,20 @@ func init() { var _ = go11tag -var counterErrorsGOPATHEntryRelative = telemetry.NewCounter("go/errors:gopath-entry-relative") +var counterErrorsGOPATHEntryRelative = counter.New("go/errors:gopath-entry-relative") func main() { log.SetFlags(0) - telemetry.MaybeChild() // Run in child mode if this is the telemetry sidecar child process. - telemetry.OpenCounters() // Open the telemetry counter file so counters can be written to it. + telemetry.MaybeChild() // Run in child mode if this is the telemetry sidecar child process. + counter.Open() // Open the telemetry counter file so counters can be written to it. handleChdirFlag() toolchain.Select() telemetry.MaybeParent() // Run the upload process. Opening the counter file is idempotent. flag.Usage = base.Usage flag.Parse() - telemetry.Inc("go/invocations") - telemetry.CountFlags("go/flag:", *flag.CommandLine) + counter.Inc("go/invocations") + counter.CountFlags("go/flag:", *flag.CommandLine) args := flag.Args() if len(args) < 1 { @@ -113,7 +114,7 @@ func main() { cfg.CmdName = args[0] // for error messages if args[0] == "help" { - telemetry.Inc("go/subcommand:" + strings.Join(append([]string{"help"}, args[1:]...), "-")) + counter.Inc("go/subcommand:" + strings.Join(append([]string{"help"}, args[1:]...), "-")) help.Help(os.Stdout, args[1:]) return } @@ -128,17 +129,17 @@ func main() { } switch strings.ToLower(cfg.GOROOT) { case "/usr/local/go": // Location recommended for installation on Linux and Darwin and used by Mac installer. - telemetry.Inc("go/goroot:usr-local-go") + counter.Inc("go/goroot:usr-local-go") case "/usr/lib/go": // A typical location used by Linux package managers. - telemetry.Inc("go/goroot:usr-lib-go") + counter.Inc("go/goroot:usr-lib-go") case "/usr/lib/golang": // Another typical location used by Linux package managers. - telemetry.Inc("go/goroot:usr-lib-golang") + counter.Inc("go/goroot:usr-lib-golang") case `c:\program files\go`: // Location used by Windows installer. - telemetry.Inc("go/goroot:program-files-go") + counter.Inc("go/goroot:program-files-go") case `c:\program files (x86)\go`: // Location used by 386 Windows installer on amd64 platform. - telemetry.Inc("go/goroot:program-files-x86-go") + counter.Inc("go/goroot:program-files-x86-go") default: - telemetry.Inc("go/goroot:other") + counter.Inc("go/goroot:other") } // Diagnose common mistake: GOPATH==GOROOT. @@ -184,7 +185,7 @@ func main() { } if args[used] == "help" { // Accept 'go mod help' and 'go mod help foo' for 'go help mod' and 'go help mod foo'. - telemetry.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "-" + strings.Join(args[used:], "-")) + counter.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "-" + strings.Join(args[used:], "-")) help.Help(os.Stdout, append(slices.Clip(args[:used]), args[used+1:]...)) base.Exit() } @@ -196,7 +197,7 @@ func main() { if cmdName == "" { cmdName = args[0] } - telemetry.Inc("go/subcommand:unknown") + counter.Inc("go/subcommand:unknown") fmt.Fprintf(os.Stderr, "go %s: unknown command\nRun 'go help%s' for usage.\n", cmdName, helpArg) base.SetExitStatus(2) base.Exit() @@ -206,7 +207,7 @@ func main() { // increment in the tool subcommand's Run function because we need // to do the flag processing in invoke first. if cfg.CmdName != "tool" { - telemetry.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-")) + counter.Inc("go/subcommand:" + strings.ReplaceAll(cfg.CmdName, " ", "-")) } telemetrystats.Increment() invoke(cmd, args[used-1:]) @@ -274,8 +275,8 @@ func invoke(cmd *base.Command, args []string) { base.SetFromGOFLAGS(&cmd.Flag) cmd.Flag.Parse(args[1:]) flagCounterPrefix := "go/" + strings.ReplaceAll(cfg.CmdName, " ", "-") + "/flag" - telemetry.CountFlags(flagCounterPrefix+":", cmd.Flag) - telemetry.CountFlagValue(flagCounterPrefix+"/", cmd.Flag, "buildmode") + counter.CountFlags(flagCounterPrefix+":", cmd.Flag) + counter.CountFlagValue(flagCounterPrefix+"/", cmd.Flag, "buildmode") args = cmd.Flag.Args() } @@ -361,7 +362,7 @@ func handleChdirFlag() { _, dir, _ = strings.Cut(a, "=") os.Args = slices.Delete(os.Args, used, used+1) } - telemetry.Inc("go/flag:C") + counter.Inc("go/flag:C") if err := os.Chdir(dir); err != nil { base.Fatalf("go: %v", err) diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index d6721f93273..d91a75b1050 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -25,7 +25,7 @@ import ( "strconv" "strings" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "golang.org/x/sync/semaphore" ) @@ -374,11 +374,11 @@ func main() { } func gofmtMain(s *sequencer) { - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("gofmt/invocations") - telemetry.CountFlags("gofmt/flag:", *flag.CommandLine) + counter.Inc("gofmt/invocations") + counter.CountFlags("gofmt/flag:", *flag.CommandLine) if *cpuprofile != "" { fdSem <- true diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.go b/src/cmd/internal/obj/arm64/asm_arm64_test.go index 068039496a2..83d137a0846 100644 --- a/src/cmd/internal/obj/arm64/asm_arm64_test.go +++ b/src/cmd/internal/obj/arm64/asm_arm64_test.go @@ -4,302 +4,7 @@ package arm64 -import ( - "bytes" - "fmt" - "internal/testenv" - "os" - "path/filepath" - "regexp" - "testing" -) - -func TestSplitImm24uScaled(t *testing.T) { - tests := []struct { - v int32 - shift int - wantErr bool - wantHi int32 - wantLo int32 - }{ - { - v: 0, - shift: 0, - wantHi: 0, - wantLo: 0, - }, - { - v: 0x1001, - shift: 0, - wantHi: 0x1000, - wantLo: 0x1, - }, - { - v: 0xffffff, - shift: 0, - wantHi: 0xfff000, - wantLo: 0xfff, - }, - { - v: 0xffffff, - shift: 1, - wantErr: true, - }, - { - v: 0xfe, - shift: 1, - wantHi: 0x0, - wantLo: 0x7f, - }, - { - v: 0x10fe, - shift: 1, - wantHi: 0x0, - wantLo: 0x87f, - }, - { - v: 0x2002, - shift: 1, - wantHi: 0x2000, - wantLo: 0x1, - }, - { - v: 0xfffffe, - shift: 1, - wantHi: 0xffe000, - wantLo: 0xfff, - }, - { - v: 0x1000ffe, - shift: 1, - wantHi: 0xfff000, - wantLo: 0xfff, - }, - { - v: 0x1001000, - shift: 1, - wantErr: true, - }, - { - v: 0xfffffe, - shift: 2, - wantErr: true, - }, - { - v: 0x4004, - shift: 2, - wantHi: 0x4000, - wantLo: 0x1, - }, - { - v: 0xfffffc, - shift: 2, - wantHi: 0xffc000, - wantLo: 0xfff, - }, - { - v: 0x1002ffc, - shift: 2, - wantHi: 0xfff000, - wantLo: 0xfff, - }, - { - v: 0x1003000, - shift: 2, - wantErr: true, - }, - { - v: 0xfffffe, - shift: 3, - wantErr: true, - }, - { - v: 0x8008, - shift: 3, - wantHi: 0x8000, - wantLo: 0x1, - }, - { - v: 0xfffff8, - shift: 3, - wantHi: 0xff8000, - wantLo: 0xfff, - }, - { - v: 0x1006ff8, - shift: 3, - wantHi: 0xfff000, - wantLo: 0xfff, - }, - { - v: 0x1007000, - shift: 3, - wantErr: true, - }, - } - for _, test := range tests { - hi, lo, err := splitImm24uScaled(test.v, test.shift) - switch { - case err == nil && test.wantErr: - t.Errorf("splitImm24uScaled(%v, %v) succeeded, want error", test.v, test.shift) - case err != nil && !test.wantErr: - t.Errorf("splitImm24uScaled(%v, %v) failed: %v", test.v, test.shift, err) - case !test.wantErr: - if got, want := hi, test.wantHi; got != want { - t.Errorf("splitImm24uScaled(%x, %x) - got hi %x, want %x", test.v, test.shift, got, want) - } - if got, want := lo, test.wantLo; got != want { - t.Errorf("splitImm24uScaled(%x, %x) - got lo %x, want %x", test.v, test.shift, got, want) - } - } - } - for shift := 0; shift <= 3; shift++ { - for v := int32(0); v < 0xfff000+0xfff< 0 { // dump symbol info on crash diff --git a/src/cmd/link/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go index 8cfecafe84a..14f04855305 100644 --- a/src/cmd/link/internal/ld/pe.go +++ b/src/cmd/link/internal/ld/pe.go @@ -1548,8 +1548,21 @@ func (rt *peBaseRelocTable) write(ctxt *Link) { // sort the pages array sort.Sort(rt.pages) + // .reloc section must be 32-bit aligned + if out.Offset()&3 != 0 { + Errorf(nil, "internal error, start of .reloc not 32-bit aligned") + } + for _, p := range rt.pages { b := rt.blocks[p] + + // Add a dummy entry at the end of the list if we have an + // odd number of entries, so as to ensure that the next + // block starts on a 32-bit boundary (see issue 68260). + if len(b.entries)&1 != 0 { + b.entries = append(b.entries, peBaseRelocEntry{}) + } + const sizeOfPEbaseRelocBlock = 8 // 2 * sizeof(uint32) blockSize := uint32(sizeOfPEbaseRelocBlock + len(b.entries)*2) out.Write32(p) diff --git a/src/cmd/nm/nm.go b/src/cmd/nm/nm.go index e0d98d5f6c6..752870654d5 100644 --- a/src/cmd/nm/nm.go +++ b/src/cmd/nm/nm.go @@ -13,7 +13,7 @@ import ( "sort" "cmd/internal/objfile" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) const helpText = `usage: go tool nm [options] file... @@ -68,11 +68,11 @@ func (nflag) String() string { func main() { log.SetFlags(0) - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("nm/invocations") - telemetry.CountFlags("nm/flag:", *flag.CommandLine) + counter.Inc("nm/invocations") + counter.CountFlags("nm/flag:", *flag.CommandLine) switch *sortOrder { case "address", "name", "none", "size": diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go index 7554b5500c1..b5b0d7f5178 100644 --- a/src/cmd/objdump/main.go +++ b/src/cmd/objdump/main.go @@ -41,7 +41,7 @@ import ( "strings" "cmd/internal/objfile" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" ) var printCode = flag.Bool("S", false, "print Go code alongside assembly") @@ -58,12 +58,12 @@ func usage() { func main() { log.SetFlags(0) log.SetPrefix("objdump: ") - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("objdump/invocations") - telemetry.CountFlags("objdump/flag:", *flag.CommandLine) + counter.Inc("objdump/invocations") + counter.CountFlags("objdump/flag:", *flag.CommandLine) if flag.NArg() != 1 && flag.NArg() != 3 { usage() } diff --git a/src/cmd/pack/pack.go b/src/cmd/pack/pack.go index 28f217ace1b..4ac6ce995fe 100644 --- a/src/cmd/pack/pack.go +++ b/src/cmd/pack/pack.go @@ -6,7 +6,7 @@ package main import ( "cmd/internal/archive" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "fmt" "io" "io/fs" @@ -31,7 +31,7 @@ func usage() { func main() { log.SetFlags(0) log.SetPrefix("pack: ") - telemetry.OpenCounters() + counter.Open() // need "pack op archive" at least. if len(os.Args) < 3 { log.Print("not enough arguments") @@ -39,8 +39,8 @@ func main() { usage() } setOp(os.Args[1]) - telemetry.Inc("pack/invocations") - telemetry.Inc("pack/op:" + string(op)) + counter.Inc("pack/invocations") + counter.Inc("pack/op:" + string(op)) var ar *Archive switch op { case 'p': diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index 722b745287e..a1c2cd210f8 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -25,22 +25,22 @@ import ( "time" "cmd/internal/objfile" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "github.com/google/pprof/driver" "github.com/google/pprof/profile" ) func main() { - telemetry.OpenCounters() - telemetry.Inc("pprof/invocations") + counter.Open() + counter.Inc("pprof/invocations") options := &driver.Options{ Fetch: new(fetcher), Obj: new(objTool), UI: newUI(), } err := driver.PProf(options) - telemetry.CountFlags("pprof/flag:", *flag.CommandLine) // pprof will use the flag package as its default + counter.CountFlags("pprof/flag:", *flag.CommandLine) // pprof will use the flag package as its default if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(2) diff --git a/src/cmd/preprofile/main.go b/src/cmd/preprofile/main.go index 1260eed1047..60aa1f7cc6c 100644 --- a/src/cmd/preprofile/main.go +++ b/src/cmd/preprofile/main.go @@ -18,7 +18,7 @@ import ( "bufio" "cmd/internal/objabi" "cmd/internal/pgo" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "flag" "fmt" "log" @@ -73,12 +73,12 @@ func main() { log.SetFlags(0) log.SetPrefix("preprofile: ") - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("preprofile/invocations") - telemetry.CountFlags("preprofile/flag:", *flag.CommandLine) + counter.Inc("preprofile/invocations") + counter.CountFlags("preprofile/flag:", *flag.CommandLine) if *input == "" { log.Print("Input pprof path required (-i)") usage() diff --git a/src/cmd/test2json/main.go b/src/cmd/test2json/main.go index 844ee5aa6c6..b704dd4d31a 100644 --- a/src/cmd/test2json/main.go +++ b/src/cmd/test2json/main.go @@ -96,7 +96,7 @@ import ( "os/exec" "os/signal" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "cmd/internal/test2json" ) @@ -116,12 +116,12 @@ func ignoreSignals() { } func main() { - telemetry.OpenCounters() + counter.Open() flag.Usage = usage flag.Parse() - telemetry.Inc("test2json/invocations") - telemetry.CountFlags("test2json/flag:", *flag.CommandLine) + counter.Inc("test2json/invocations") + counter.CountFlags("test2json/flag:", *flag.CommandLine) var mode test2json.Mode if *flagT { diff --git a/src/cmd/trace/gstate.go b/src/cmd/trace/gstate.go index bcbe3b6c7fa..638d492670a 100644 --- a/src/cmd/trace/gstate.go +++ b/src/cmd/trace/gstate.go @@ -46,7 +46,7 @@ type gState[R resource] struct { // goroutine stopped executing. These are flushed on every stop or block. completedRanges []completedRange - // startRunning is the most recent event that caused a goroutine to + // startRunningTime is the most recent event that caused a goroutine to // transition to GoRunning. startRunningTime trace.Time diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index e48048b9f25..d51ee581645 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -6,7 +6,7 @@ package main import ( "cmd/internal/browser" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "flag" "fmt" "internal/trace" @@ -64,14 +64,14 @@ var ( ) func main() { - telemetry.OpenCounters() + counter.Open() flag.Usage = func() { fmt.Fprint(os.Stderr, usageMessage) os.Exit(2) } flag.Parse() - telemetry.Inc("trace/invocations") - telemetry.CountFlags("trace/flag:", *flag.CommandLine) + counter.Inc("trace/invocations") + counter.CountFlags("trace/flag:", *flag.CommandLine) // Go 1.7 traces embed symbol info and does not require the binary. // But we optionally accept binary as first arg for Go 1.5 traces. diff --git a/src/cmd/vendor/golang.org/x/mod/module/module.go b/src/cmd/vendor/golang.org/x/mod/module/module.go index cac1a899e9c..2a364b229b9 100644 --- a/src/cmd/vendor/golang.org/x/mod/module/module.go +++ b/src/cmd/vendor/golang.org/x/mod/module/module.go @@ -506,7 +506,6 @@ var badWindowsNames = []string{ "PRN", "AUX", "NUL", - "COM0", "COM1", "COM2", "COM3", @@ -516,7 +515,6 @@ var badWindowsNames = []string{ "COM7", "COM8", "COM9", - "LPT0", "LPT1", "LPT2", "LPT3", diff --git a/src/cmd/vendor/golang.org/x/mod/sumdb/client.go b/src/cmd/vendor/golang.org/x/mod/sumdb/client.go index 04c6e24d0e0..04dbdfe46af 100644 --- a/src/cmd/vendor/golang.org/x/mod/sumdb/client.go +++ b/src/cmd/vendor/golang.org/x/mod/sumdb/client.go @@ -142,6 +142,14 @@ func (c *Client) initWork() { c.verifiers = note.VerifierList(verifier) c.name = verifier.Name() + if c.latest.N == 0 { + c.latest.Hash, err = tlog.TreeHash(0, nil) + if err != nil { + c.initErr = err + return + } + } + data, err := c.ops.ReadConfig(c.name + "/latest") if err != nil { c.initErr = err diff --git a/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tlog.go b/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tlog.go index 6a11a752f90..f7ea7538320 100644 --- a/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tlog.go +++ b/src/cmd/vendor/golang.org/x/mod/sumdb/tlog/tlog.go @@ -234,14 +234,22 @@ func (f HashReaderFunc) ReadHashes(indexes []int64) ([]Hash, error) { return f(indexes) } +// emptyHash is the hash of the empty tree, per RFC 6962, Section 2.1. +// It is the hash of the empty string. +var emptyHash = Hash{ + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, + 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, + 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, + 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, +} + // TreeHash computes the hash for the root of the tree with n records, // using the HashReader to obtain previously stored hashes // (those returned by StoredHashes during the writes of those n records). // TreeHash makes a single call to ReadHash requesting at most 1 + log₂ n hashes. -// The tree of size zero is defined to have an all-zero Hash. func TreeHash(n int64, r HashReader) (Hash, error) { if n == 0 { - return Hash{}, nil + return emptyHash, nil } indexes := subTreeIndex(0, n, nil) hashes, err := r.ReadHashes(indexes) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/mremap.go b/src/cmd/vendor/golang.org/x/sys/unix/mremap.go index fd45fe529da..3a5e776f895 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/mremap.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/mremap.go @@ -50,3 +50,8 @@ func (m *mremapMmapper) Mremap(oldData []byte, newLength int, flags int) (data [ func Mremap(oldData []byte, newLength int, flags int) (data []byte, err error) { return mapper.Mremap(oldData, newLength, flags) } + +func MremapPtr(oldAddr unsafe.Pointer, oldSize uintptr, newAddr unsafe.Pointer, newSize uintptr, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mremap(uintptr(oldAddr), oldSize, newSize, flags, uintptr(newAddr)) + return unsafe.Pointer(xaddr), err +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_darwin.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_darwin.go index 59542a897d2..4cc7b005967 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -542,6 +542,18 @@ func SysctlKinfoProcSlice(name string, args ...int) ([]KinfoProc, error) { } } +//sys pthread_chdir_np(path string) (err error) + +func PthreadChdir(path string) (err error) { + return pthread_chdir_np(path) +} + +//sys pthread_fchdir_np(fd int) (err error) + +func PthreadFchdir(fd int) (err error) { + return pthread_fchdir_np(fd) +} + //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_unix.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_unix.go index 77081de8c7d..4e92e5aa406 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_unix.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_unix.go @@ -154,6 +154,15 @@ func Munmap(b []byte) (err error) { return mapper.Munmap(b) } +func MmapPtr(fd int, offset int64, addr unsafe.Pointer, length uintptr, prot int, flags int) (ret unsafe.Pointer, err error) { + xaddr, err := mapper.mmap(uintptr(addr), length, prot, flags, fd, offset) + return unsafe.Pointer(xaddr), err +} + +func MunmapPtr(addr unsafe.Pointer, length uintptr) (err error) { + return mapper.munmap(uintptr(addr), length) +} + func Read(fd int, p []byte) (n int, err error) { n, err = read(fd, p) if raceenabled { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index ccb02f240a4..07642c308d3 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index 8b8bb284028..923e08cb792 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 1b40b997b52..7d73dda6473 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -760,6 +760,39 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func pthread_chdir_np(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := syscall_syscall(libc_pthread_chdir_np_trampoline_addr, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_chdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_chdir_np pthread_chdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pthread_fchdir_np(fd int) (err error) { + _, _, e1 := syscall_syscall(libc_pthread_fchdir_np_trampoline_addr, uintptr(fd), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pthread_fchdir_np_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pthread_fchdir_np pthread_fchdir_np "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 08362c1ab74..057700111e7 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -228,6 +228,16 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_pthread_chdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_chdir_np(SB) +GLOBL ·libc_pthread_chdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_chdir_np_trampoline_addr(SB)/8, $libc_pthread_chdir_np_trampoline<>(SB) + +TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pthread_fchdir_np(SB) +GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/src/cmd/vendor/golang.org/x/sys/windows/security_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/security_windows.go index 6f7d2ac70a9..97651b5bd04 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/security_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/security_windows.go @@ -894,7 +894,7 @@ type ACL struct { aclRevision byte sbz1 byte aclSize uint16 - aceCount uint16 + AceCount uint16 sbz2 uint16 } @@ -1087,6 +1087,27 @@ type EXPLICIT_ACCESS struct { Trustee TRUSTEE } +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header +type ACE_HEADER struct { + AceType uint8 + AceFlags uint8 + AceSize uint16 +} + +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace +type ACCESS_ALLOWED_ACE struct { + Header ACE_HEADER + Mask ACCESS_MASK + SidStart uint32 +} + +const ( + // Constants for AceType + // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header + ACCESS_ALLOWED_ACE_TYPE = 0 + ACCESS_DENIED_ACE_TYPE = 1 +) + // This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions. type TrusteeValue uintptr @@ -1158,6 +1179,7 @@ type OBJECTS_AND_NAME struct { //sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD //sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW +//sys GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) = advapi32.GetAce // Control returns the security descriptor control bits. func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) { diff --git a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 9f73df75b5f..eba761018aa 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -91,6 +91,7 @@ var ( procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") procEqualSid = modadvapi32.NewProc("EqualSid") procFreeSid = modadvapi32.NewProc("FreeSid") + procGetAce = modadvapi32.NewProc("GetAce") procGetLengthSid = modadvapi32.NewProc("GetLengthSid") procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW") procGetSecurityDescriptorControl = modadvapi32.NewProc("GetSecurityDescriptorControl") @@ -1224,6 +1225,14 @@ func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCE return } +func GetAce(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) { + r0, _, _ := syscall.Syscall(procGetAce.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(aceIndex), uintptr(unsafe.Pointer(pAce))) + if r0 == 0 { + ret = GetLastError() + } + return +} + func SetKernelObjectSecurity(handle Handle, securityInformation SECURITY_INFORMATION, securityDescriptor *SECURITY_DESCRIPTOR) (err error) { r1, _, e1 := syscall.Syscall(procSetKernelObjectSecurity.Addr(), 3, uintptr(handle), uintptr(securityInformation), uintptr(unsafe.Pointer(securityDescriptor))) if r1 == 0 { diff --git a/src/cmd/vendor/golang.org/x/telemetry/counter/counter.go b/src/cmd/vendor/golang.org/x/telemetry/counter/counter.go index ba1c68889e1..ff727ad97ce 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/counter/counter.go +++ b/src/cmd/vendor/golang.org/x/telemetry/counter/counter.go @@ -15,6 +15,7 @@ import ( "runtime/debug" "golang.org/x/telemetry/internal/counter" + "golang.org/x/telemetry/internal/telemetry" ) // Inc increments the counter with the given name. @@ -82,12 +83,23 @@ func NewStack(name string, depth int) *StackCounter { // If the telemetry mode is "off", Open is a no-op. Otherwise, it opens the // counter file on disk and starts to mmap telemetry counters to the file. // Open also persists any counters already created in the current process. -// -// Programs using telemetry should call Open exactly once. func Open() { counter.Open() } +// OpenDir prepares telemetry counters for recording to the file system, using +// the specified telemetry directory, if it is not the empty string. +// +// If the telemetry mode is "off", Open is a no-op. Otherwise, it opens the +// counter file on disk and starts to mmap telemetry counters to the file. +// Open also persists any counters already created in the current process. +func OpenDir(telemetryDir string) { + if telemetryDir != "" { + telemetry.Default = telemetry.NewDir(telemetryDir) + } + counter.Open() +} + // 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. diff --git a/src/cmd/vendor/golang.org/x/telemetry/counter/doc.go b/src/cmd/vendor/golang.org/x/telemetry/counter/doc.go index 53695ac2701..639e9ea3568 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/counter/doc.go +++ b/src/cmd/vendor/golang.org/x/telemetry/counter/doc.go @@ -48,4 +48,11 @@ // For example given two counters "gopls/completion/latency:<50ms" and // "gopls/completion/latency:<100ms", the "<100ms" bucket counts events // with latency in the half-open interval [50ms, 100ms). +// +// # Debugging +// +// The GODEBUG environment variable can enable printing of additional debug +// information for counters. Adding GODEBUG=countertrace=1 to the environment +// of a process using counters causes the x/telemetry/counter package to log +// counter information to stderr. package counter diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/counter/counter.go b/src/cmd/vendor/golang.org/x/telemetry/internal/counter/counter.go index cc562bc7442..6fbcec3a926 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/counter/counter.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/counter/counter.go @@ -15,10 +15,15 @@ import ( "sync/atomic" ) -// Note: not using internal/godebug, so that internal/godebug can use internal/counter. -var debugCounter = strings.Contains(os.Getenv("GODEBUG"), "countertrace=1") +var ( + // Note: not using internal/godebug, so that internal/godebug can use + // internal/counter. + debugCounter = strings.Contains(os.Getenv("GODEBUG"), "countertrace=1") + CrashOnBugs = false // for testing; if set, exit on fatal log messages +) -func debugPrintf(format string, args ...interface{}) { +// debugPrintf formats a debug message if GODEBUG=countertrace=1. +func debugPrintf(format string, args ...any) { if debugCounter { if len(format) == 0 || format[len(format)-1] != '\n' { format += "\n" @@ -27,6 +32,17 @@ func debugPrintf(format string, args ...interface{}) { } } +// debugFatalf logs a fatal error if GODEBUG=countertrace=1. +func debugFatalf(format string, args ...any) { + if debugCounter || CrashOnBugs { + if len(format) == 0 || format[len(format)-1] != '\n' { + format += "\n" + } + fmt.Fprintf(os.Stderr, "counter bug: "+format, args...) + os.Exit(1) + } +} + // A Counter is a single named event counter. // A Counter is safe for use by multiple goroutines simultaneously. // @@ -246,6 +262,7 @@ func (c *Counter) releaseLock(state counterStateBits) { } } +// add wraps the atomic.Uint64.Add operation to handle integer overflow. func (c *Counter) add(n uint64) uint64 { count := c.ptr.count for { @@ -336,11 +353,11 @@ func readFile(f *file) (*File, error) { } // ReadFile reads the counters and stack counters from the given file. -// This is the implementation of x/telemetry/counter/countertest.Read +// This is the implementation of x/telemetry/counter/countertest.ReadFile. func ReadFile(name string) (counters, stackCounters map[string]uint64, _ error) { // TODO: Document the format of the stackCounters names. - data, err := os.ReadFile(name) + data, err := ReadMapped(name) if err != nil { return nil, nil, fmt.Errorf("failed to read from file: %v", err) } @@ -359,3 +376,26 @@ func ReadFile(name string) (counters, stackCounters map[string]uint64, _ error) } return counters, stackCounters, nil } + +// ReadMapped reads the contents of the given file by memory mapping. +// +// This avoids file synchronization issues. +func ReadMapped(name string) ([]byte, error) { + f, err := os.OpenFile(name, os.O_RDWR, 0666) + if err != nil { + return nil, err + } + defer f.Close() + fi, err := f.Stat() + if err != nil { + return nil, err + } + mapping, err := memmap(f) + if err != nil { + return nil, err + } + data := make([]byte, fi.Size()) + copy(data, mapping.Data) + munmap(mapping) + return data, nil +} diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/counter/file.go b/src/cmd/vendor/golang.org/x/telemetry/internal/counter/file.go index 0cb6cc22de3..4fe9e577b17 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/counter/file.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/counter/file.go @@ -36,7 +36,22 @@ type file struct { buildInfo *debug.BuildInfo timeBegin, timeEnd time.Time err error - current atomic.Pointer[mappedFile] // may be read without holding mu, but may be nil + // current holds the current file mapping, which may change when the file is + // rotated or extended. + // + // current may be read without holding mu, but may be nil. + // + // The cleanup logic for file mappings is complicated, because invalidating + // counter pointers is reentrant: [file.invalidateCounters] may call + // [file.lookup], which acquires mu. Therefore, writing current must be done + // as follows: + // 1. record the previous value of current + // 2. Store a new value in current + // 3. unlock mu + // 4. call invalidateCounters + // 5. close the previous mapped value from (1) + // TODO(rfindley): simplify + current atomic.Pointer[mappedFile] } var defaultFile file @@ -292,7 +307,7 @@ func (f *file) rotate1() time.Time { } name := filepath.Join(dir, baseName) - m, err := openMapped(name, meta, nil) + m, err := openMapped(name, meta) if err != nil { // Mapping failed: // If there used to be a mapped file, after cleanup @@ -334,12 +349,16 @@ func (f *file) newCounter1(name string) (v *atomic.Uint64, cleanup func()) { cleanup = nop if newM != nil { f.current.Store(newM) - // TODO(rfindley): shouldn't this close f.current? - cleanup = f.invalidateCounters + cleanup = func() { + f.invalidateCounters() + current.close() + } } return v, cleanup } +var openOnce sync.Once + // Open associates counting with the defaultFile. // The returned function is for testing only, and should // be called after all Inc()s are finished, but before @@ -349,25 +368,61 @@ func Open() func() { if telemetry.DisabledOnPlatform { return func() {} } - if mode, _ := telemetry.Default.Mode(); mode == "off" { - // Don't open the file when telemetry is off. - defaultFile.err = ErrDisabled - return func() {} // No need to clean up. - } - debugPrintf("Open") - defaultFile.rotate() - return func() { - // Once this has been called, the defaultFile is no longer usable. - mf := defaultFile.current.Load() - if mf == nil { - // telemetry might have been off + close := func() {} + openOnce.Do(func() { + if mode, _ := telemetry.Default.Mode(); mode == "off" { + // Don't open the file when telemetry is off. + defaultFile.err = ErrDisabled + // No need to clean up. return } - mf.close() - } + debugPrintf("Open") + defaultFile.rotate() + close = func() { + // Once this has been called, the defaultFile is no longer usable. + mf := defaultFile.current.Load() + if mf == nil { + // telemetry might have been off + return + } + mf.close() + } + }) + return close } +const ( + FileVersion = "v1" + hdrPrefix = "# telemetry/counter file " + FileVersion + "\n" + recordUnit = 32 + maxMetaLen = 512 + numHash = 512 // 2kB for hash table + maxNameLen = 4 * 1024 + limitOff = 0 + hashOff = 4 + pageSize = 16 * 1024 + minFileLen = 16 * 1024 +) + // A mappedFile is a counter file mmapped into memory. +// +// The file layout for a mappedFile m is as follows: +// +// offset, byte size: description +// ------------------ ----------- +// 0, hdrLen: header, containing metadata; see [mappedHeader] +// hdrLen+limitOff, 4: uint32 allocation limit (byte offset of the end of counter records) +// hdrLen+hashOff, 4*numHash: hash table, stores uint32 heads of a linked list of records, keyed by name hash +// hdrLen+hashOff+4*numHash to limit: counter records: see record syntax below +// +// The record layout is as follows: +// +// offset, byte size: description +// ------------------ ----------- +// 0, 8: uint64 counter value +// 8, 12: uint32 name length +// 12, 16: uint32 offset of next record in linked list +// 16, name length: counter name type mappedFile struct { meta string hdrLen uint32 @@ -377,9 +432,16 @@ type mappedFile struct { mapping *mmap.Data } +// openMapped opens and memory maps a file. +// +// name is the path to the file. +// +// meta is the file metadata, which must match the metadata of the file on disk +// exactly. +// // existing should be nil the first time this is called for a file, // and when remapping, should be the previous mappedFile. -func openMapped(name string, meta string, existing *mappedFile) (_ *mappedFile, err error) { +func openMapped(name, meta string) (_ *mappedFile, err error) { hdr, err := mappedHeader(meta) if err != nil { return nil, err @@ -395,13 +457,13 @@ func openMapped(name string, meta string, existing *mappedFile) (_ *mappedFile, f: f, meta: meta, } - // without this files cannot be cleanedup on Windows (affects tests) - runtime.SetFinalizer(m, (*mappedFile).close) + defer func() { if err != nil { m.close() } }() + info, err := f.Stat() if err != nil { return nil, err @@ -426,17 +488,14 @@ func openMapped(name string, meta string, existing *mappedFile) (_ *mappedFile, } // Map into memory. - var mapping mmap.Data - if existing != nil { - mapping, err = memmap(f, existing.mapping) - } else { - mapping, err = memmap(f, nil) - } + mapping, err := memmap(f) if err != nil { return nil, err } - m.mapping = &mapping + m.mapping = mapping if !bytes.HasPrefix(m.mapping.Data, hdr) { + // TODO(rfindley): we can and should do better here, reading the mapped + // header length and comparing headers exactly. return nil, fmt.Errorf("counter: header mismatch") } m.hdrLen = uint32(len(hdr)) @@ -444,19 +503,6 @@ func openMapped(name string, meta string, existing *mappedFile) (_ *mappedFile, return m, nil } -const ( - FileVersion = "v1" - hdrPrefix = "# telemetry/counter file " + FileVersion + "\n" - recordUnit = 32 - maxMetaLen = 512 - numHash = 512 // 2kB for hash table - maxNameLen = 4 * 1024 - limitOff = 0 - hashOff = 4 - pageSize = 16 * 1024 - minFileLen = 16 * 1024 -) - func mappedHeader(meta string) ([]byte, error) { if len(meta) > maxMetaLen { return nil, fmt.Errorf("counter: metadata too large") @@ -477,6 +523,11 @@ func (m *mappedFile) place(limit uint32, name string) (start, end uint32) { } n := round(uint32(16+len(name)), recordUnit) start = round(limit, recordUnit) // should already be rounded but just in case + // Note: Checking for crossing a page boundary would be + // start/pageSize != (start+n-1)/pageSize, + // but we are checking for reaching the page end, so no -1. + // The page end is reserved for use by extend. + // See the comment in m.extend. if start/pageSize != (start+n)/pageSize { // bump start to next page start = round(limit, pageSize) @@ -531,6 +582,9 @@ func (m *mappedFile) cas32(off, old, new uint32) bool { return (*atomic.Uint32)(unsafe.Pointer(&m.mapping.Data[off])).CompareAndSwap(old, new) } +// entryAt reads a counter record at the given byte offset. +// +// See the documentation for [mappedFile] for a description of the counter record layout. func (m *mappedFile) entryAt(off uint32) (name []byte, next uint32, v *atomic.Uint64, ok bool) { if off < m.hdrLen+hashOff || int64(off)+16 > int64(len(m.mapping.Data)) { return nil, 0, nil, false @@ -545,7 +599,14 @@ func (m *mappedFile) entryAt(off uint32) (name []byte, next uint32, v *atomic.Ui return name, next, v, true } +// writeEntryAt writes a new counter record at the given offset. +// +// See the documentation for [mappedFile] for a description of the counter record layout. +// +// writeEntryAt only returns false in the presence of some form of corruption: +// an offset outside the bounds of the record region in the mapped file. func (m *mappedFile) writeEntryAt(off uint32, name string) (next *atomic.Uint32, v *atomic.Uint64, ok bool) { + // TODO(rfindley): shouldn't this first condition be off < m.hdrLen+hashOff+4*numHash? if off < m.hdrLen+hashOff || int64(off)+16+int64(len(name)) > int64(len(m.mapping.Data)) { return nil, nil, false } @@ -556,6 +617,11 @@ func (m *mappedFile) writeEntryAt(off uint32, name string) (next *atomic.Uint32, return next, v, true } +// lookup searches the mapped file for a counter record with the given name, returning: +// - v: the mapped counter value +// - headOff: the offset of the head pointer (see [mappedFile]) +// - head: the value of the head pointer +// - ok: whether lookup succeeded func (m *mappedFile) lookup(name string) (v *atomic.Uint64, headOff, head uint32, ok bool) { h := hash(name) headOff = m.hdrLen + hashOff + h*4 @@ -574,6 +640,9 @@ func (m *mappedFile) lookup(name string) (v *atomic.Uint64, headOff, head uint32 return nil, headOff, head, true } +// newCounter allocates and writes a new counter record with the given name. +// +// If name is already recorded in the file, newCounter returns the existing counter. func (m *mappedFile) newCounter(name string) (v *atomic.Uint64, m1 *mappedFile, err error) { if len(name) > maxNameLen { return nil, nil, fmt.Errorf("counter name too long") @@ -590,19 +659,37 @@ func (m *mappedFile) newCounter(name string) (v *atomic.Uint64, m1 *mappedFile, }() v, headOff, head, ok := m.lookup(name) - for !ok { + for tries := 0; !ok; tries++ { + if tries >= 10 { + debugFatalf("corrupt: failed to remap after 10 tries") + return nil, nil, errCorrupt + } // Lookup found an invalid pointer, // perhaps because the file has grown larger than the mapping. limit := m.load32(m.hdrLen + limitOff) - if int64(limit) <= int64(len(m.mapping.Data)) { - // Mapping doesn't need to grow, so lookup found actual corruption. - debugPrintf("corrupt1\n") + if limit, datalen := int64(limit), int64(len(m.mapping.Data)); limit <= datalen { + // Mapping doesn't need to grow, so lookup found actual corruption, + // in the form of an entry pointer that exceeds the recorded allocation + // limit. This should never happen, unless the actual file contents are + // corrupt. + debugFatalf("corrupt: limit %d is within mapping length %d", limit, datalen) return nil, nil, errCorrupt } - newM, err := openMapped(m.f.Name(), m.meta, m) + // That the recorded limit is greater than the mapped data indicates that + // an external process has extended the file. Re-map to pick up this extension. + newM, err := openMapped(m.f.Name(), m.meta) if err != nil { return nil, nil, err } + if limit, datalen := int64(limit), int64(len(newM.mapping.Data)); limit > datalen { + // We've re-mapped, yet limit still exceeds the data length. This + // indicates that the underlying file was somehow truncated, or the + // recorded limit is corrupt. + debugFatalf("corrupt: limit %d exceeds file size %d", limit, datalen) + return nil, nil, errCorrupt + } + // If m != orig, this is at least the second time around the loop + // trying to open the mapping. Close the previous attempt. if m != orig { m.close() } @@ -643,7 +730,7 @@ func (m *mappedFile) newCounter(name string) (v *atomic.Uint64, m1 *mappedFile, // Write record. next, v, ok := m.writeEntryAt(start, name) if !ok { - debugPrintf("corrupt2 %#x+%d vs %#x\n", start, len(name), len(m.mapping.Data)) + debugFatalf("corrupt: failed to write entry: %#x+%d vs %#x\n", start, len(name), len(m.mapping.Data)) return nil, nil, errCorrupt // more likely our math is wrong } @@ -679,12 +766,26 @@ func (m *mappedFile) extend(end uint32) (*mappedFile, error) { return nil, err } if info.Size() < int64(end) { + // Note: multiple processes could be calling extend at the same time, + // but this write only writes the last 4 bytes of the page. + // The last 4 bytes of the page are reserved for this purpose and hold no data. + // (In m.place, if a new record would extend to the very end of the page, + // it is placed in the next page instead.) + // So it is fine if multiple processes extend at the same time. if _, err := m.f.WriteAt(m.zero[:], int64(end)-int64(len(m.zero))); err != nil { return nil, err } } - newM, err := openMapped(m.f.Name(), m.meta, m) - m.f.Close() + newM, err := openMapped(m.f.Name(), m.meta) + if err != nil { + return nil, err + } + if int64(len(newM.mapping.Data)) < int64(end) { + // File system or logic bug: new file is somehow not extended. + // See go.dev/issue/68311, where this appears to have been happening. + newM.close() + return nil, errCorrupt + } return newM, err } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap.go b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap.go index fb3ca9650d9..2febe3ecb9e 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap.go @@ -26,12 +26,11 @@ type Data struct { // Mmap maps the given file into memory. // When remapping a file, pass the most recently returned Data. -func Mmap(f *os.File, data *Data) (Data, error) { - return mmapFile(f, data) +func Mmap(f *os.File) (*Data, error) { + return mmapFile(f) } // Munmap unmaps the given file from memory. func Munmap(d *Data) error { - // d.f.Close() on Windows still gets an error - return munmapFile(*d) + return munmapFile(d) } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_other.go b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_other.go index 361ca8b01aa..190afd8e9fb 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_other.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_other.go @@ -12,14 +12,14 @@ import ( ) // mmapFile on other systems doesn't mmap the file. It just reads everything. -func mmapFile(f *os.File, _ *Data) (Data, error) { +func mmapFile(f *os.File) (*Data, error) { b, err := io.ReadAll(f) if err != nil { - return Data{}, err + return nil, err } - return Data{f, b, nil}, nil + return &Data{f, b, nil}, nil } -func munmapFile(d Data) error { +func munmapFile(_ *Data) error { return nil } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_unix.go b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_unix.go index af462ff6764..f15ac615b34 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_unix.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_unix.go @@ -13,29 +13,29 @@ import ( "syscall" ) -func mmapFile(f *os.File, _ *Data) (Data, error) { +func mmapFile(f *os.File) (*Data, error) { st, err := f.Stat() if err != nil { - return Data{}, err + return nil, err } size := st.Size() pagesize := int64(os.Getpagesize()) if int64(int(size+(pagesize-1))) != size+(pagesize-1) { - return Data{}, fmt.Errorf("%s: too large for mmap", f.Name()) + return nil, fmt.Errorf("%s: too large for mmap", f.Name()) } n := int(size) if n == 0 { - return Data{f, nil, nil}, nil + return &Data{f, nil, nil}, nil } mmapLength := int(((size + pagesize - 1) / pagesize) * pagesize) // round up to page size data, err := syscall.Mmap(int(f.Fd()), 0, mmapLength, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { - return Data{}, &fs.PathError{Op: "mmap", Path: f.Name(), Err: err} + return nil, &fs.PathError{Op: "mmap", Path: f.Name(), Err: err} } - return Data{f, data[:n], nil}, nil + return &Data{f, data[:n], nil}, nil } -func munmapFile(d Data) error { +func munmapFile(d *Data) error { if len(d.Data) == 0 { return nil } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_windows.go b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_windows.go index e70e7c7b074..2e8dfbea9c0 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_windows.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/mmap/mmap_windows.go @@ -13,35 +13,35 @@ import ( "golang.org/x/sys/windows" ) -func mmapFile(f *os.File, previous *Data) (Data, error) { - if previous != nil { - munmapFile(*previous) - } +func mmapFile(f *os.File) (*Data, error) { st, err := f.Stat() if err != nil { - return Data{}, err + return nil, err } size := st.Size() if size == 0 { - return Data{f, nil, nil}, nil + return &Data{f, nil, nil}, nil } // set the min and max sizes to zero to map the whole file, as described in // https://learn.microsoft.com/en-us/windows/win32/memory/creating-a-file-mapping-object#file-mapping-size h, err := windows.CreateFileMapping(windows.Handle(f.Fd()), nil, syscall.PAGE_READWRITE, 0, 0, nil) if err != nil { - return Data{}, fmt.Errorf("CreateFileMapping %s: %w", f.Name(), err) + return nil, fmt.Errorf("CreateFileMapping %s: %w", f.Name(), err) } // the mapping extends from zero to the end of the file mapping // https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile addr, err := windows.MapViewOfFile(h, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, 0) if err != nil { - return Data{}, fmt.Errorf("MapViewOfFile %s: %w", f.Name(), err) + return nil, fmt.Errorf("MapViewOfFile %s: %w", f.Name(), err) } - // need to remember addr and h for unmapping - return Data{f, unsafe.Slice((*byte)(unsafe.Pointer(addr)), size), h}, nil + // Note: previously, we called windows.VirtualQuery here to get the exact + // size of the memory mapped region, but VirtualQuery reported sizes smaller + // than the actual file size (hypothesis: VirtualQuery only reports pages in + // a certain state, and newly written pages may not be counted). + return &Data{f, unsafe.Slice((*byte)(unsafe.Pointer(addr)), size), h}, nil } -func munmapFile(d Data) error { +func munmapFile(d *Data) error { err := windows.UnmapViewOfFile(uintptr(unsafe.Pointer(&d.Data[0]))) x, ok := d.Windows.(windows.Handle) if ok { diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/proginfo.go b/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/proginfo.go index 20be9664fc8..60cb0edc792 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/proginfo.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/proginfo.go @@ -9,8 +9,6 @@ import ( "path/filepath" "runtime/debug" "strings" - - "golang.org/x/mod/module" ) // IsToolchainProgram reports whether a program with the given path is a Go @@ -43,8 +41,13 @@ func ProgramInfo(info *debug.BuildInfo) (goVers, progPath, progVers string) { progVers = goVers } else { progVers = info.Main.Version - if strings.Contains(progVers, "devel") || module.IsPseudoVersion(progVers) { - // We don't want to track pseudo versions, but may want to track prereleases. + if strings.Contains(progVers, "devel") || strings.Count(progVers, "-") > 1 { + // Heuristically mark all pseudo-version-like version strings as "devel" + // to avoid creating too many counter files. + // We should not use regexp that pulls in large dependencies. + // Pseudo-versions have at least three parts (https://go.dev/ref/mod#pseudo-versions). + // This heuristic still allows use to track prerelease + // versions (e.g. gopls@v0.16.0-pre.1, vscgo@v0.42.0-rc.1). progVers = "devel" } } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/types.go b/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/types.go index bcd172d97f8..7c788b34152 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/types.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/telemetry/types.go @@ -31,9 +31,9 @@ type CounterConfig struct { Depth int `json:",omitempty"` // for stack counters } -// A Report is what's uploaded (or saved locally) +// A Report is the weekly aggregate of counters. type Report struct { - Week string // first day this report covers (YYYY-MM-DD) + Week string // End day this report covers (YYYY-MM-DD) LastWeek string // Week field from latest previous report uploaded X float64 // A random probability used to determine which counters are uploaded Programs []*ProgramReport diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/findwork.go b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/findwork.go index f1490be3a57..2b165be3f9f 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/findwork.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/findwork.go @@ -32,7 +32,7 @@ func (u *uploader) findWork() work { } mode, asof := u.dir.Mode() - u.logger.Printf("Finding work: mode %s, asof %s", mode, asof) + u.logger.Printf("Finding work: mode %s asof %s", mode, asof) // count files end in .v1.count // reports end in .json. If they are not to be uploaded they diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/reports.go b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/reports.go index d1091f40add..31bda70676f 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/reports.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/reports.go @@ -115,8 +115,11 @@ func (u *uploader) deleteFiles(files []string) { } } -// createReport for all the count files for the same date. -// returns the absolute path name of the file containing the report +// createReport creates local and upload report files by +// combining all the count files for the expiryDate, and +// returns the upload report file's path. +// It may delete the count files once local and upload report +// files are successfully created. func (u *uploader) createReport(start time.Time, expiryDate string, countFiles []string, lastWeek string) (string, error) { uploadOK := true mode, asof := u.dir.Mode() diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/upload.go b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/upload.go index 2a3bf70f9d5..aa0f4f0c87c 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/upload/upload.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/upload/upload.go @@ -65,12 +65,6 @@ func (u *uploader) uploadReportContents(fname string, buf []byte) bool { fdate = fdate[len(fdate)-len("2006-01-02"):] newname := filepath.Join(u.dir.UploadDir(), fdate+".json") - if _, err := os.Stat(newname); err == nil { - // Another process uploaded but failed to clean up (or hasn't yet cleaned - // up). Ensure that cleanup occurs. - _ = os.Remove(fname) - return false - } // Lock the upload, to prevent duplicate uploads. { @@ -84,6 +78,14 @@ func (u *uploader) uploadReportContents(fname string, buf []byte) bool { defer os.Remove(lockname) } + if _, err := os.Stat(newname); err == nil { + // Another process uploaded but failed to clean up (or hasn't yet cleaned + // up). Ensure that cleanup occurs. + u.logger.Printf("After acquire: report already uploaded") + _ = os.Remove(fname) + return false + } + endpoint := u.uploadServerURL + "/" + fdate b := bytes.NewReader(buf) resp, err := http.Post(endpoint, "application/json", b) diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 8ba7df290fa..8a3cc87dcb1 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-20240603162849-5dfbda438323 ## explicit; go 1.21 golang.org/x/build/relnote -# golang.org/x/mod v0.18.0 +# golang.org/x/mod v0.19.0 ## explicit; go 1.18 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile @@ -40,12 +40,12 @@ golang.org/x/mod/zip ## explicit; go 1.18 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.21.0 +# golang.org/x/sys v0.22.0 ## explicit; go 1.18 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/telemetry v0.0.0-20240612191826-8cad58b3fcbb +# golang.org/x/telemetry v0.0.0-20240712210958-268b4a8ec2d7 ## explicit; go 1.20 golang.org/x/telemetry golang.org/x/telemetry/counter diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go index 84821d43fcb..3ace8ad6897 100644 --- a/src/cmd/vet/main.go +++ b/src/cmd/vet/main.go @@ -6,7 +6,7 @@ package main import ( "cmd/internal/objabi" - "cmd/internal/telemetry" + "cmd/internal/telemetry/counter" "flag" "golang.org/x/tools/go/analysis/unitchecker" @@ -47,10 +47,10 @@ import ( ) func main() { - telemetry.OpenCounters() + counter.Open() objabi.AddVersionFlag() - telemetry.Inc("vet/invocations") + counter.Inc("vet/invocations") unitchecker.Main( appends.Analyzer, asmdecl.Analyzer, @@ -89,5 +89,5 @@ func main() { // It's possible that unitchecker will exit early. In // those cases the flags won't be counted. - telemetry.CountFlags("vet/flag:", *flag.CommandLine) + counter.CountFlags("vet/flag:", *flag.CommandLine) } diff --git a/src/context/context.go b/src/context/context.go index 30adfe987d5..763d4f777ff 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -739,6 +739,8 @@ func stringify(v any) string { return s.String() case string: return s + case nil: + return "" } return reflectlite.TypeOf(v).String() } diff --git a/src/context/x_test.go b/src/context/x_test.go index 2c66ed42b2f..ab3c2757cf5 100644 --- a/src/context/x_test.go +++ b/src/context/x_test.go @@ -243,6 +243,10 @@ func TestValues(t *testing.T) { c4 := WithValue(c3, k1, nil) check(c4, "c4", "", "c2k2", "c3k3") + if got, want := fmt.Sprint(c4), `context.Background.WithValue(context_test.key1, c1k1).WithValue(context_test.key2(1), c2k2).WithValue(context_test.key2(3), c3k3).WithValue(context_test.key1, )`; got != want { + t.Errorf("c.String() = %q want %q", got, want) + } + o0 := otherContext{Background()} check(o0, "o0", "", "", "") diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go index 55415abf020..7accad76324 100644 --- a/src/crypto/hmac/hmac_test.go +++ b/src/crypto/hmac/hmac_test.go @@ -5,8 +5,8 @@ package hmac import ( - "bytes" "crypto/internal/boring" + "crypto/internal/cryptotest" "crypto/md5" "crypto/sha1" "crypto/sha256" @@ -621,39 +621,14 @@ func TestEqual(t *testing.T) { } } -func TestWriteAfterSum(t *testing.T) { - h := New(sha1.New, nil) - h.Write([]byte("hello")) - sumHello := h.Sum(nil) +func TestHMACHash(t *testing.T) { + for i, test := range hmacTests { + baseHash := test.hash + key := test.key - h = New(sha1.New, nil) - h.Write([]byte("hello world")) - sumHelloWorld := h.Sum(nil) - - // Test that Sum has no effect on future Sum or Write operations. - // This is a bit unusual as far as usage, but it's allowed - // by the definition of Go hash.Hash, and some clients expect it to work. - h = New(sha1.New, nil) - h.Write([]byte("hello")) - if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { - t.Fatalf("1st Sum after hello = %x, want %x", sum, sumHello) - } - if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { - t.Fatalf("2nd Sum after hello = %x, want %x", sum, sumHello) - } - - h.Write([]byte(" world")) - if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) { - t.Fatalf("1st Sum after hello world = %x, want %x", sum, sumHelloWorld) - } - if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) { - t.Fatalf("2nd Sum after hello world = %x, want %x", sum, sumHelloWorld) - } - - h.Reset() - h.Write([]byte("hello")) - if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { - t.Fatalf("Sum after Reset + hello = %x, want %x", sum, sumHello) + t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) { + cryptotest.TestHash(t, func() hash.Hash { return New(baseHash, key) }) + }) } } diff --git a/src/crypto/internal/cryptotest/hash.go b/src/crypto/internal/cryptotest/hash.go new file mode 100644 index 00000000000..a950dcb2821 --- /dev/null +++ b/src/crypto/internal/cryptotest/hash.go @@ -0,0 +1,189 @@ +// 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 cryptotest + +import ( + "bytes" + "hash" + "io" + "math/rand" + "testing" + "time" +) + +type MakeHash func() hash.Hash + +// TestHash performs a set of tests on hash.Hash implementations, checking the +// documented requirements of Write, Sum, Reset, Size, and BlockSize. +func TestHash(t *testing.T, mh MakeHash) { + + // Test that Sum returns an appended digest matching output of Size + t.Run("SumAppend", func(t *testing.T) { + h := mh() + rng := newRandReader(t) + + emptyBuff := []byte("") + shortBuff := []byte("a") + longBuff := make([]byte, h.BlockSize()+1) + rng.Read(longBuff) + + // Set of example strings to append digest to + prefixes := [][]byte{nil, emptyBuff, shortBuff, longBuff} + + // Go to each string and check digest gets appended to and is correct size. + for _, prefix := range prefixes { + h.Reset() + + sum := getSum(t, h, prefix) // Append new digest to prefix + + // Check that Sum didn't alter the prefix + if !bytes.Equal(sum[0:len(prefix)], prefix) { + t.Errorf("Sum alters passed buffer instead of appending; got %x, want %x", sum[0:len(prefix)], prefix) + } + + // Check that the appended sum wasn't affected by the prefix + if expectedSum := getSum(t, h, nil); !bytes.Equal(sum[len(prefix):], expectedSum) { + t.Errorf("Sum behavior affected by data in the input buffer; got %x, want %x", sum[len(prefix):], expectedSum) + } + + // Check size of append + if got, want := len(sum)-len(prefix), h.Size(); got != want { + t.Errorf("Sum appends number of bytes != Size; got %v , want %v", got, want) + } + } + }) + + // Test that Hash.Write never returns error. + t.Run("WriteWithoutError", func(t *testing.T) { + h := mh() + rng := newRandReader(t) + + emptySlice := []byte("") + shortSlice := []byte("a") + longSlice := make([]byte, h.BlockSize()+1) + rng.Read(longSlice) + + // Set of example strings to append digest to + slices := [][]byte{emptySlice, shortSlice, longSlice} + + for _, slice := range slices { + writeToHash(t, h, slice) // Writes and checks Write doesn't error + } + }) + + t.Run("ResetState", func(t *testing.T) { + h := mh() + rng := newRandReader(t) + + emptySum := getSum(t, h, nil) + + // Write to hash and then Reset it and see if Sum is same as emptySum + writeEx := make([]byte, h.BlockSize()) + rng.Read(writeEx) + writeToHash(t, h, writeEx) + h.Reset() + resetSum := getSum(t, h, nil) + + if !bytes.Equal(emptySum, resetSum) { + t.Errorf("Reset hash yields different Sum than new hash; got %x, want %x", emptySum, resetSum) + } + }) + + // Check that Write isn't reading from beyond input slice's bounds + t.Run("OutOfBoundsRead", func(t *testing.T) { + h := mh() + blockSize := h.BlockSize() + rng := newRandReader(t) + + msg := make([]byte, blockSize) + rng.Read(msg) + writeToHash(t, h, msg) + expectedDigest := getSum(t, h, nil) // Record control digest + + h.Reset() + + // Make a buffer with msg in the middle and data on either end + buff := make([]byte, blockSize*3) + endOfPrefix, startOfSuffix := blockSize, blockSize*2 + + copy(buff[endOfPrefix:startOfSuffix], msg) + rng.Read(buff[:endOfPrefix]) + rng.Read(buff[startOfSuffix:]) + + writeToHash(t, h, buff[endOfPrefix:startOfSuffix]) + testDigest := getSum(t, h, nil) + + if !bytes.Equal(testDigest, expectedDigest) { + t.Errorf("Write affected by data outside of input slice bounds; got %x, want %x", testDigest, expectedDigest) + } + }) + + // Test that multiple calls to Write is stateful + t.Run("StatefulWrite", func(t *testing.T) { + h := mh() + rng := newRandReader(t) + + prefix, suffix := make([]byte, h.BlockSize()), make([]byte, h.BlockSize()) + rng.Read(prefix) + rng.Read(suffix) + + // Write prefix then suffix sequentially and record resulting hash + writeToHash(t, h, prefix) + writeToHash(t, h, suffix) + serialSum := getSum(t, h, nil) + + h.Reset() + + // Write prefix and suffix at the same time and record resulting hash + writeToHash(t, h, append(prefix, suffix...)) + compositeSum := getSum(t, h, nil) + + // Check that sequential writing results in the same as writing all at once + if !bytes.Equal(compositeSum, serialSum) { + t.Errorf("two successive Write calls resulted in a different Sum than a single one; got %x, want %x", compositeSum, serialSum) + } + }) +} + +// Helper function for writing. Verifies that Write does not error. +func writeToHash(t *testing.T, h hash.Hash, p []byte) { + t.Helper() + + before := make([]byte, len(p)) + copy(before, p) + + n, err := h.Write(p) + if err != nil || n != len(p) { + t.Errorf("Write returned error; got (%v, %v), want (nil, %v)", err, n, len(p)) + } + + if !bytes.Equal(p, before) { + t.Errorf("Write modified input slice; got %x, want %x", p, before) + } +} + +// Helper function for getting Sum. Checks that Sum doesn't change hash state. +func getSum(t *testing.T, h hash.Hash, buff []byte) []byte { + t.Helper() + + testBuff := make([]byte, len(buff)) + copy(testBuff, buff) + + sum := h.Sum(buff) + testSum := h.Sum(testBuff) + + // Check that Sum doesn't change underlying hash state + if !bytes.Equal(sum, testSum) { + t.Errorf("successive calls to Sum yield different results; got %x, want %x", sum, testSum) + } + + return sum +} + +func newRandReader(t *testing.T) io.Reader { + seed := time.Now().UnixNano() + t.Logf("Deterministic RNG seed: 0x%x", seed) + return rand.New(rand.NewSource(seed)) +} diff --git a/src/crypto/md5/md5_test.go b/src/crypto/md5/md5_test.go index 851e7fb10d4..a5b661126dd 100644 --- a/src/crypto/md5/md5_test.go +++ b/src/crypto/md5/md5_test.go @@ -6,6 +6,7 @@ package md5 import ( "bytes" + "crypto/internal/cryptotest" "crypto/rand" "encoding" "fmt" @@ -225,6 +226,10 @@ func TestAllocations(t *testing.T) { } } +func TestMD5Hash(t *testing.T) { + cryptotest.TestHash(t, New) +} + var bench = New() var buf = make([]byte, 1024*1024*8+1) var sum = make([]byte, bench.Size()) diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go index 85ed1260915..634ab9de1ba 100644 --- a/src/crypto/sha1/sha1_test.go +++ b/src/crypto/sha1/sha1_test.go @@ -9,6 +9,7 @@ package sha1 import ( "bytes" "crypto/internal/boring" + "crypto/internal/cryptotest" "crypto/rand" "encoding" "fmt" @@ -234,6 +235,10 @@ func TestAllocations(t *testing.T) { } } +func TestSHA1Hash(t *testing.T) { + cryptotest.TestHash(t, New) +} + var bench = New() var buf = make([]byte, 8192) diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go index 7304678346b..d91f01e9ba3 100644 --- a/src/crypto/sha256/sha256_test.go +++ b/src/crypto/sha256/sha256_test.go @@ -9,6 +9,7 @@ package sha256 import ( "bytes" "crypto/internal/boring" + "crypto/internal/cryptotest" "crypto/rand" "encoding" "fmt" @@ -325,6 +326,15 @@ func TestCgo(t *testing.T) { h.Sum(nil) } +func TestSHA256Hash(t *testing.T) { + t.Run("SHA-224", func(t *testing.T) { + cryptotest.TestHash(t, New224) + }) + t.Run("SHA-256", func(t *testing.T) { + cryptotest.TestHash(t, New) + }) +} + var bench = New() var buf = make([]byte, 8192) diff --git a/src/crypto/sha512/sha512_test.go b/src/crypto/sha512/sha512_test.go index 921cdbb7bbd..a1ff571383e 100644 --- a/src/crypto/sha512/sha512_test.go +++ b/src/crypto/sha512/sha512_test.go @@ -9,6 +9,7 @@ package sha512 import ( "bytes" "crypto/internal/boring" + "crypto/internal/cryptotest" "crypto/rand" "encoding" "encoding/hex" @@ -909,6 +910,21 @@ func TestAllocations(t *testing.T) { } } +func TestSHA512Hash(t *testing.T) { + t.Run("SHA-384", func(t *testing.T) { + cryptotest.TestHash(t, New384) + }) + t.Run("SHA-512/224", func(t *testing.T) { + cryptotest.TestHash(t, New512_224) + }) + t.Run("SHA-512/256", func(t *testing.T) { + cryptotest.TestHash(t, New512_256) + }) + t.Run("SHA-512", func(t *testing.T) { + cryptotest.TestHash(t, New) + }) +} + var bench = New() var buf = make([]byte, 8192) diff --git a/src/crypto/tls/bogo_shim_test.go b/src/crypto/tls/bogo_shim_test.go index 5dc3b7c13ea..f481a5a40fb 100644 --- a/src/crypto/tls/bogo_shim_test.go +++ b/src/crypto/tls/bogo_shim_test.go @@ -33,8 +33,9 @@ var ( trustCert = flag.String("trust-cert", "", "") - minVersion = flag.Int("min-version", VersionSSL30, "") - maxVersion = flag.Int("max-version", VersionTLS13, "") + minVersion = flag.Int("min-version", VersionSSL30, "") + maxVersion = flag.Int("max-version", VersionTLS13, "") + expectVersion = flag.Int("expect-version", 0, "") noTLS13 = flag.Bool("no-tls13", false, "") @@ -53,6 +54,7 @@ var ( echConfigListB64 = flag.String("ech-config-list", "", "") expectECHAccepted = flag.Bool("expect-ech-accept", false, "") expectHRR = flag.Bool("expect-hrr", false, "") + expectNoHRR = flag.Bool("expect-no-hrr", false, "") expectedECHRetryConfigs = flag.String("expect-ech-retry-configs", "", "") expectNoECHRetryConfigs = flag.Bool("expect-no-ech-retry-configs", false, "") onInitialExpectECHAccepted = flag.Bool("on-initial-expect-ech-accept", false, "") @@ -74,6 +76,8 @@ var ( advertiseALPN = flag.String("advertise-alpn", "", "") expectALPN = flag.String("expect-alpn", "", "") + rejectALPN = flag.Bool("reject-alpn", false, "") + declineALPN = flag.Bool("decline-alpn", false, "") hostName = flag.String("host-name", "", "") @@ -125,6 +129,14 @@ func bogoShim() { } } + if *rejectALPN { + cfg.NextProtos = []string{"unnegotiableprotocol"} + } + + if *declineALPN { + cfg.NextProtos = []string{} + } + if *hostName != "" { cfg.ServerName = *hostName } @@ -252,7 +264,12 @@ func bogoShim() { if *expectALPN != "" && cs.NegotiatedProtocol != *expectALPN { log.Fatalf("unexpected protocol negotiated: want %q, got %q", *expectALPN, cs.NegotiatedProtocol) } - + if *expectVersion != 0 && cs.Version != uint16(*expectVersion) { + log.Fatalf("expected ssl version %q, got %q", uint16(*expectVersion), cs.Version) + } + if *declineALPN && cs.NegotiatedProtocol != "" { + log.Fatal("unexpected ALPN protocol") + } if *expectECHAccepted && !cs.ECHAccepted { log.Fatal("expected ECH to be accepted, but connection state shows it was not") } else if i == 0 && *onInitialExpectECHAccepted && !cs.ECHAccepted { @@ -267,6 +284,10 @@ func bogoShim() { log.Fatal("expected HRR but did not do it") } + if *expectNoHRR && cs.testingOnlyDidHRR { + log.Fatal("expected no HRR but did do it") + } + if *expectSessionMiss && cs.DidResume { log.Fatal("unexpected session resumption") } diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index 553d2dde01d..5025657590d 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -478,7 +478,9 @@ func (c *Conn) loadSession(hello *clientHelloMsg) ( } if c.quic != nil { - c.quicResumeSession(session) + if c.quic.enableSessionEvents { + c.quicResumeSession(session) + } // For 0-RTT, the cipher suite has to match exactly, and we need to be // offering the same ALPN. diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index 4570f5b05ed..501f9c6755f 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -283,7 +283,7 @@ func (test *clientTest) loadData() (flows [][]byte, err error) { } func (test *clientTest) run(t *testing.T, write bool) { - var clientConn, serverConn net.Conn + var clientConn net.Conn var recordingConn *recordingConn var childProcess *exec.Cmd var stdin opensslInput @@ -302,178 +302,138 @@ func (test *clientTest) run(t *testing.T, write bool) { } }() } else { - clientConn, serverConn = localPipe(t) + flows, err := test.loadData() + if err != nil { + t.Fatalf("failed to load data from %s: %v", test.dataPath(), err) + } + clientConn = &replayingConn{t: t, flows: flows, reading: false} } - doneChan := make(chan bool) - defer func() { - clientConn.Close() - <-doneChan - }() - go func() { - defer close(doneChan) + config := test.config + if config == nil { + config = testConfig + } + client := Client(clientConn, config) + defer client.Close() - config := test.config - if config == nil { - config = testConfig + if _, err := client.Write([]byte("hello\n")); err != nil { + t.Errorf("Client.Write failed: %s", err) + return + } + + for i := 1; i <= test.numRenegotiations; i++ { + // The initial handshake will generate a + // handshakeComplete signal which needs to be quashed. + if i == 1 && write { + <-stdout.handshakeComplete } - client := Client(clientConn, config) - defer client.Close() - if _, err := client.Write([]byte("hello\n")); err != nil { + // OpenSSL will try to interleave application data and + // a renegotiation if we send both concurrently. + // Therefore: ask OpensSSL to start a renegotiation, run + // a goroutine to call client.Read and thus process the + // renegotiation request, watch for OpenSSL's stdout to + // indicate that the handshake is complete and, + // finally, have OpenSSL write something to cause + // client.Read to complete. + if write { + stdin <- opensslRenegotiate + } + + signalChan := make(chan struct{}) + + go func() { + defer close(signalChan) + + buf := make([]byte, 256) + n, err := client.Read(buf) + + if test.checkRenegotiationError != nil { + newErr := test.checkRenegotiationError(i, err) + if err != nil && newErr == nil { + return + } + err = newErr + } + + if err != nil { + t.Errorf("Client.Read failed after renegotiation #%d: %s", i, err) + return + } + + buf = buf[:n] + if !bytes.Equal([]byte(opensslSentinel), buf) { + t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel) + } + + if expected := i + 1; client.handshakes != expected { + t.Errorf("client should have recorded %d handshakes, but believes that %d have occurred", expected, client.handshakes) + } + }() + + if write && test.renegotiationExpectedToFail != i { + <-stdout.handshakeComplete + stdin <- opensslSendSentinel + } + <-signalChan + } + + if test.sendKeyUpdate { + if write { + <-stdout.handshakeComplete + stdin <- opensslKeyUpdate + } + + doneRead := make(chan struct{}) + + go func() { + defer close(doneRead) + + buf := make([]byte, 256) + n, err := client.Read(buf) + + if err != nil { + t.Errorf("Client.Read failed after KeyUpdate: %s", err) + return + } + + buf = buf[:n] + if !bytes.Equal([]byte(opensslSentinel), buf) { + t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel) + } + }() + + if write { + // There's no real reason to wait for the client KeyUpdate to + // send data with the new server keys, except that s_server + // drops writes if they are sent at the wrong time. + <-stdout.readKeyUpdate + stdin <- opensslSendSentinel + } + <-doneRead + + if _, err := client.Write([]byte("hello again\n")); err != nil { t.Errorf("Client.Write failed: %s", err) return } + } - for i := 1; i <= test.numRenegotiations; i++ { - // The initial handshake will generate a - // handshakeComplete signal which needs to be quashed. - if i == 1 && write { - <-stdout.handshakeComplete - } - - // OpenSSL will try to interleave application data and - // a renegotiation if we send both concurrently. - // Therefore: ask OpensSSL to start a renegotiation, run - // a goroutine to call client.Read and thus process the - // renegotiation request, watch for OpenSSL's stdout to - // indicate that the handshake is complete and, - // finally, have OpenSSL write something to cause - // client.Read to complete. - if write { - stdin <- opensslRenegotiate - } - - signalChan := make(chan struct{}) - - go func() { - defer close(signalChan) - - buf := make([]byte, 256) - n, err := client.Read(buf) - - if test.checkRenegotiationError != nil { - newErr := test.checkRenegotiationError(i, err) - if err != nil && newErr == nil { - return - } - err = newErr - } - - if err != nil { - t.Errorf("Client.Read failed after renegotiation #%d: %s", i, err) - return - } - - buf = buf[:n] - if !bytes.Equal([]byte(opensslSentinel), buf) { - t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel) - } - - if expected := i + 1; client.handshakes != expected { - t.Errorf("client should have recorded %d handshakes, but believes that %d have occurred", expected, client.handshakes) - } - }() - - if write && test.renegotiationExpectedToFail != i { - <-stdout.handshakeComplete - stdin <- opensslSendSentinel - } - <-signalChan - } - - if test.sendKeyUpdate { - if write { - <-stdout.handshakeComplete - stdin <- opensslKeyUpdate - } - - doneRead := make(chan struct{}) - - go func() { - defer close(doneRead) - - buf := make([]byte, 256) - n, err := client.Read(buf) - - if err != nil { - t.Errorf("Client.Read failed after KeyUpdate: %s", err) - return - } - - buf = buf[:n] - if !bytes.Equal([]byte(opensslSentinel), buf) { - t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel) - } - }() - - if write { - // There's no real reason to wait for the client KeyUpdate to - // send data with the new server keys, except that s_server - // drops writes if they are sent at the wrong time. - <-stdout.readKeyUpdate - stdin <- opensslSendSentinel - } - <-doneRead - - if _, err := client.Write([]byte("hello again\n")); err != nil { - t.Errorf("Client.Write failed: %s", err) - return - } - } - - if test.validate != nil { - if err := test.validate(client.ConnectionState()); err != nil { - t.Errorf("validate callback returned error: %s", err) - } - } - - // If the server sent us an alert after our last flight, give it a - // chance to arrive. - if write && test.renegotiationExpectedToFail == 0 { - if err := peekError(client); err != nil { - t.Errorf("final Read returned an error: %s", err) - } - } - }() - - if !write { - flows, err := test.loadData() - if err != nil { - t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err) - } - for i, b := range flows { - if i%2 == 1 { - if *fast { - serverConn.SetWriteDeadline(time.Now().Add(1 * time.Second)) - } else { - serverConn.SetWriteDeadline(time.Now().Add(1 * time.Minute)) - } - serverConn.Write(b) - continue - } - bb := make([]byte, len(b)) - if *fast { - serverConn.SetReadDeadline(time.Now().Add(1 * time.Second)) - } else { - serverConn.SetReadDeadline(time.Now().Add(1 * time.Minute)) - } - _, err := io.ReadFull(serverConn, bb) - if err != nil { - t.Fatalf("%s, flow %d: %s", test.name, i+1, err) - } - if !bytes.Equal(b, bb) { - t.Fatalf("%s, flow %d: mismatch on read: got:%x want:%x", test.name, i+1, bb, b) - } + if test.validate != nil { + if err := test.validate(client.ConnectionState()); err != nil { + t.Errorf("validate callback returned error: %s", err) } } - <-doneChan - if !write { - serverConn.Close() + // If the server sent us an alert after our last flight, give it a + // chance to arrive. + if write && test.renegotiationExpectedToFail == 0 { + if err := peekError(client); err != nil { + t.Errorf("final Read returned an error: %s", err) + } } if write { + clientConn.Close() path := test.dataPath() out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 6744e713c9f..db5e35d9a46 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -900,7 +900,7 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { session.ageAdd = msg.ageAdd session.EarlyData = c.quic != nil && msg.maxEarlyData == 0xffffffff // RFC 9001, Section 4.6.1 session.ticket = msg.label - if c.quic != nil && c.quic.enableStoreSessionEvent { + if c.quic != nil && c.quic.enableSessionEvents { c.quicStoreSession(session) return nil } diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index 44bc8f1bb74..94d3d0f6dc8 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -21,6 +21,7 @@ import ( "os/exec" "path/filepath" "runtime" + "slices" "strings" "testing" "time" @@ -659,7 +660,7 @@ func (test *serverTest) loadData() (flows [][]byte, err error) { } func (test *serverTest) run(t *testing.T, write bool) { - var clientConn, serverConn net.Conn + var serverConn net.Conn var recordingConn *recordingConn var childProcess *exec.Cmd @@ -676,65 +677,33 @@ func (test *serverTest) run(t *testing.T, write bool) { } }() } else { - clientConn, serverConn = localPipe(t) + flows, err := test.loadData() + if err != nil { + t.Fatalf("Failed to load data from %s", test.dataPath()) + } + serverConn = &replayingConn{t: t, flows: flows, reading: true} } config := test.config if config == nil { config = testConfig } server := Server(serverConn, config) - connStateChan := make(chan ConnectionState, 1) - go func() { - _, err := server.Write([]byte("hello, world\n")) - if len(test.expectHandshakeErrorIncluding) > 0 { - if err == nil { - t.Errorf("Error expected, but no error returned") - } else if s := err.Error(); !strings.Contains(s, test.expectHandshakeErrorIncluding) { - t.Errorf("Error expected containing '%s' but got '%s'", test.expectHandshakeErrorIncluding, s) - } - } else { - if err != nil { - t.Logf("Error from Server.Write: '%s'", err) - } - } - server.Close() - serverConn.Close() - connStateChan <- server.ConnectionState() - }() - if !write { - flows, err := test.loadData() + _, err := server.Write([]byte("hello, world\n")) + if len(test.expectHandshakeErrorIncluding) > 0 { + if err == nil { + t.Errorf("Error expected, but no error returned") + } else if s := err.Error(); !strings.Contains(s, test.expectHandshakeErrorIncluding) { + t.Errorf("Error expected containing '%s' but got '%s'", test.expectHandshakeErrorIncluding, s) + } + } else { if err != nil { - t.Fatalf("%s: failed to load data from %s", test.name, test.dataPath()) + t.Logf("Error from Server.Write: '%s'", err) } - for i, b := range flows { - if i%2 == 0 { - if *fast { - clientConn.SetWriteDeadline(time.Now().Add(1 * time.Second)) - } else { - clientConn.SetWriteDeadline(time.Now().Add(1 * time.Minute)) - } - clientConn.Write(b) - continue - } - bb := make([]byte, len(b)) - if *fast { - clientConn.SetReadDeadline(time.Now().Add(1 * time.Second)) - } else { - clientConn.SetReadDeadline(time.Now().Add(1 * time.Minute)) - } - n, err := io.ReadFull(clientConn, bb) - if err != nil { - t.Fatalf("%s #%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", test.name, i+1, err, n, len(bb), bb[:n], b) - } - if !bytes.Equal(b, bb) { - t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i+1, bb, b) - } - } - clientConn.Close() } + server.Close() - connState := <-connStateChan + connState := server.ConnectionState() peerCerts := connState.PeerCertificates if len(peerCerts) == len(test.expectedPeerCerts) { for i, peerCert := range peerCerts { @@ -754,6 +723,7 @@ func (test *serverTest) run(t *testing.T, write bool) { } if write { + serverConn.Close() path := test.dataPath() out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { @@ -1330,37 +1300,14 @@ func benchmarkHandshakeServer(b *testing.B, version uint16, cipherSuite uint16, serverConn.Close() flows := serverConn.(*recordingConn).flows - feeder := make(chan struct{}) - clientConn, serverConn = localPipe(b) - - go func() { - for range feeder { - for i, f := range flows { - if i%2 == 0 { - clientConn.Write(f) - continue - } - ff := make([]byte, len(f)) - n, err := io.ReadFull(clientConn, ff) - if err != nil { - b.Errorf("#%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", i+1, err, n, len(ff), ff[:n], f) - } - if !bytes.Equal(f, ff) { - b.Errorf("#%d: mismatch on read: got:%x want:%x", i+1, ff, f) - } - } - } - }() - b.ResetTimer() for i := 0; i < b.N; i++ { - feeder <- struct{}{} - server := Server(serverConn, config) + replay := &replayingConn{t: b, flows: slices.Clone(flows), reading: true} + server := Server(replay, config) if err := server.Handshake(); err != nil { b.Fatalf("handshake failed: %v", err) } } - close(feeder) } func BenchmarkHandshakeServer(b *testing.B) { diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index f24c2671acd..503a732e057 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -377,7 +377,7 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { continue } - if c.quic != nil { + if c.quic != nil && c.quic.enableSessionEvents { if err := c.quicResumeSession(sessionState); err != nil { return err } diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go index 57fc761dbb8..bc3d23d5adc 100644 --- a/src/crypto/tls/handshake_test.go +++ b/src/crypto/tls/handshake_test.go @@ -6,6 +6,7 @@ package tls import ( "bufio" + "bytes" "crypto/ed25519" "crypto/x509" "encoding/hex" @@ -42,7 +43,6 @@ import ( var ( update = flag.Bool("update", false, "update golden files on failure") - fast = flag.Bool("fast", false, "impose a quick, possibly flaky timeout on recorded tests") keyFile = flag.String("keylog", "", "destination file for KeyLogWriter") bogoMode = flag.Bool("bogo-mode", false, "Enabled bogo shim mode, ignore everything else") bogoFilter = flag.String("bogo-filter", "", "BoGo test filter") @@ -223,6 +223,76 @@ func parseTestData(r io.Reader) (flows [][]byte, err error) { return flows, nil } +// replayingConn is a net.Conn that replays flows recorded by recordingConn. +type replayingConn struct { + t testing.TB + sync.Mutex + flows [][]byte + reading bool +} + +var _ net.Conn = (*replayingConn)(nil) + +func (r *replayingConn) Read(b []byte) (n int, err error) { + r.Lock() + defer r.Unlock() + + if !r.reading { + r.t.Errorf("expected write, got read") + return 0, fmt.Errorf("recording expected write, got read") + } + + n = copy(b, r.flows[0]) + r.flows[0] = r.flows[0][n:] + if len(r.flows[0]) == 0 { + r.flows = r.flows[1:] + if len(r.flows) == 0 { + return n, io.EOF + } else { + r.reading = false + } + } + return n, nil +} + +func (r *replayingConn) Write(b []byte) (n int, err error) { + r.Lock() + defer r.Unlock() + + if r.reading { + r.t.Errorf("expected read, got write") + return 0, fmt.Errorf("recording expected read, got write") + } + + if !bytes.HasPrefix(r.flows[0], b) { + r.t.Errorf("write mismatch: expected %x, got %x", r.flows[0], b) + return 0, fmt.Errorf("write mismatch") + } + r.flows[0] = r.flows[0][len(b):] + if len(r.flows[0]) == 0 { + r.flows = r.flows[1:] + r.reading = true + } + return len(b), nil +} + +func (r *replayingConn) Close() error { + r.Lock() + defer r.Unlock() + + if len(r.flows) > 0 { + r.t.Errorf("closed with unfinished flows") + return fmt.Errorf("unexpected close") + } + return nil +} + +func (r *replayingConn) LocalAddr() net.Addr { return nil } +func (r *replayingConn) RemoteAddr() net.Addr { return nil } +func (r *replayingConn) SetDeadline(t time.Time) error { return nil } +func (r *replayingConn) SetReadDeadline(t time.Time) error { return nil } +func (r *replayingConn) SetWriteDeadline(t time.Time) error { return nil } + // tempFile creates a temp file containing contents and returns its path. func tempFile(contents string) string { file, err := os.CreateTemp("", "go-tls-test") diff --git a/src/crypto/tls/quic.go b/src/crypto/tls/quic.go index 8e722c6a590..9dd6168b623 100644 --- a/src/crypto/tls/quic.go +++ b/src/crypto/tls/quic.go @@ -50,12 +50,12 @@ type QUICConn struct { type QUICConfig struct { TLSConfig *Config - // EnableStoreSessionEvent may be set to true to enable the - // [QUICStoreSession] event for client connections. + // EnableSessionEvents may be set to true to enable the + // [QUICStoreSession] and [QUICResumeSession] events for client connections. // When this event is enabled, sessions are not automatically // stored in the client session cache. // The application should use [QUICConn.StoreSession] to store sessions. - EnableStoreSessionEvent bool + EnableSessionEvents bool } // A QUICEventKind is a type of operation on a QUIC connection. @@ -113,7 +113,7 @@ const ( // QUICStoreSession indicates that the server has provided state permitting // the client to resume the session. // [QUICEvent.SessionState] is set. - // The application should use [QUICConn.Store] session to store the [SessionState]. + // The application should use [QUICConn.StoreSession] session to store the [SessionState]. // The application may modify the [SessionState] before storing it. // This event only occurs on client connections. QUICStoreSession @@ -165,7 +165,7 @@ type quicState struct { transportParams []byte // to send to the peer - enableStoreSessionEvent bool + enableSessionEvents bool } // QUICClient returns a new TLS client side connection using QUICTransport as the @@ -186,9 +186,9 @@ func QUICServer(config *QUICConfig) *QUICConn { func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn { conn.quic = &quicState{ - signalc: make(chan struct{}), - blockedc: make(chan struct{}), - enableStoreSessionEvent: config.EnableStoreSessionEvent, + signalc: make(chan struct{}), + blockedc: make(chan struct{}), + enableSessionEvents: config.EnableSessionEvents, } conn.quic.events = conn.quic.eventArr[:0] return &QUICConn{ diff --git a/src/crypto/tls/quic_test.go b/src/crypto/tls/quic_test.go index 5a6f66e4dea..1bb2e55bddc 100644 --- a/src/crypto/tls/quic_test.go +++ b/src/crypto/tls/quic_test.go @@ -24,22 +24,22 @@ type testQUICConn struct { complete bool } -func newTestQUICClient(t *testing.T, config *Config) *testQUICConn { - q := &testQUICConn{t: t} - q.conn = QUICClient(&QUICConfig{ - TLSConfig: config, - }) +func newTestQUICClient(t *testing.T, config *QUICConfig) *testQUICConn { + q := &testQUICConn{ + t: t, + conn: QUICClient(config), + } t.Cleanup(func() { q.conn.Close() }) return q } -func newTestQUICServer(t *testing.T, config *Config) *testQUICConn { - q := &testQUICConn{t: t} - q.conn = QUICServer(&QUICConfig{ - TLSConfig: config, - }) +func newTestQUICServer(t *testing.T, config *QUICConfig) *testQUICConn { + q := &testQUICConn{ + t: t, + conn: QUICServer(config), + } t.Cleanup(func() { q.conn.Close() }) @@ -140,6 +140,11 @@ func runTestQUICConnection(ctx context.Context, cli, srv *testQUICConn, onEvent return err } } + case QUICStoreSession: + if a != cli { + return errors.New("unexpected QUICStoreSession event received by server") + } + a.conn.StoreSession(e.SessionState) case QUICResumeSession: if a.onResumeSession != nil { a.onResumeSession(e.SessionState) @@ -154,8 +159,8 @@ func runTestQUICConnection(ctx context.Context, cli, srv *testQUICConn, onEvent } func TestQUICConnection(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) @@ -196,13 +201,13 @@ func TestQUICConnection(t *testing.T) { } func TestQUICSessionResumption(t *testing.T) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) - clientConfig.ServerName = "example.go.dev" + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1) + clientConfig.TLSConfig.ServerName = "example.go.dev" - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, clientConfig) cli.conn.SetTransportParameters(nil) @@ -228,13 +233,13 @@ func TestQUICSessionResumption(t *testing.T) { } func TestQUICFragmentaryData(t *testing.T) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) - clientConfig.ServerName = "example.go.dev" + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1) + clientConfig.TLSConfig.ServerName = "example.go.dev" - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, clientConfig) cli.conn.SetTransportParameters(nil) @@ -260,8 +265,8 @@ func TestQUICFragmentaryData(t *testing.T) { func TestQUICPostHandshakeClientAuthentication(t *testing.T) { // RFC 9001, Section 4.4. - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) srv := newTestQUICServer(t, config) @@ -288,8 +293,8 @@ func TestQUICPostHandshakeClientAuthentication(t *testing.T) { func TestQUICPostHandshakeKeyUpdate(t *testing.T) { // RFC 9001, Section 6. - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) srv := newTestQUICServer(t, config) @@ -312,8 +317,8 @@ func TestQUICPostHandshakeKeyUpdate(t *testing.T) { } func TestQUICPostHandshakeMessageTooLarge(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) srv := newTestQUICServer(t, config) @@ -334,13 +339,13 @@ func TestQUICPostHandshakeMessageTooLarge(t *testing.T) { } func TestQUICHandshakeError(t *testing.T) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.InsecureSkipVerify = false - clientConfig.ServerName = "name" + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.InsecureSkipVerify = false + clientConfig.TLSConfig.ServerName = "name" - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, clientConfig) cli.conn.SetTransportParameters(nil) @@ -360,9 +365,9 @@ func TestQUICHandshakeError(t *testing.T) { // and that it reports the application protocol as soon as it has been // negotiated. func TestQUICConnectionState(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 - config.NextProtos = []string{"h3"} + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 + config.TLSConfig.NextProtos = []string{"h3"} cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) srv := newTestQUICServer(t, config) @@ -391,10 +396,10 @@ func TestQUICStartContextPropagation(t *testing.T) { const key = "key" const value = "value" ctx := context.WithValue(context.Background(), key, value) - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 calls := 0 - config.GetConfigForClient = func(info *ClientHelloInfo) (*Config, error) { + config.TLSConfig.GetConfigForClient = func(info *ClientHelloInfo) (*Config, error) { calls++ got, _ := info.Context().Value(key).(string) if got != value { @@ -415,13 +420,13 @@ func TestQUICStartContextPropagation(t *testing.T) { } func TestQUICDelayedTransportParameters(t *testing.T) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) - clientConfig.ServerName = "example.go.dev" + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1) + clientConfig.TLSConfig.ServerName = "example.go.dev" - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.TLSConfig.MinVersion = VersionTLS13 cliParams := "client params" srvParams := "server params" @@ -449,8 +454,8 @@ func TestQUICDelayedTransportParameters(t *testing.T) { } func TestQUICEmptyTransportParameters(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) @@ -475,8 +480,8 @@ func TestQUICEmptyTransportParameters(t *testing.T) { } func TestQUICCanceledWaitingForData(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.SetTransportParameters(nil) cli.conn.Start(context.Background()) @@ -489,8 +494,8 @@ func TestQUICCanceledWaitingForData(t *testing.T) { } func TestQUICCanceledWaitingForTransportParams(t *testing.T) { - config := testConfig.Clone() - config.MinVersion = VersionTLS13 + config := &QUICConfig{TLSConfig: testConfig.Clone()} + config.TLSConfig.MinVersion = VersionTLS13 cli := newTestQUICClient(t, config) cli.conn.Start(context.Background()) for cli.conn.NextEvent().Kind != QUICTransportParametersRequired { @@ -502,15 +507,15 @@ func TestQUICCanceledWaitingForTransportParams(t *testing.T) { } func TestQUICEarlyData(t *testing.T) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) - clientConfig.ServerName = "example.go.dev" - clientConfig.NextProtos = []string{"h3"} + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1) + clientConfig.TLSConfig.ServerName = "example.go.dev" + clientConfig.TLSConfig.NextProtos = []string{"h3"} - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 - serverConfig.NextProtos = []string{"h3"} + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.TLSConfig.MinVersion = VersionTLS13 + serverConfig.TLSConfig.NextProtos = []string{"h3"} cli := newTestQUICClient(t, clientConfig) cli.conn.SetTransportParameters(nil) @@ -528,7 +533,14 @@ func TestQUICEarlyData(t *testing.T) { cli2.conn.SetTransportParameters(nil) srv2 := newTestQUICServer(t, serverConfig) srv2.conn.SetTransportParameters(nil) - if err := runTestQUICConnection(context.Background(), cli2, srv2, nil); err != nil { + onEvent := func(e QUICEvent, src, dst *testQUICConn) bool { + switch e.Kind { + case QUICStoreSession, QUICResumeSession: + t.Errorf("with EnableSessionEvents=false, got unexpected event %v", e.Kind) + } + return false + } + if err := runTestQUICConnection(context.Background(), cli2, srv2, onEvent); err != nil { t.Fatalf("error during second connection handshake: %v", err) } if !cli2.conn.ConnectionState().DidResume { @@ -557,15 +569,17 @@ func TestQUICEarlyDataDeclined(t *testing.T) { } func testQUICEarlyDataDeclined(t *testing.T, server bool) { - clientConfig := testConfig.Clone() - clientConfig.MinVersion = VersionTLS13 - clientConfig.ClientSessionCache = NewLRUClientSessionCache(1) - clientConfig.ServerName = "example.go.dev" - clientConfig.NextProtos = []string{"h3"} + clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + clientConfig.EnableSessionEvents = true + clientConfig.TLSConfig.MinVersion = VersionTLS13 + clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1) + clientConfig.TLSConfig.ServerName = "example.go.dev" + clientConfig.TLSConfig.NextProtos = []string{"h3"} - serverConfig := testConfig.Clone() - serverConfig.MinVersion = VersionTLS13 - serverConfig.NextProtos = []string{"h3"} + serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()} + serverConfig.EnableSessionEvents = true + serverConfig.TLSConfig.MinVersion = VersionTLS13 + serverConfig.TLSConfig.NextProtos = []string{"h3"} cli := newTestQUICClient(t, clientConfig) cli.conn.SetTransportParameters(nil) diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go index da310bfb12a..d0892e80fc2 100644 --- a/src/database/sql/driver/driver.go +++ b/src/database/sql/driver/driver.go @@ -415,7 +415,7 @@ type NamedValueChecker interface { type ColumnConverter interface { // ColumnConverter returns a ValueConverter for the provided // column index. If the type of a specific column isn't known - // or shouldn't be handled specially, DefaultValueConverter + // or shouldn't be handled specially, [DefaultParameterConverter] // can be returned. ColumnConverter(idx int) ValueConverter } diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index efceecdf82b..f8205704e38 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -73,8 +73,7 @@ import ( // use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal // reuses the existing map, keeping existing entries. Unmarshal then stores // key-value pairs from the JSON object into the map. The map's key type must -// either be any string type, an integer, implement [json.Unmarshaler], or -// implement [encoding.TextUnmarshaler]. +// either be any string type, an integer, or implement [encoding.TextUnmarshaler]. // // If the JSON-encoded data contain a syntax error, Unmarshal returns a [SyntaxError]. // diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index cb28feb279f..7bee1a6805f 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -141,7 +141,7 @@ import ( // are sorted and used as JSON object keys by applying the following rules, // subject to the UTF-8 coercion described for string values above: // - keys of any string type are used directly -// - [encoding.TextMarshalers] are marshaled +// - keys that implement [encoding.TextMarshaler] are marshaled // - integer keys are converted to strings // // Pointer values encode as the value pointed to. diff --git a/src/go.mod b/src/go.mod index b38dee7e6dd..789f5aaa1d3 100644 --- a/src/go.mod +++ b/src/go.mod @@ -8,6 +8,6 @@ require ( ) require ( - golang.org/x/sys v0.21.0 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index c5eab7d4b2f..a75ea98c731 100644 --- a/src/go.sum +++ b/src/go.sum @@ -2,7 +2,7 @@ golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a h1:37MIv+iGfwMYzWJECGy golang.org/x/crypto v0.23.1-0.20240603234054-0b431c7de36a/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/net v0.25.1-0.20240603202750-6249541f2a6c h1:CR/7/SLUhIJw6g675eeoDiwggElO2MV9rGkNYjqi8GM= golang.org/x/net v0.25.1-0.20240603202750-6249541f2a6c/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 84b0096c770..9146cae492e 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -642,6 +642,9 @@ var depsRules = ` FMT < internal/txtar; + CRYPTO-MATH, testing + < crypto/internal/cryptotest; + # v2 execution trace parser. FMT < internal/trace/event; diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index c99c9a9550d..3f459d38830 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -1102,3 +1102,32 @@ func _() { conf := Config{GoVersion: "go1.17"} mustTypecheck(src, &conf, nil) } + +func TestIssue68334(t *testing.T) { + const src = ` +package p + +func f(x int) { + for i, j := range x { + _, _ = i, j + } + var a, b int + for a, b = range x { + _, _ = a, b + } +} +` + + got := "" + conf := Config{ + GoVersion: "go1.21", // #68334 requires GoVersion <= 1.21 + Error: func(err error) { got += err.Error() + "\n" }, // #68334 requires Error != nil + } + typecheck(src, &conf, nil) // do not crash + + want := "p:5:20: cannot range over x (variable of type int): requires go1.22 or later\n" + + "p:9:19: cannot range over x (variable of type int): requires go1.22 or later\n" + if got != want { + t.Errorf("got: %s want: %s", got, want) + } +} diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 215b20160d8..c9f7a4f929a 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -922,14 +922,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { // initialize lhs iteration variable, if any typ := rhs[i] - if typ == nil { + if typ == nil || typ == Typ[Invalid] { + // typ == Typ[Invalid] can happen if allowVersion fails. obj.typ = Typ[Invalid] obj.used = true // don't complain about unused variable continue } if rangeOverInt { - assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) check.initVar(obj, &x, "range clause") } else { var y operand @@ -959,12 +960,12 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) { // assign to lhs iteration variable, if any typ := rhs[i] - if typ == nil { + if typ == nil || typ == Typ[Invalid] { continue } if rangeOverInt { - assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt) + assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt) check.assignVar(lhs, nil, &x, "range clause") // If the assignment succeeded, if x was untyped before, it now // has a type inferred via the assignment. It must be an integer. diff --git a/src/go/types/typeparam.go b/src/go/types/typeparam.go index 58a02de8602..789b63d7a15 100644 --- a/src/go/types/typeparam.go +++ b/src/go/types/typeparam.go @@ -28,8 +28,8 @@ type TypeParam struct { } // NewTypeParam returns a new TypeParam. Type parameters may be set on a Named -// or Signature type by calling SetTypeParams. Setting a type parameter on more -// than one type will result in a panic. +// type by calling SetTypeParams. Setting a type parameter on more than one type +// will result in a panic. // // The constraint argument can be nil, and set later via SetConstraint. If the // constraint is non-nil, it must be fully defined. diff --git a/src/internal/coverage/defs.go b/src/internal/coverage/defs.go index 340ac956355..1ef47638e62 100644 --- a/src/internal/coverage/defs.go +++ b/src/internal/coverage/defs.go @@ -267,7 +267,7 @@ func (cm CounterGranularity) String() string { // by the Go command and by the coverage runtime. const MetaFilesFileName = "metafiles.txt" -// MetaFilePaths contains information generated by the Go command and +// MetaFileCollection contains information generated by the Go command and // the read in by coverage test support functions within an executing // "go test -cover" binary. type MetaFileCollection struct { diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index 4c084635fbe..a802ac9c370 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -25,7 +25,7 @@ type Info struct { // Note: After adding entries to this table, update the list in doc/godebug.md as well. // (Otherwise the test in this package will fail.) var All = []Info{ - {Name: "asynctimerchan", Package: "time", Changed: 23, Old: "1", Opaque: true}, + {Name: "asynctimerchan", Package: "time", Changed: 23, Old: "1"}, {Name: "execerrdot", Package: "os/exec"}, {Name: "gocachehash", Package: "cmd/go"}, {Name: "gocachetest", Package: "cmd/go"}, diff --git a/src/io/pipe_test.go b/src/io/pipe_test.go index a8c4e306cc3..fcf94d52d21 100644 --- a/src/io/pipe_test.go +++ b/src/io/pipe_test.go @@ -421,3 +421,21 @@ func sortBytesInGroups(b []byte, n int) []byte { slices.SortFunc(groups, bytes.Compare) return bytes.Join(groups, nil) } + +var ( + rSink *PipeReader + wSink *PipeWriter +) + +func TestPipeAllocations(t *testing.T) { + numAllocs := testing.AllocsPerRun(10, func() { + rSink, wSink = Pipe() + }) + + // go.dev/cl/473535 claimed Pipe() should only do 2 allocations, + // plus the 2 escaping to heap for simulating real world usages. + expectedAllocs := 4 + if int(numAllocs) > expectedAllocs { + t.Fatalf("too many allocations for io.Pipe() call: %f", numAllocs) + } +} diff --git a/src/iter/iter.go b/src/iter/iter.go index 4ea919b0724..14fd8f8115f 100644 --- a/src/iter/iter.go +++ b/src/iter/iter.go @@ -31,7 +31,7 @@ element in the sequence, false if it should stop. Iterator functions are most often called by a range loop, as in: func PrintAll[V any](seq iter.Seq[V]) { - for _, v := range seq { + for v := range seq { fmt.Println(v) } } @@ -92,9 +92,8 @@ sequence only once. These “single-use iterators” typically report values from a data stream that cannot be rewound to start over. Calling the iterator again after stopping early may continue the stream, but calling it again after the sequence is finished will yield -no values at all, immediately returning true. Doc comments for -functions or methods that return single-use iterators should document -this fact: +no values at all. Doc comments for functions or methods that return +single-use iterators should document this fact: // Lines returns an iterator over lines read from r. // It returns a single-use iterator. @@ -119,17 +118,24 @@ For example: // Pairs returns an iterator over successive pairs of values from seq. func Pairs[V any](seq iter.Seq[V]) iter.Seq2[V, V] { - return func(yield func(V, V) bool) bool { + return func(yield func(V, V) bool) { next, stop := iter.Pull(seq) defer stop() - v1, ok1 := next() - v2, ok2 := next() - for ok1 || ok2 { + for { + v1, ok1 := next() + if !ok1 { + return + } + v2, ok2 := next() + // If ok2 is false, v2 should be the + // zero value; yield one last pair. if !yield(v1, v2) { - return false + return + } + if !ok2 { + return } } - return true } } diff --git a/src/log/slog/internal/buffer/buffer.go b/src/log/slog/internal/buffer/buffer.go index 310ec37d4a1..110c6281ab2 100644 --- a/src/log/slog/internal/buffer/buffer.go +++ b/src/log/slog/internal/buffer/buffer.go @@ -7,7 +7,10 @@ package buffer import "sync" -// buffer adapted from go/src/fmt/print.go +// Buffer is a byte buffer. +// +// This implementation is adapted from the unexported type buffer +// in go/src/fmt/print.go. type Buffer []byte // Having an initial size gives a dramatic speedup. diff --git a/src/math/floor_asm.go b/src/math/floor_asm.go index 5cb45f5a7e8..fb419d6da2f 100644 --- a/src/math/floor_asm.go +++ b/src/math/floor_asm.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 386 || amd64 || arm64 || ppc64 || ppc64le || riscv64 || s390x || wasm +//go:build 386 || amd64 || arm64 || ppc64 || ppc64le || s390x || wasm package math diff --git a/src/math/floor_noasm.go b/src/math/floor_noasm.go index 6754ca8fc80..5641c7ea0a4 100644 --- a/src/math/floor_noasm.go +++ b/src/math/floor_noasm.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 !386 && !amd64 && !arm64 && !ppc64 && !ppc64le && !riscv64 && !s390x && !wasm +//go:build !386 && !amd64 && !arm64 && !ppc64 && !ppc64le && !s390x && !wasm package math diff --git a/src/math/floor_riscv64.s b/src/math/floor_riscv64.s deleted file mode 100644 index 62ce963781a..00000000000 --- a/src/math/floor_riscv64.s +++ /dev/null @@ -1,41 +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. - -#include "textflag.h" - -#define PosInf 0x7FF0000000000000 - -// The rounding mode of RISC-V is different from Go spec. - -#define ROUNDFN(NAME, MODE) \ -TEXT NAME(SB),NOSPLIT,$0; \ - MOVD x+0(FP), F0; \ - /* whether x is NaN */; \ - FEQD F0, F0, X6; \ - BNEZ X6, 3(PC); \ - /* return NaN if x is NaN */; \ - MOVD F0, ret+8(FP); \ - RET; \ - MOV $PosInf, X6; \ - FMVDX X6, F1; \ - FABSD F0, F2; \ - /* if abs(x) > +Inf, return Inf instead of round(x) */; \ - FLTD F1, F2, X6; \ - /* Inf should keep same signed with x then return */; \ - BEQZ X6, 3(PC); \ - FCVTLD.MODE F0, X6; \ - FCVTDL X6, F1; \ - /* rounding will drop signed bit in RISCV, restore it */; \ - FSGNJD F0, F1, F0; \ - MOVD F0, ret+8(FP); \ - RET - -// func archFloor(x float64) float64 -ROUNDFN(·archFloor, RDN) - -// func archCeil(x float64) float64 -ROUNDFN(·archCeil, RUP) - -// func archTrunc(x float64) float64 -ROUNDFN(·archTrunc, RTZ) diff --git a/src/net/http/filetransport.go b/src/net/http/filetransport.go index 7384b22fbe9..b08bae63487 100644 --- a/src/net/http/filetransport.go +++ b/src/net/http/filetransport.go @@ -35,7 +35,7 @@ func NewFileTransport(fs FileSystem) RoundTripper { // NewFileTransportFS returns a new [RoundTripper], serving the provided // file system fsys. The returned RoundTripper ignores the URL host in its // incoming requests, as well as most other properties of the -// request. +// request. The files provided by fsys must implement [io.Seeker]. // // The typical use case for NewFileTransportFS is to register the "file" // protocol with a [Transport], as in: diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 70653550f02..3a716fbd2cc 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -819,6 +819,7 @@ func ServeFile(w ResponseWriter, r *Request, name string) { // ServeFileFS replies to the request with the contents // of the named file or directory from the file system fsys. +// The files provided by fsys must implement [io.Seeker]. // // If the provided name is constructed from user input, it should be // sanitized before calling [ServeFileFS]. @@ -965,6 +966,7 @@ func FileServer(root FileSystem) Handler { // FileServerFS returns a handler that serves HTTP requests // with the contents of the file system fsys. +// The files provided by fsys must implement [io.Seeker]. // // As a special case, the returned file server redirects any request // ending in "/index.html" to the same path, without the final diff --git a/src/net/http/request.go b/src/net/http/request.go index 456615a79ad..ad1b5a620b0 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -377,6 +377,8 @@ func (r *Request) WithContext(ctx context.Context) *Request { // Clone returns a deep copy of r with its context changed to ctx. // The provided ctx must be non-nil. // +// Clone only makes a shallow copy of the Body field. +// // For an outgoing client request, the context controls the entire // lifetime of a request and its response: obtaining a connection, // sending the request, and reading the response headers and body. diff --git a/src/net/http/routing_tree_test.go b/src/net/http/routing_tree_test.go index 7de6b195072..f3f216357d9 100644 --- a/src/net/http/routing_tree_test.go +++ b/src/net/http/routing_tree_test.go @@ -7,6 +7,7 @@ package http import ( "fmt" "io" + "maps" "strings" "testing" @@ -261,9 +262,7 @@ func TestMatchingMethods(t *testing.T) { t.Run(test.name, func(t *testing.T) { ms := map[string]bool{} test.tree.matchingMethods(test.host, test.path, ms) - keys := mapKeys(ms) - slices.Sort(keys) - got := strings.Join(keys, ",") + got := strings.Join(slices.Sorted(maps.Keys(ms)), ",") if got != test.want { t.Errorf("got %s, want %s", got, test.want) } diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 3ec10c2f14d..b2858ba8f2b 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -613,6 +613,22 @@ func TestMuxNoSlashRedirectWithTrailingSlash(t *testing.T) { } } +// Test that we don't attempt trailing-slash response 405 on a path that already has +// a trailing slash. +// See issue #67657. +func TestMuxNoSlash405WithTrailingSlash(t *testing.T) { + mux := NewServeMux() + mux.HandleFunc("GET /{x}/", func(w ResponseWriter, r *Request) { + fmt.Fprintln(w, "ok") + }) + w := httptest.NewRecorder() + req, _ := NewRequest("GET", "/", nil) + mux.ServeHTTP(w, req) + if g, w := w.Code, 404; g != w { + t.Errorf("got %d, want %d", g, w) + } +} + func TestShouldRedirectConcurrency(t *testing.T) { run(t, testShouldRedirectConcurrency) } func testShouldRedirectConcurrency(t *testing.T, mode testMode) { mux := NewServeMux() diff --git a/src/net/http/server.go b/src/net/http/server.go index a5e98f1d957..1ff72a04550 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -16,6 +16,7 @@ import ( "internal/godebug" "io" "log" + "maps" "math/rand" "net" "net/textproto" @@ -2721,19 +2722,10 @@ func (mux *ServeMux) matchingMethods(host, path string) []string { ms := map[string]bool{} mux.tree.matchingMethods(host, path, ms) // matchOrRedirect will try appending a trailing slash if there is no match. - mux.tree.matchingMethods(host, path+"/", ms) - methods := mapKeys(ms) - slices.Sort(methods) - return methods -} - -// TODO(jba): replace with maps.Keys when it is defined. -func mapKeys[K comparable, V any](m map[K]V) []K { - var ks []K - for k := range m { - ks = append(ks, k) + if !strings.HasSuffix(path, "/") { + mux.tree.matchingMethods(host, path+"/", ms) } - return ks + return slices.Sorted(maps.Keys(ms)) } // ServeHTTP dispatches the request to the handler whose diff --git a/src/net/netip/export_test.go b/src/net/netip/export_test.go index 4febcad8936..b2fae1aa47e 100644 --- a/src/net/netip/export_test.go +++ b/src/net/netip/export_test.go @@ -16,6 +16,10 @@ type Uint128 = uint128 type AddrDetail = addrDetail +func MakeAddrDetail(isV6 bool, zoneV6 string) AddrDetail { + return AddrDetail{isV6: isV6, zoneV6: zoneV6} +} + func Mk128(hi, lo uint64) Uint128 { return uint128{hi, lo} } diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 57063eeb718..a1e93cb29bf 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -59,8 +59,8 @@ type Addr struct { // addrDetail represents the details of an Addr, like address family and IPv6 zone. type addrDetail struct { - IsV6 bool // IPv4 is false, IPv6 is true. - ZoneV6 string // != "" only if IsV6 is true. + isV6 bool // IPv4 is false, IPv6 is true. + zoneV6 string // != "" only if IsV6 is true. } // z0, z4, and z6noz are sentinel Addr.z values. @@ -68,7 +68,7 @@ type addrDetail struct { var ( z0 unique.Handle[addrDetail] z4 = unique.Make(addrDetail{}) - z6noz = unique.Make(addrDetail{IsV6: true}) + z6noz = unique.Make(addrDetail{isV6: true}) ) // IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast @@ -410,7 +410,7 @@ func (ip Addr) Zone() string { if ip.z == z0 { return "" } - return ip.z.Value().ZoneV6 + return ip.z.Value().zoneV6 } // Compare returns an integer comparing two IPs. @@ -495,7 +495,7 @@ func (ip Addr) WithZone(zone string) Addr { ip.z = z6noz return ip } - ip.z = unique.Make(addrDetail{IsV6: true, ZoneV6: zone}) + ip.z = unique.Make(addrDetail{isV6: true, zoneV6: zone}) return ip } diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go index ad0e7542082..e1a0a83f645 100644 --- a/src/net/netip/netip_test.go +++ b/src/net/netip/netip_test.go @@ -112,18 +112,18 @@ func TestParseAddr(t *testing.T) { // IPv6 with a zone specifier. { in: "fd7a:115c:a1e0:ab12:4843:cd96:626b:430b%eth0", - ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth0"})), + ip: MkAddr(Mk128(0xfd7a115ca1e0ab12, 0x4843cd96626b430b), unique.Make(MakeAddrDetail(true, "eth0"))), }, // IPv6 with dotted decimal and zone specifier. { in: "1:2::ffff:192.168.140.255%eth1", - ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"})), + ip: MkAddr(Mk128(0x0001000200000000, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))), str: "1:2::ffff:c0a8:8cff%eth1", }, // 4-in-6 with zone { in: "::ffff:192.168.140.255%eth1", - ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"})), + ip: MkAddr(Mk128(0, 0x0000ffffc0a88cff), unique.Make(MakeAddrDetail(true, "eth1"))), str: "::ffff:192.168.140.255%eth1", }, // IPv6 with capital letters. @@ -893,6 +893,15 @@ func TestAddrLessCompare(t *testing.T) { {mustIP("::1%a"), mustIP("::1%b"), true}, {mustIP("::1%a"), mustIP("::1%a"), false}, {mustIP("::1%b"), mustIP("::1%a"), false}, + + // For Issue 68113, verify that an IPv4 address and a + // v4-mapped-IPv6 address differing only in their zone + // pointer are unequal via all three of + // ==/Compare/reflect.DeepEqual. In Go 1.22 and + // earlier, these were accidentally equal via + // DeepEqual due to their zone pointers (z) differing + // but pointing to identical structures. + {mustIP("::ffff:11.1.1.12"), mustIP("11.1.1.12"), false}, } for _, tt := range tests { got := tt.a.Less(tt.b) @@ -920,6 +929,12 @@ func TestAddrLessCompare(t *testing.T) { t.Errorf("Less(%q, %q) was correctly %v, but so was Less(%q, %q)", tt.a, tt.b, got, tt.b, tt.a) } } + + // Also check reflect.DeepEqual. See issue 68113. + deepEq := reflect.DeepEqual(tt.a, tt.b) + if (cmp == 0) != deepEq { + t.Errorf("%q and %q differ in == (%v) vs reflect.DeepEqual (%v)", tt.a, tt.b, cmp == 0, deepEq) + } } // And just sort. @@ -1723,7 +1738,7 @@ var parseBenchInputs = []struct { } func BenchmarkParseAddr(b *testing.B) { - sinkInternValue = unique.Make(AddrDetail{IsV6: true, ZoneV6: "eth1"}) // Pin to not benchmark the intern package + sinkInternValue = unique.Make(MakeAddrDetail(true, "eth1")) // Pin to not benchmark the intern package for _, test := range parseBenchInputs { b.Run(test.name, func(b *testing.B) { b.ReportAllocs() diff --git a/src/os/error.go b/src/os/error.go index 5a824a9e0e1..284b9e992cd 100644 --- a/src/os/error.go +++ b/src/os/error.go @@ -71,7 +71,7 @@ func NewSyscallError(syscall string, err error) error { return &SyscallError{syscall, err} } -// IsExist returns a boolean indicating whether the error is known to report +// IsExist returns a boolean indicating whether its argument is known to report // that a file or directory already exists. It is satisfied by [ErrExist] as // well as some syscall errors. // @@ -81,7 +81,7 @@ func IsExist(err error) bool { return underlyingErrorIs(err, ErrExist) } -// IsNotExist returns a boolean indicating whether the error is known to +// IsNotExist returns a boolean indicating whether its argument is known to // report that a file or directory does not exist. It is satisfied by // [ErrNotExist] as well as some syscall errors. // @@ -91,7 +91,7 @@ func IsNotExist(err error) bool { return underlyingErrorIs(err, ErrNotExist) } -// IsPermission returns a boolean indicating whether the error is known to +// IsPermission returns a boolean indicating whether its argument is known to // report that permission is denied. It is satisfied by [ErrPermission] as well // as some syscall errors. // @@ -101,7 +101,7 @@ func IsPermission(err error) bool { return underlyingErrorIs(err, ErrPermission) } -// IsTimeout returns a boolean indicating whether the error is known +// IsTimeout returns a boolean indicating whether its argument is known // to report that a timeout occurred. // // This function predates [errors.Is], and the notion of whether an diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 50ed3a8d165..da9f68fe28f 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -334,8 +334,10 @@ type Cmd struct { lookPathErr error // cachedLookExtensions caches the result of calling lookExtensions. + // It is set when Command is called with an absolute path, letting it do + // the work of resolving the extension, so Start doesn't need to do it again. // This is only used on Windows. - cachedLookExtensions string + cachedLookExtensions struct{ in, out string } } // A ctxResult reports the result of watching the Context associated with a @@ -436,12 +438,12 @@ func Command(name string, arg ...string) *Cmd { // Since the path is absolute, its extension should be unambiguous // and independent of cmd.Dir, and we can go ahead and cache the lookup now. // - // Note that we cannot add an extension here for relative paths, because - // cmd.Dir may be set after we return from this function and that may cause - // the command to resolve to a different extension. - lp, err := lookExtensions(name, "") - cmd.cachedLookExtensions = lp - if err != nil { + // Note that we don't cache anything here for relative paths, because + // cmd.Dir may be set after we return from this function and that may + // cause the command to resolve to a different extension. + if lp, err := lookExtensions(name, ""); err == nil { + cmd.cachedLookExtensions.in, cmd.cachedLookExtensions.out = name, lp + } else { cmd.Err = err } } @@ -642,29 +644,32 @@ func (c *Cmd) Start() error { return c.Err } lp := c.Path - if c.cachedLookExtensions != "" { - lp = c.cachedLookExtensions - } - if runtime.GOOS == "windows" && c.cachedLookExtensions == "" { - // If c.Path is relative, we had to wait until now - // to resolve it in case c.Dir was changed. - // (If it is absolute, we already resolved its extension in Command - // and shouldn't need to do so again.) - // - // Unfortunately, we cannot write the result back to c.Path because programs - // may assume that they can call Start concurrently with reading the path. - // (It is safe and non-racy to do so on Unix platforms, and users might not - // test with the race detector on all platforms; - // see https://go.dev/issue/62596.) - // - // So we will pass the fully resolved path to os.StartProcess, but leave - // c.Path as is: missing a bit of logging information seems less harmful - // than triggering a surprising data race, and if the user really cares - // about that bit of logging they can always use LookPath to resolve it. - var err error - lp, err = lookExtensions(c.Path, c.Dir) - if err != nil { - return err + if runtime.GOOS == "windows" { + if c.Path == c.cachedLookExtensions.in { + // If Command was called with an absolute path, we already resolved + // its extension and shouldn't need to do so again (provided c.Path + // wasn't set to another value between the calls to Command and Start). + lp = c.cachedLookExtensions.out + } else { + // If *Cmd was made without using Command at all, or if Command was + // called with a relative path, we had to wait until now to resolve + // it in case c.Dir was changed. + // + // Unfortunately, we cannot write the result back to c.Path because programs + // may assume that they can call Start concurrently with reading the path. + // (It is safe and non-racy to do so on Unix platforms, and users might not + // test with the race detector on all platforms; + // see https://go.dev/issue/62596.) + // + // So we will pass the fully resolved path to os.StartProcess, but leave + // c.Path as is: missing a bit of logging information seems less harmful + // than triggering a surprising data race, and if the user really cares + // about that bit of logging they can always use LookPath to resolve it. + var err error + lp, err = lookExtensions(c.Path, c.Dir) + if err != nil { + return err + } } } if c.Cancel != nil && c.ctx == nil { diff --git a/src/os/exec/exec_test.go b/src/os/exec/exec_test.go index dbe59fea119..a0bb89e203d 100644 --- a/src/os/exec/exec_test.go +++ b/src/os/exec/exec_test.go @@ -1838,7 +1838,7 @@ func TestPathRace(t *testing.T) { func TestAbsPathExec(t *testing.T) { testenv.MustHaveExec(t) - testenv.MustHaveGoBuild(t) // must have GOROOT/bin/gofmt, but close enough + testenv.MustHaveGoBuild(t) // must have GOROOT/bin/{go,gofmt} // A simple exec of a full path should work. // Go 1.22 broke this on Windows, requiring ".exe"; see #66586. @@ -1863,4 +1863,24 @@ func TestAbsPathExec(t *testing.T) { if err == nil { t.Errorf("using exec.Cmd{Path: %#q}: unexpected success", cmd.Path) } + + // A simple exec after modifying Cmd.Path should work. + // This broke on Windows. See go.dev/issue/68314. + t.Run("modified", func(t *testing.T) { + if exec.Command(filepath.Join(testenv.GOROOT(t), "bin/go")).Run() == nil { + // The implementation of the test case below relies on the go binary + // exiting with a non-zero exit code when run without any arguments. + // In the unlikely case that changes, we need to use another binary. + t.Fatal("test case needs updating to verify fix for go.dev/issue/68314") + } + exe1 := filepath.Join(testenv.GOROOT(t), "bin/go") + exe2 := filepath.Join(testenv.GOROOT(t), "bin/gofmt") + cmd := exec.Command(exe1) + cmd.Path = exe2 + cmd.Args = []string{cmd.Path} + err := cmd.Run() + if err != nil { + t.Error("ran wrong binary") + } + }) } diff --git a/src/os/os_test.go b/src/os/os_test.go index 7348a9f01ca..878974384db 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -158,28 +158,20 @@ func equal(name1, name2 string) (r bool) { return } -// localTmp returns a local temporary directory not on NFS. -func localTmp() string { - switch runtime.GOOS { - case "android", "ios", "windows": - return TempDir() - } - return "/tmp" -} - -func newFile(testName string, t *testing.T) (f *File) { - f, err := CreateTemp(localTmp(), "_Go_"+testName) +func newFile(t *testing.T) (f *File) { + t.Helper() + f, err := CreateTemp("", "_Go_"+t.Name()) if err != nil { - t.Fatalf("TempFile %s: %s", testName, err) - } - return -} - -func newDir(testName string, t *testing.T) (name string) { - name, err := MkdirTemp(localTmp(), "_Go_"+testName) - if err != nil { - t.Fatalf("TempDir %s: %s", testName, err) + t.Fatal(err) } + t.Cleanup(func() { + if err := f.Close(); err != nil && !errors.Is(err, ErrClosed) { + t.Fatal(err) + } + if err := Remove(f.Name()); err != nil { + t.Fatal(err) + } + }) return } @@ -1276,9 +1268,7 @@ func TestChmod(t *testing.T) { } t.Parallel() - f := newFile("TestChmod", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) // Creation mode is read write fm := FileMode(0456) @@ -1314,9 +1304,7 @@ func checkSize(t *testing.T, f *File, size int64) { func TestFTruncate(t *testing.T) { t.Parallel() - f := newFile("TestFTruncate", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) checkSize(t, f, 0) f.Write([]byte("hello, world\n")) @@ -1336,9 +1324,7 @@ func TestFTruncate(t *testing.T) { func TestTruncate(t *testing.T) { t.Parallel() - f := newFile("TestTruncate", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) checkSize(t, f, 0) f.Write([]byte("hello, world\n")) @@ -1375,15 +1361,21 @@ func TestTruncateNonexistentFile(t *testing.T) { assertPathError(t, path, err) } -// Use TempDir (via newFile) to make sure we're on a local file system, -// so that timings are not distorted by latency and caching. -// On NFS, timings can be off due to caching of meta-data on -// NFS servers (Issue 848). +var hasNoatime = sync.OnceValue(func() bool { + // A sloppy way to check if noatime flag is set (as all filesystems are + // checked, not just the one we're interested in). A correct way + // would be to use statvfs syscall and check if flags has ST_NOATIME, + // but the syscall is OS-specific and is not even wired into Go stdlib. + // + // Only used on NetBSD (which ignores explicit atime updates with noatime). + mounts, _ := ReadFile("/proc/mounts") + return bytes.Contains(mounts, []byte("noatime")) +}) + func TestChtimes(t *testing.T) { t.Parallel() - f := newFile("TestChtimes", t) - defer Remove(f.Name()) + f := newFile(t) f.Write([]byte("hello, world\n")) f.Close() @@ -1391,139 +1383,119 @@ func TestChtimes(t *testing.T) { testChtimes(t, f.Name()) } -func TestChtimesWithZeroTimes(t *testing.T) { - file := newFile("chtimes-with-zero", t) +func TestChtimesOmit(t *testing.T) { + t.Parallel() + + testChtimesOmit(t, true, false) + testChtimesOmit(t, false, true) + testChtimesOmit(t, true, true) + testChtimesOmit(t, false, false) // Same as TestChtimes. +} + +func testChtimesOmit(t *testing.T, omitAt, omitMt bool) { + t.Logf("omit atime: %v, mtime: %v", omitAt, omitMt) + file := newFile(t) _, err := file.Write([]byte("hello, world\n")) - if err != nil { - t.Fatalf("Write: %s", err) - } - fName := file.Name() - defer Remove(file.Name()) - err = file.Close() - if err != nil { - t.Errorf("%v", err) - } - fs, err := Stat(fName) if err != nil { t.Fatal(err) } - startAtime := Atime(fs) - startMtime := fs.ModTime() + name := file.Name() + err = file.Close() + if err != nil { + t.Error(err) + } + fs, err := Stat(name) + if err != nil { + t.Fatal(err) + } + + wantAtime := Atime(fs) + wantMtime := fs.ModTime() switch runtime.GOOS { case "js": - startAtime = startAtime.Truncate(time.Second) - startMtime = startMtime.Truncate(time.Second) - } - at0 := startAtime - mt0 := startMtime - t0 := startMtime.Truncate(time.Second).Add(1 * time.Hour) - - tests := []struct { - aTime time.Time - mTime time.Time - wantATime time.Time - wantMTime time.Time - }{ - { - aTime: time.Time{}, - mTime: time.Time{}, - wantATime: startAtime, - wantMTime: startMtime, - }, - { - aTime: t0.Add(200 * time.Second), - mTime: time.Time{}, - wantATime: t0.Add(200 * time.Second), - wantMTime: startMtime, - }, - { - aTime: time.Time{}, - mTime: t0.Add(100 * time.Second), - wantATime: t0.Add(200 * time.Second), - wantMTime: t0.Add(100 * time.Second), - }, - { - aTime: t0.Add(300 * time.Second), - mTime: t0.Add(100 * time.Second), - wantATime: t0.Add(300 * time.Second), - wantMTime: t0.Add(100 * time.Second), - }, + wantAtime = wantAtime.Truncate(time.Second) + wantMtime = wantMtime.Truncate(time.Second) } - for _, tt := range tests { - // Now change the times accordingly. - if err := Chtimes(fName, tt.aTime, tt.mTime); err != nil { - t.Error(err) - } + var setAtime, setMtime time.Time // Zero value means omit. + if !omitAt { + wantAtime = wantAtime.Add(-1 * time.Second) + setAtime = wantAtime + } + if !omitMt { + wantMtime = wantMtime.Add(-1 * time.Second) + setMtime = wantMtime + } - // Finally verify the expectations. - fs, err = Stat(fName) - if err != nil { - t.Error(err) - } - at0 = Atime(fs) - mt0 = fs.ModTime() + // Change the times accordingly. + if err := Chtimes(name, setAtime, setMtime); err != nil { + t.Error(err) + } - if got, want := at0, tt.wantATime; !got.Equal(want) { - errormsg := fmt.Sprintf("AccessTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want) - switch runtime.GOOS { - case "plan9": - // Mtime is the time of the last change of - // content. Similarly, atime is set whenever - // the contents are accessed; also, it is set - // whenever mtime is set. - case "windows": - t.Error(errormsg) - default: // unix's - if got, want := at0, tt.wantATime; !got.Equal(want) { - mounts, err := ReadFile("/bin/mounts") - if err != nil { - mounts, err = ReadFile("/etc/mtab") - } - if strings.Contains(string(mounts), "noatime") { - t.Log(errormsg) - t.Log("A filesystem is mounted with noatime; ignoring.") - } else { - switch runtime.GOOS { - case "netbsd", "dragonfly": - // On a 64-bit implementation, birth time is generally supported and cannot be changed. - // When supported, atime update is restricted and depends on the file system and on the - // OS configuration. - if strings.Contains(runtime.GOARCH, "64") { - t.Log(errormsg) - t.Log("Filesystem might not support atime changes; ignoring.") - } - default: - t.Error(errormsg) - } - } - } - } - } - if got, want := mt0, tt.wantMTime; !got.Equal(want) { - errormsg := fmt.Sprintf("ModTime mismatch with values ATime:%q-MTime:%q\ngot: %q\nwant: %q", tt.aTime, tt.mTime, got, want) - switch runtime.GOOS { - case "dragonfly": + // Verify the expectations. + fs, err = Stat(name) + if err != nil { + t.Error(err) + } + gotAtime := Atime(fs) + gotMtime := fs.ModTime() + + // TODO: remove the dragonfly omitAt && omitMt exceptions below once the + // fix (https://github.com/DragonFlyBSD/DragonFlyBSD/commit/c7c71870ed0) + // is available generally and on CI runners. + if !gotAtime.Equal(wantAtime) { + errormsg := fmt.Sprintf("atime mismatch, got: %q, want: %q", gotAtime, wantAtime) + switch runtime.GOOS { + case "plan9": + // Mtime is the time of the last change of content. + // Similarly, atime is set whenever the contents are + // accessed; also, it is set whenever mtime is set. + case "dragonfly": + if omitAt && omitMt { t.Log(errormsg) - t.Log("Mtime is always updated; ignoring.") - default: + t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.") + } else { + // Assume hammer2 fs; https://www.dragonflybsd.org/hammer/ says: + // > Because HAMMER2 is a block copy-on-write filesystem, + // > the "atime" field is not supported and will typically + // > just reflect local system in-memory caches or mtime. + // + // TODO: if only can CI define TMPDIR to point to a tmpfs + // (e.g. /var/run/shm), this exception can be removed. + t.Log(errormsg) + t.Log("Known DragonFly BSD issue (atime not supported on hammer2); ignoring.") + } + case "netbsd": + if !omitAt && hasNoatime() { + t.Log(errormsg) + t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.") + } else { t.Error(errormsg) } + default: + t.Error(errormsg) + } + } + if !gotMtime.Equal(wantMtime) { + errormsg := fmt.Sprintf("mtime mismatch, got: %q, want: %q", gotMtime, wantMtime) + switch runtime.GOOS { + case "dragonfly": + if omitAt && omitMt { + t.Log(errormsg) + t.Log("Known DragonFly BSD issue (won't work when both times are omitted); ignoring.") + } else { + t.Error(errormsg) + } + default: + t.Error(errormsg) } } } -// Use TempDir (via newDir) to make sure we're on a local file system, -// so that timings are not distorted by latency and caching. -// On NFS, timings can be off due to caching of meta-data on -// NFS servers (Issue 848). func TestChtimesDir(t *testing.T) { t.Parallel() - name := newDir("TestChtimes", t) - defer RemoveAll(name) - - testChtimes(t, name) + testChtimes(t, t.TempDir()) } func testChtimes(t *testing.T, name string) { @@ -1550,6 +1522,7 @@ func testChtimes(t *testing.T, name string) { pat := Atime(postStat) pmt := postStat.ModTime() if !pat.Before(at) { + errormsg := fmt.Sprintf("AccessTime didn't go backwards; was=%v, after=%v", at, pat) switch runtime.GOOS { case "plan9": // Mtime is the time of the last change of @@ -1557,14 +1530,14 @@ func testChtimes(t *testing.T, name string) { // the contents are accessed; also, it is set // whenever mtime is set. case "netbsd": - mounts, _ := ReadFile("/proc/mounts") - if strings.Contains(string(mounts), "noatime") { - t.Logf("AccessTime didn't go backwards, but see a filesystem mounted noatime; ignoring. Issue 19293.") + if hasNoatime() { + t.Log(errormsg) + t.Log("Known NetBSD issue (atime not changed on fs mounted with noatime); ignoring.") } else { - t.Logf("AccessTime didn't go backwards; was=%v, after=%v (Ignoring on NetBSD, assuming noatime, Issue 19293)", at, pat) + t.Errorf(errormsg) } default: - t.Errorf("AccessTime didn't go backwards; was=%v, after=%v", at, pat) + t.Errorf(errormsg) } } @@ -1574,9 +1547,8 @@ func testChtimes(t *testing.T, name string) { } func TestChtimesToUnixZero(t *testing.T) { - file := newFile("chtimes-to-unix-zero", t) + file := newFile(t) fn := file.Name() - defer Remove(fn) if _, err := file.Write([]byte("hi")); err != nil { t.Fatal(err) } @@ -1796,9 +1768,7 @@ func TestProgWideChdir(t *testing.T) { func TestSeek(t *testing.T) { t.Parallel() - f := newFile("TestSeek", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) const data = "hello, world\n" io.WriteString(f, data) @@ -2040,9 +2010,7 @@ func TestHostname(t *testing.T) { func TestReadAt(t *testing.T) { t.Parallel() - f := newFile("TestReadAt", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) const data = "hello, world\n" io.WriteString(f, data) @@ -2064,9 +2032,7 @@ func TestReadAt(t *testing.T) { func TestReadAtOffset(t *testing.T) { t.Parallel() - f := newFile("TestReadAtOffset", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) const data = "hello, world\n" io.WriteString(f, data) @@ -2095,9 +2061,7 @@ func TestReadAtOffset(t *testing.T) { func TestReadAtNegativeOffset(t *testing.T) { t.Parallel() - f := newFile("TestReadAtNegativeOffset", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) const data = "hello, world\n" io.WriteString(f, data) @@ -2116,9 +2080,7 @@ func TestReadAtNegativeOffset(t *testing.T) { func TestWriteAt(t *testing.T) { t.Parallel() - f := newFile("TestWriteAt", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) const data = "hello, world\n" io.WriteString(f, data) @@ -2141,9 +2103,7 @@ func TestWriteAt(t *testing.T) { func TestWriteAtNegativeOffset(t *testing.T) { t.Parallel() - f := newFile("TestWriteAtNegativeOffset", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) n, err := f.WriteAt([]byte("WORLD"), -10) @@ -2477,9 +2437,7 @@ func TestStatRelativeSymlink(t *testing.T) { func TestReadAtEOF(t *testing.T) { t.Parallel() - f := newFile("TestReadAtEOF", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) _, err := f.ReadAt(make([]byte, 10), 0) switch err { @@ -2495,12 +2453,7 @@ func TestReadAtEOF(t *testing.T) { func TestLongPath(t *testing.T) { t.Parallel() - tmpdir := newDir("TestLongPath", t) - defer func(d string) { - if err := RemoveAll(d); err != nil { - t.Fatalf("RemoveAll failed: %v", err) - } - }(tmpdir) + tmpdir := t.TempDir() // Test the boundary of 247 and fewer bytes (normal) and 248 and more bytes (adjusted). sizes := []int{247, 248, 249, 400} diff --git a/src/os/os_unix_test.go b/src/os/os_unix_test.go index 98e436fae66..fcc75e5ee61 100644 --- a/src/os/os_unix_test.go +++ b/src/os/os_unix_test.go @@ -45,13 +45,7 @@ func TestChown(t *testing.T) { } t.Parallel() - // Use TempDir() to make sure we're on a local file system, - // so that the group ids returned by Getgroups will be allowed - // on the file. On NFS, the Getgroups groups are - // basically useless. - f := newFile("TestChown", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) dir, err := f.Stat() if err != nil { t.Fatalf("stat %s: %s", f.Name(), err) @@ -99,13 +93,7 @@ func TestFileChown(t *testing.T) { } t.Parallel() - // Use TempDir() to make sure we're on a local file system, - // so that the group ids returned by Getgroups will be allowed - // on the file. On NFS, the Getgroups groups are - // basically useless. - f := newFile("TestFileChown", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) dir, err := f.Stat() if err != nil { t.Fatalf("stat %s: %s", f.Name(), err) @@ -151,13 +139,7 @@ func TestLchown(t *testing.T) { testenv.MustHaveSymlink(t) t.Parallel() - // Use TempDir() to make sure we're on a local file system, - // so that the group ids returned by Getgroups will be allowed - // on the file. On NFS, the Getgroups groups are - // basically useless. - f := newFile("TestLchown", t) - defer Remove(f.Name()) - defer f.Close() + f := newFile(t) dir, err := f.Stat() if err != nil { t.Fatalf("stat %s: %s", f.Name(), err) @@ -223,8 +205,7 @@ func TestReaddirRemoveRace(t *testing.T) { } return oldStat(name) } - dir := newDir("TestReaddirRemoveRace", t) - defer RemoveAll(dir) + dir := t.TempDir() if err := WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil { t.Fatal(err) } @@ -255,8 +236,7 @@ func TestMkdirStickyUmask(t *testing.T) { t.Parallel() const umask = 0077 - dir := newDir("TestMkdirStickyUmask", t) - defer RemoveAll(dir) + dir := t.TempDir() oldUmask := syscall.Umask(umask) defer syscall.Umask(oldUmask) @@ -396,14 +376,14 @@ func TestIssue60181(t *testing.T) { want := "hello gopher" - a, err := CreateTemp("", "a") + a, err := CreateTemp(".", "a") if err != nil { t.Fatal(err) } a.WriteString(want[:5]) a.Close() - b, err := CreateTemp("", "b") + b, err := CreateTemp(".", "b") if err != nil { t.Fatal(err) } diff --git a/src/runtime/arena.go b/src/runtime/arena.go index 47b131466c8..cd9a9dfae10 100644 --- a/src/runtime/arena.go +++ b/src/runtime/arena.go @@ -232,7 +232,7 @@ func userArenaChunkReserveBytes() uintptr { } type userArena struct { - // full is a list of full chunks that have not enough free memory left, and + // fullList is a list of full chunks that have not enough free memory left, and // that we'll free once this user arena is freed. // // Can't use mSpanList here because it's not-in-heap. diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index b8be9f8272b..da3d956d480 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -230,6 +230,10 @@ Below is the full list of supported metrics, ordered lexicographically. /gc/stack/starting-size:bytes The stack size of new goroutines. + /godebug/non-default-behavior/asynctimerchan:events + The number of non-default behaviors executed by the time package + due to a non-default GODEBUG=asynctimerchan=... setting. + /godebug/non-default-behavior/execerrdot:events The number of non-default behaviors executed by the os/exec package due to a non-default GODEBUG=execerrdot=... setting. diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index b51a1ad3ce3..e5620aec11d 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -9,6 +9,7 @@ package runtime import ( "internal/abi" + "internal/goarch" "internal/profilerecord" "internal/runtime/atomic" "runtime/internal/sys" @@ -542,16 +543,14 @@ func saveblockevent(cycles, rate int64, skip int, which bucketType) { gp := getg() mp := acquirem() // we must not be preempted while accessing profstack - nstk := 1 + var nstk int if tracefpunwindoff() || gp.m.hasCgoOnStack() { - mp.profStack[0] = logicalStackSentinel if gp.m.curg == nil || gp.m.curg == gp { - nstk = callers(skip, mp.profStack[1:]) + nstk = callers(skip, mp.profStack) } else { - nstk = gcallers(gp.m.curg, skip, mp.profStack[1:]) + nstk = gcallers(gp.m.curg, skip, mp.profStack) } } else { - mp.profStack[0] = uintptr(skip) if gp.m.curg == nil || gp.m.curg == gp { if skip > 0 { // We skip one fewer frame than the provided value for frame @@ -559,12 +558,12 @@ func saveblockevent(cycles, rate int64, skip int, which bucketType) { // frame, whereas the saved frame pointer will give us the // caller's return address first (so, not including // saveblockevent) - mp.profStack[0] -= 1 + skip -= 1 } - nstk += fpTracebackPCs(unsafe.Pointer(getfp()), mp.profStack[1:]) + nstk = fpTracebackPartialExpand(skip, unsafe.Pointer(getfp()), mp.profStack) } else { - mp.profStack[1] = gp.m.curg.sched.pc - nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.m.curg.sched.bp), mp.profStack[2:]) + mp.profStack[0] = gp.m.curg.sched.pc + nstk = 1 + fpTracebackPartialExpand(skip, unsafe.Pointer(gp.m.curg.sched.bp), mp.profStack[1:]) } } @@ -572,6 +571,52 @@ func saveblockevent(cycles, rate int64, skip int, which bucketType) { releasem(mp) } +// fpTracebackPartialExpand records a call stack obtained starting from fp. +// This function will skip the given number of frames, properly accounting for +// inlining, and save remaining frames as "physical" return addresses. The +// consumer should later use CallersFrames or similar to expand inline frames. +func fpTracebackPartialExpand(skip int, fp unsafe.Pointer, pcBuf []uintptr) int { + var n int + lastFuncID := abi.FuncIDNormal + skipOrAdd := func(retPC uintptr) bool { + if skip > 0 { + skip-- + } else if n < len(pcBuf) { + pcBuf[n] = retPC + n++ + } + return n < len(pcBuf) + } + for n < len(pcBuf) && fp != nil { + // return addr sits one word above the frame pointer + pc := *(*uintptr)(unsafe.Pointer(uintptr(fp) + goarch.PtrSize)) + + if skip > 0 { + callPC := pc - 1 + fi := findfunc(callPC) + u, uf := newInlineUnwinder(fi, callPC) + for ; uf.valid(); uf = u.next(uf) { + sf := u.srcFunc(uf) + if sf.funcID == abi.FuncIDWrapper && elideWrapperCalling(lastFuncID) { + // ignore wrappers + } else if more := skipOrAdd(uf.pc + 1); !more { + return n + } + lastFuncID = sf.funcID + } + } else { + // We've skipped the desired number of frames, so no need + // to perform further inline expansion now. + pcBuf[n] = pc + n++ + } + + // follow the frame pointer to the next one + fp = unsafe.Pointer(*(*uintptr)(fp)) + } + return n +} + // lockTimer assists with profiling contention on runtime-internal locks. // // There are several steps between the time that an M experiences contention and @@ -892,9 +937,10 @@ func (r *StackRecord) Stack() []uintptr { // at the beginning of main). var MemProfileRate int = 512 * 1024 -// disableMemoryProfiling is set by the linker if runtime.MemProfile +// disableMemoryProfiling is set by the linker if memory profiling // is not used and the link type guarantees nobody else could use it // elsewhere. +// We check if the runtime.memProfileInternal symbol is present. var disableMemoryProfiling bool // A MemProfileRecord describes the live objects allocated @@ -955,6 +1001,13 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { // memProfileInternal returns the number of records n in the profile. If there // are less than size records, copyFn is invoked for each record, and ok returns // true. +// +// The linker set disableMemoryProfiling to true to disable memory profiling +// if this function is not reachable. Mark it noinline to ensure the symbol exists. +// (This function is big and normally not inlined anyway.) +// See also disableMemoryProfiling above and cmd/link/internal/ld/lib.go:linksetup. +// +//go:noinline func memProfileInternal(size int, inuseZero bool, copyFn func(profilerecord.MemProfileRecord)) (n int, ok bool) { cycle := mProfCycle.read() // If we're between mProf_NextCycle and mProf_Flush, take care @@ -1067,10 +1120,34 @@ type BlockProfileRecord struct { // the [testing] package's -test.blockprofile flag instead // of calling BlockProfile directly. func BlockProfile(p []BlockProfileRecord) (n int, ok bool) { - return blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) { + n, ok = blockProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) { copyBlockProfileRecord(&p[0], r) p = p[1:] }) + if !ok { + return + } + expandFrames(p[:n]) + return +} + +func expandFrames(p []BlockProfileRecord) { + expandedStack := makeProfStack() + for i := range p { + cf := CallersFrames(p[i].Stack()) + j := 0 + for ; j < len(expandedStack); j++ { + f, more := cf.Next() + // f.PC is a "call PC", but later consumers will expect + // "return PCs" + expandedStack[i] = f.PC + 1 + if !more { + break + } + } + k := copy(p[i].Stack0[:], expandedStack[:j]) + clear(p[i].Stack0[k:]) + } } // blockProfileInternal returns the number of records n in the profile. If there @@ -1103,6 +1180,9 @@ func blockProfileInternal(size int, copyFn func(profilerecord.BlockProfileRecord return } +// copyBlockProfileRecord copies the sample values and call stack from src to dst. +// The call stack is copied as-is. The caller is responsible for handling inline +// expansion, needed when the call stack was collected with frame pointer unwinding. func copyBlockProfileRecord(dst *BlockProfileRecord, src profilerecord.BlockProfileRecord) { dst.Count = src.Count dst.Cycles = src.Cycles @@ -1115,7 +1195,11 @@ func copyBlockProfileRecord(dst *BlockProfileRecord, src profilerecord.BlockProf if asanenabled { asanwrite(unsafe.Pointer(&dst.Stack0[0]), unsafe.Sizeof(dst.Stack0)) } - i := fpunwindExpand(dst.Stack0[:], src.Stack) + // We just copy the stack here without inline expansion + // (needed if frame pointer unwinding is used) + // since this function is called under the profile lock, + // and doing something that might allocate can violate lock ordering. + i := copy(dst.Stack0[:], src.Stack) clear(dst.Stack0[i:]) } @@ -1134,10 +1218,15 @@ func pprof_blockProfileInternal(p []profilerecord.BlockProfileRecord) (n int, ok // Most clients should use the [runtime/pprof] package // instead of calling MutexProfile directly. func MutexProfile(p []BlockProfileRecord) (n int, ok bool) { - return mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) { + n, ok = mutexProfileInternal(len(p), func(r profilerecord.BlockProfileRecord) { copyBlockProfileRecord(&p[0], r) p = p[1:] }) + if !ok { + return + } + expandFrames(p[:n]) + return } // mutexProfileInternal returns the number of records n in the profile. If there diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index be17e598754..4b7a9f63c65 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -404,6 +404,25 @@ type countProfile interface { Label(i int) *labelMap } +// expandInlinedFrames copies the call stack from pcs into dst, expanding any +// PCs corresponding to inlined calls into the corresponding PCs for the inlined +// functions. Returns the number of frames copied to dst. +func expandInlinedFrames(dst, pcs []uintptr) int { + cf := runtime.CallersFrames(pcs) + var n int + for n < len(dst) { + f, more := cf.Next() + // f.PC is a "call PC", but later consumers will expect + // "return PCs" + dst[n] = f.PC + 1 + n++ + if !more { + break + } + } + return n +} + // printCountCycleProfile outputs block profile records (for block or mutex profiles) // as the pprof-proto format output. Translations from cycle count to time duration // are done because The proto expects count and time (nanoseconds) instead of count @@ -426,7 +445,7 @@ func printCountCycleProfile(w io.Writer, countName, cycleName string, records [] values[1] = int64(float64(r.Cycles) / cpuGHz) // For count profiles, all stack addresses are // return PCs, which is what appendLocsForStack expects. - n := pprof_fpunwindExpand(expandedStack[:], r.Stack) + n := expandInlinedFrames(expandedStack, r.Stack) locs = b.appendLocsForStack(locs[:0], expandedStack[:n]) b.pbSample(values, locs, nil) } @@ -586,7 +605,8 @@ func writeHeapInternal(w io.Writer, debug int, defaultSampleType string) error { runtime.ReadMemStats(memStats) } - // Find out how many records there are (MemProfile(nil, true)), + // Find out how many records there are (the call + // pprof_memProfileInternal(nil, true) below), // allocate that many records, and get the data. // There's a race—more records might be added between // the two calls—so allocate a few extra records for safety @@ -934,7 +954,7 @@ func writeProfileInternal(w io.Writer, debug int, name string, runtimeProfile fu for i := range p { r := &p[i] fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count) - n := pprof_fpunwindExpand(expandedStack, r.Stack) + n := expandInlinedFrames(expandedStack, r.Stack) stack := expandedStack[:n] for _, pc := range stack { fmt.Fprintf(w, " %#x", pc) diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go index 09abbb31ae6..bd11914544f 100644 --- a/src/runtime/pprof/pprof_test.go +++ b/src/runtime/pprof/pprof_test.go @@ -2578,3 +2578,81 @@ func produceProfileEvents(t *testing.T, depth int) { runtime.GC() goroutineDeep(t, depth-4) // -4 for produceProfileEvents, **, chanrecv1, chanrev, gopark } + +func TestMutexBlockFullAggregation(t *testing.T) { + // This regression test is adapted from + // https://github.com/grafana/pyroscope-go/issues/103, + // authored by Tolya Korniltsev + + var m sync.Mutex + + prev := runtime.SetMutexProfileFraction(-1) + defer runtime.SetMutexProfileFraction(prev) + + const fraction = 1 + const iters = 100 + const workers = 2 + + runtime.SetMutexProfileFraction(fraction) + runtime.SetBlockProfileRate(1) + defer runtime.SetBlockProfileRate(0) + + wg := sync.WaitGroup{} + wg.Add(workers) + for j := 0; j < workers; j++ { + go func() { + for i := 0; i < iters; i++ { + m.Lock() + // Wait at least 1 millisecond to pass the + // starvation threshold for the mutex + time.Sleep(time.Millisecond) + m.Unlock() + } + wg.Done() + }() + } + wg.Wait() + + assertNoDuplicates := func(name string, collect func([]runtime.BlockProfileRecord) (int, bool)) { + var p []runtime.BlockProfileRecord + n, ok := collect(nil) + for { + p = make([]runtime.BlockProfileRecord, n+50) + n, ok = collect(p) + if ok { + p = p[:n] + break + } + } + seen := make(map[string]struct{}) + for _, r := range p { + cf := runtime.CallersFrames(r.Stack()) + var stack strings.Builder + for { + f, more := cf.Next() + stack.WriteString(f.Func.Name()) + if !more { + break + } + stack.WriteByte('\n') + } + s := stack.String() + if !strings.Contains(s, "TestMutexBlockFullAggregation") { + continue + } + if _, ok := seen[s]; ok { + t.Errorf("saw duplicate entry in %s profile with stack:\n%s", name, s) + } + seen[s] = struct{}{} + } + if len(seen) == 0 { + t.Errorf("did not see any samples in %s profile for this test", name) + } + } + t.Run("mutex", func(t *testing.T) { + assertNoDuplicates("mutex", runtime.MutexProfile) + }) + t.Run("block", func(t *testing.T) { + assertNoDuplicates("block", runtime.BlockProfile) + }) +} diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 67eadeac9ee..a930ea707f1 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -200,7 +200,7 @@ func testGoroutineParallelism2(t *testing.T, load, netpoll bool) { laddr = "127.0.0.1:0" } ln, err := net.Listen("tcp", laddr) - if err != nil { + if err == nil { defer ln.Close() // yup, defer in a loop } } diff --git a/src/slices/iter.go b/src/slices/iter.go index 131cece3a06..cd8f308ca08 100644 --- a/src/slices/iter.go +++ b/src/slices/iter.go @@ -9,8 +9,8 @@ import ( "iter" ) -// All returns an iterator over index-value pairs in the slice. -// The indexes range in the usual order, from 0 through len(s)-1. +// All returns an iterator over index-value pairs in the slice +// in the usual order. func All[Slice ~[]E, E any](s Slice) iter.Seq2[int, E] { return func(yield func(int, E) bool) { for i, v := range s { @@ -22,7 +22,7 @@ func All[Slice ~[]E, E any](s Slice) iter.Seq2[int, E] { } // Backward returns an iterator over index-value pairs in the slice, -// traversing it backward. The indexes range from len(s)-1 down to 0. +// traversing it backward with descending indices. func Backward[Slice ~[]E, E any](s Slice) iter.Seq2[int, E] { return func(yield func(int, E) bool) { for i := len(s) - 1; i >= 0; i-- { @@ -33,8 +33,7 @@ func Backward[Slice ~[]E, E any](s Slice) iter.Seq2[int, E] { } } -// Values returns an iterator over the slice elements, -// starting with s[0]. +// Values returns an iterator that yields the slice elements in order. func Values[Slice ~[]E, E any](s Slice) iter.Seq[E] { return func(yield func(E) bool) { for _, v := range s { diff --git a/src/strings/builder.go b/src/strings/builder.go index e6df08c6f47..3b37888cbf6 100644 --- a/src/strings/builder.go +++ b/src/strings/builder.go @@ -23,6 +23,18 @@ type Builder struct { buf []byte } +// This is just a wrapper around abi.NoEscape. +// +// This wrapper is necessary because internal/abi is a runtime package, +// so it can not be built with -d=checkptr, causing incorrect inlining +// decision when building with checkptr enabled, see issue #68415. +// +//go:nosplit +//go:nocheckptr +func noescape(p unsafe.Pointer) unsafe.Pointer { + return abi.NoEscape(p) +} + func (b *Builder) copyCheck() { if b.addr == nil { // This hack works around a failing of Go's escape analysis @@ -30,7 +42,7 @@ func (b *Builder) copyCheck() { // See issue 23382. // TODO: once issue 7921 is fixed, this should be reverted to // just "b.addr = b". - b.addr = (*Builder)(abi.NoEscape(unsafe.Pointer(b))) + b.addr = (*Builder)(noescape(unsafe.Pointer(b))) } else if b.addr != b { panic("strings: illegal use of non-zero Builder copied by value") } diff --git a/src/strings/compare.go b/src/strings/compare.go index b3c01fddc15..dcf442471af 100644 --- a/src/strings/compare.go +++ b/src/strings/compare.go @@ -10,7 +10,7 @@ import "internal/bytealg" // The result will be 0 if a == b, -1 if a < b, and +1 if a > b. // // Use Compare when you need to perform a three-way comparison (with -// slices.SortFunc, for example). It is usually clearer and always faster +// [slices.SortFunc], for example). It is usually clearer and always faster // to use the built-in string comparison operators ==, <, >, and so on. func Compare(a, b string) int { return bytealg.CompareString(a, b) diff --git a/src/strings/replace.go b/src/strings/replace.go index 3b17a55b915..ae127288003 100644 --- a/src/strings/replace.go +++ b/src/strings/replace.go @@ -299,7 +299,7 @@ func makeGenericReplacer(oldnew []string) *genericReplacer { type appendSliceWriter []byte -// Write writes to the buffer to satisfy io.Writer. +// Write writes to the buffer to satisfy [io.Writer]. func (w *appendSliceWriter) Write(p []byte) (int, error) { *w = append(*w, p...) return len(p), nil diff --git a/src/strings/strings.go b/src/strings/strings.go index 95180828f6b..fba303c12a7 100644 --- a/src/strings/strings.go +++ b/src/strings/strings.go @@ -121,7 +121,7 @@ func IndexByte(s string, c byte) int { // IndexRune returns the index of the first instance of the Unicode code point // r, or -1 if rune is not present in s. -// If r is utf8.RuneError, it returns the first instance of any +// If r is [utf8.RuneError], it returns the first instance of any // invalid UTF-8 byte sequence. func IndexRune(s string, r rune) int { switch { @@ -275,7 +275,7 @@ func genSplit(s, sep string, sepSave, n int) []string { // Edge cases for s and sep (for example, empty strings) are handled // as described in the documentation for [Split]. // -// To split around the first instance of a separator, see Cut. +// To split around the first instance of a separator, see [Cut]. func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } // SplitAfterN slices s into substrings after each instance of sep and @@ -304,7 +304,7 @@ func SplitAfterN(s, sep string, n int) []string { // // It is equivalent to [SplitN] with a count of -1. // -// To split around the first instance of a separator, see Cut. +// To split around the first instance of a separator, see [Cut]. func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } // SplitAfter slices s into all substrings after each instance of sep and @@ -324,7 +324,7 @@ func SplitAfter(s, sep string) []string { var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} // Fields splits the string s around each instance of one or more consecutive white space -// characters, as defined by unicode.IsSpace, returning a slice of substrings of s or an +// characters, as defined by [unicode.IsSpace], returning a slice of substrings of s or an // empty slice if s contains only white space. func Fields(s string) []string { // First count the fields. diff --git a/src/sync/atomic/type.go b/src/sync/atomic/type.go index 7d2b6805bca..f487cb9c5f7 100644 --- a/src/sync/atomic/type.go +++ b/src/sync/atomic/type.go @@ -156,7 +156,7 @@ func (x *Uint32) And(mask uint32) (old uint32) { return AndUint32(&x.v, mask) } // Or atomically performs a bitwise OR operation on x using the bitmask // provided as mask and returns the old value. -func (x *Uint32) Or(mask uint32) (new uint32) { return OrUint32(&x.v, mask) } +func (x *Uint32) Or(mask uint32) (old uint32) { return OrUint32(&x.v, mask) } // A Uint64 is an atomic uint64. The zero value is zero. type Uint64 struct { @@ -188,7 +188,7 @@ func (x *Uint64) And(mask uint64) (old uint64) { return AndUint64(&x.v, mask) } // Or atomically performs a bitwise OR operation on x using the bitmask // provided as mask and returns the old value. -func (x *Uint64) Or(mask uint64) (new uint64) { return OrUint64(&x.v, mask) } +func (x *Uint64) Or(mask uint64) (old uint64) { return OrUint64(&x.v, mask) } // A Uintptr is an atomic uintptr. The zero value is zero. type Uintptr struct { diff --git a/src/sync/export_test.go b/src/sync/export_test.go index b55cecd987d..ddde30e018a 100644 --- a/src/sync/export_test.go +++ b/src/sync/export_test.go @@ -10,7 +10,7 @@ var Runtime_Semrelease = runtime_Semrelease var Runtime_procPin = runtime_procPin var Runtime_procUnpin = runtime_procUnpin -// poolDequeue testing. +// PoolDequeue exports an interface for pollDequeue testing. type PoolDequeue interface { PushHead(val any) bool PopHead() (any, bool) diff --git a/src/sync/rwmutex.go b/src/sync/rwmutex.go index 66cb93c44ef..1d5b8fde4a8 100644 --- a/src/sync/rwmutex.go +++ b/src/sync/rwmutex.go @@ -234,7 +234,7 @@ func syscall_hasWaitingReaders(rw *RWMutex) bool { } // RLocker returns a [Locker] interface that implements -// the [RWMutex.Lock] and [RWMutex.Unlock] methods by calling rw.RLock and rw.RUnlock. +// the [Locker.Lock] and [Locker.Unlock] methods by calling rw.RLock and rw.RUnlock. func (rw *RWMutex) RLocker() Locker { return (*rlocker)(rw) } diff --git a/src/testing/testing.go b/src/testing/testing.go index 200fa659b86..526cba39f8f 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -362,7 +362,7 @@ // // func TestMain(m *testing.M) { // // call flag.Parse() here if TestMain uses flags -// os.Exit(m.Run()) +// m.Run() // } // // TestMain is a low-level primitive and should not be necessary for casual diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go index 23ba9aec2be..a31309874d9 100644 --- a/src/text/template/parse/node.go +++ b/src/text/template/parse/node.go @@ -217,7 +217,11 @@ func (p *PipeNode) writeTo(sb *strings.Builder) { } v.writeTo(sb) } - sb.WriteString(" := ") + if p.IsAssign { + sb.WriteString(" = ") + } else { + sb.WriteString(" := ") + } } for i, c := range p.Cmds { if i > 0 { diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go index faf226d1c3c..26aff330fe8 100644 --- a/src/text/template/parse/parse_test.go +++ b/src/text/template/parse/parse_test.go @@ -306,6 +306,9 @@ var parseTests = []parseTest{ {"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here. {"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2). {"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space. + // Check the range handles assignment vs. declaration properly. + {"bug2a", "{{range $x := 0}}{{$x}}{{end}}", noError, "{{range $x := 0}}{{$x}}{{end}}"}, + {"bug2b", "{{range $x = 0}}{{$x}}{{end}}", noError, "{{range $x = 0}}{{$x}}{{end}}"}, // dot following a literal value {"dot after integer", "{{1.E}}", hasError, ""}, {"dot after float", "{{0.1.E}}", hasError, ""}, diff --git a/src/time/format.go b/src/time/format.go index 6488ec8abaa..c9e68b3eb25 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -1203,12 +1203,14 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) default: err = errBad } - case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: - if (std == stdISO8601TZ || std == stdISO8601ShortTZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' { + case stdISO8601TZ, stdISO8601ShortTZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ: + if len(value) >= 1 && value[0] == 'Z' { value = value[1:] z = UTC break } + fallthrough + case stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: var sign, hour, min, seconds string if std == stdISO8601ColonTZ || std == stdNumColonTZ { if len(value) < 6 { diff --git a/src/time/format_test.go b/src/time/format_test.go index 4b598f6bdf8..2537c765968 100644 --- a/src/time/format_test.go +++ b/src/time/format_test.go @@ -336,6 +336,23 @@ var parseTests = []ParseTest{ {"", "2006-002 15:04:05", "2010-035 21:00:57", false, false, 1, 0}, {"", "200600201 15:04:05", "201003502 21:00:57", false, false, 1, 0}, {"", "200600204 15:04:05", "201003504 21:00:57", false, false, 1, 0}, + + // Time zone offsets + {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57Z", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57+08", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07", "2010-02-04T21:00:57-08", true, false, 1, 0}, + {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57Z", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57+0800", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z0700", "2010-02-04T21:00:57-0800", true, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57Z", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57+08:00", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00", "2010-02-04T21:00:57-08:00", true, false, 1, 0}, + {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57Z", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57+080000", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z070000", "2010-02-04T21:00:57-080000", true, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57Z", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57+08:00:00", false, false, 1, 0}, + {"", "2006-01-02T15:04:05Z07:00:00", "2010-02-04T21:00:57-08:00:00", true, false, 1, 0}, } func TestParse(t *testing.T) { diff --git a/src/time/sleep.go b/src/time/sleep.go index 2c6495d93a7..7e2fa0c20af 100644 --- a/src/time/sleep.go +++ b/src/time/sleep.go @@ -23,6 +23,7 @@ func syncTimer(c chan Time) unsafe.Pointer { // If asynctimerchan=1, we don't even tell the runtime // about channel timers, so that we get the pre-Go 1.23 code paths. if asynctimerchan.Value() == "1" { + asynctimerchan.IncNonDefault() return nil } diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go index 8d6cacb8b55..de9421bab56 100644 --- a/src/unsafe/unsafe.go +++ b/src/unsafe/unsafe.go @@ -110,7 +110,7 @@ type IntegerType int // u := unsafe.Pointer(nil) // p := unsafe.Pointer(uintptr(u) + offset) // -// (4) Conversion of a Pointer to a uintptr when calling [syscall.Syscall]. +// (4) Conversion of a Pointer to a uintptr when calling functions like [syscall.Syscall]. // // The Syscall functions in package syscall pass their uintptr arguments directly // to the operating system, which then may, depending on the details of the call, @@ -260,7 +260,7 @@ func SliceData(slice []ArbitraryType) *ArbitraryType // a run-time panic occurs. // // Since Go strings are immutable, the bytes passed to String -// must not be modified afterwards. +// must not be modified as long as the returned string value exists. func String(ptr *byte, len IntegerType) string // StringData returns a pointer to the underlying bytes of str. diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 2868749b894..b8a0b84a282 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -18,7 +18,7 @@ golang.org/x/net/idna golang.org/x/net/lif golang.org/x/net/nettest golang.org/x/net/route -# golang.org/x/sys v0.21.0 +# golang.org/x/sys v0.22.0 ## explicit; go 1.18 golang.org/x/sys/cpu # golang.org/x/text v0.16.0 diff --git a/test/fixedbugs/issue54542.go b/test/fixedbugs/issue54542.go new file mode 100644 index 00000000000..165bcc582ed --- /dev/null +++ b/test/fixedbugs/issue54542.go @@ -0,0 +1,67 @@ +// run + +//go:build !js && !wasip1 + +// 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 + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" +) + +const aSrc = `package a + +func A() { println("a") } +` + +const mainSrc = `package main + +import "a" + +func main() { a.A() } +` + +var srcs = map[string]string{ + "a.go": aSrc, + "main.go": mainSrc, +} + +func main() { + dir, err := os.MkdirTemp("", "issue54542") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + + for fn, src := range srcs { + if err := os.WriteFile(filepath.Join(dir, fn), []byte(src), 0644); err != nil { + panic(err) + } + } + + if _, err := runInDir(dir, "tool", "compile", "-p=lie", "a.go"); err != nil { + panic(err) + } + + out, err := runInDir(dir, "tool", "compile", "-I=.", "-p=main", "main.go") + if err == nil { + panic("compiling succeed unexpectedly") + } + + if bytes.Contains(out, []byte("internal compiler error:")) { + panic(fmt.Sprintf("unexpected ICE:\n%s", string(out))) + } +} + +func runInDir(dir string, args ...string) ([]byte, error) { + cmd := exec.Command("go", args...) + cmd.Dir = dir + return cmd.CombinedOutput() +} diff --git a/test/fixedbugs/issue67190.go b/test/fixedbugs/issue67190.go new file mode 100644 index 00000000000..c19b248b51c --- /dev/null +++ b/test/fixedbugs/issue67190.go @@ -0,0 +1,24 @@ +// run + +// 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 + +func main() { + ch1 := make(chan struct{}) + var ch2 <-chan struct{} = ch1 + + switch ch1 { + case ch2: + default: + panic("bad narrow case") + } + + switch ch2 { + case ch1: + default: + panic("bad narrow switch") + } +} diff --git a/test/fixedbugs/issue68227.go b/test/fixedbugs/issue68227.go new file mode 100644 index 00000000000..615d2824e42 --- /dev/null +++ b/test/fixedbugs/issue68227.go @@ -0,0 +1,43 @@ +// run + +// 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 + +import ( + "fmt" +) + +type someType []uint64 + +func (s *someType) push(v uint64) { + *s = append(*s, v) +} + +func (s *someType) problematicFn(x1Lo, x1Hi, x2Lo, x2Hi uint64) { + r1 := int32(int16(x1Lo>>0)) * int32(int16(x2Lo>>0)) + g() + r3 := int32(int16(x1Lo>>32)) * int32(int16(x2Lo>>32)) + r4 := int32(int16(x1Lo>>48)) * int32(int16(x2Lo>>48)) + r5 := int32(int16(x1Hi>>0)) * int32(int16(x2Hi>>0)) + r7 := int32(int16(x1Hi>>32)) * int32(int16(x2Hi>>32)) + r8 := int32(int16(x1Hi>>48)) * int32(int16(x2Hi>>48)) + s.push(uint64(uint32(r1)) | (uint64(uint32(r3+r4)) << 32)) + s.push(uint64(uint32(r5)) | (uint64(uint32(r7+r8)) << 32)) +} + +//go:noinline +func g() { +} + +func main() { + s := &someType{} + s.problematicFn(0x1000100010001, 0x1000100010001, 0xffffffffffffffff, 0xffffffffffffffff) + for i := 0; i < 2; i++ { + if got, want := (*s)[i], uint64(0xfffffffeffffffff); got != want { + fmt.Printf("s[%d]=%x, want %x\n", i, got, want) + } + } +} diff --git a/test/fixedbugs/issue68264.go b/test/fixedbugs/issue68264.go new file mode 100644 index 00000000000..7d67e55f6e4 --- /dev/null +++ b/test/fixedbugs/issue68264.go @@ -0,0 +1,15 @@ +// compile + +// 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 p + +type nat []int + +var a, b nat = y() + +func y() (nat, []int) { + return nat{0}, nat{1} +} diff --git a/test/fixedbugs/issue68322.go b/test/fixedbugs/issue68322.go new file mode 100644 index 00000000000..9b3e713d595 --- /dev/null +++ b/test/fixedbugs/issue68322.go @@ -0,0 +1,17 @@ +// run + +// 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 + +import "math" + +var doNotFold = 18446744073709549568.0 + +func main() { + if math.Trunc(doNotFold) != doNotFold { + panic("big (over 2**63-1) math.Trunc is incorrect") + } +} diff --git a/test/fixedbugs/issue68415.go b/test/fixedbugs/issue68415.go new file mode 100644 index 00000000000..cf278ac6035 --- /dev/null +++ b/test/fixedbugs/issue68415.go @@ -0,0 +1,15 @@ +// run -gcflags=all=-d=checkptr + +// 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 + +import "regexp" + +var dataFileRegexp = regexp.MustCompile(`^data\.\d+\.bin$`) + +func main() { + _ = dataFileRegexp +}