aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeschi Kreinick <heschi@google.com>2023-01-03 12:21:38 -0500
committerHeschi Kreinick <heschi@google.com>2023-01-03 12:21:39 -0500
commiteb598248ba92c0ae410e69900c176ceb5531d8bb (patch)
tree24c980074b6f7cc1130c43c4c374fb168ba9e123
parent9f0234214473dfb785a5ad84a8fc62a6a395cbc3 (diff)
parentdb36eca33c389871b132ffb1a84fd534a349e8d8 (diff)
downloadgo-eb598248ba92c0ae410e69900c176ceb5531d8bb.tar.gz
go-eb598248ba92c0ae410e69900c176ceb5531d8bb.zip
[release-branch.go1.20] all: merge master (db36eca) into release-branch.go1.20
Merge List: + 2022-12-29 db36eca33c doc/go1.20: fix typos + 2022-12-29 642fd5f7ce go/types, types2: use strict comparability for type set intersection + 2022-12-28 9123221ccf misc/cgo/testsanitizers: run libfuzzer tests in temp directory + 2022-12-27 e870de9936 misc/cgo/testsanitizers: add libfuzzer tests + 2022-12-23 38cfb3be9d testing: rephrase the sentence about naming test files + 2022-12-23 1ba7341cb2 cmd/link, runtime: use a different section for Go libfuzzer counters + 2022-12-22 c61d322d5f runtime: call __fork instead of fork on darwin + 2022-12-22 6c9b661867 runtime: revert Apple libc atfork workaround + 2022-12-22 6d3139b203 misc/cgo/testshared: test build std in shared mode + 2022-12-22 de6abd7889 runtime/internal/startlinetest: work around shared buildmode linking issue + 2022-12-22 18baca6765 runtime/race: add build tag to internal amd64vN packages + 2022-12-22 13ed4f42f0 doc/go1.20: fix typo + 2022-12-21 fadd77c05b runtime/coverage: add missing file close in test support helper + 2022-12-21 c9a10d48a8 crypto/x509: return typed verification errors on macOS + 2022-12-21 2321abc5e9 archive/tar, archive/zip: revert documentation of ErrInsecurePath + 2022-12-21 458241f981 net/http/httputil: don't add X-Forwarded-{Host,Proto} after invoking Director funcs + 2022-12-21 58f6022eee syscall: don't use faccessat2 on android + 2022-12-21 78fc81070a net: use correct dns msg size + 2022-12-19 a5a4744250 os: reenable TestReaddirSmallSeek on windows + 2022-12-17 0b2ad1d815 cmd/compile: sign-extend the 2nd argument of the LoweredAtomicCas32 on loong64,mips64x,riscv64 + 2022-12-16 8bcc490667 os/user,net: add -fno-stack-protector to CFLAGS + 2022-12-16 f4b42f5cb8 net/http: improve errors in TestCancelRequestWhenSharingConnection + 2022-12-16 24ac659a39 syscall, internal/poll: fall back to accept on linux-arm + 2022-12-16 3323dab1f4 os/exec: retry ETXTBSY errors in TestFindExecutableVsNoexec + 2022-12-15 628a1e7d3a doc/go1.20: fix typo + 2022-12-15 357ea85892 spec: fix typo + 2022-12-14 ea14d1b6e1 spec: document which recursive arrays and structs are valid/invalid + 2022-12-14 0b8add46ce doc/go1.20.html: pre-announce dropping Windows 7, 8, and friends + 2022-12-14 4f8bc6224b cmd/compile: desugar OCALLMETH->OCALLFUNC within devirtualization + 2022-12-14 5c682f94c6 spec: document illegal recursive type parameter lists + 2022-12-14 bd42aa86d3 spec: describe new semantics for comparable and constraint satisfaction + 2022-12-14 ffefcd360b spec: introduce notion of strict comparability + 2022-12-13 cb07765045 syscall: fix closing of reordered FDs in plan9 ForkExec + 2022-12-13 5ba98b9756 go/types, types2: report type mismatch error when conversion is impossible + 2022-12-13 61e2b8ec59 cmd/gc: test temp string comparison with all ops + 2022-12-12 b16e94d13d syscall: skip TestUseCgroupFD if cgroupfs mounted RO + 2022-12-12 27301e8247 syscall: fix shadowing bugs in forkAndExecInChild + 2022-12-12 6f7a95d25e sync: remove unused const + 2022-12-12 6b895d9eaa doc/go1.20: fix typo + 2022-12-12 c6ad9dc9b5 debug/buildinfo: check pointer size on buildinfo.Read + 2022-12-12 5dca7ed66f doc/go1.20: fix URL anchor + 2022-12-11 888047c310 cmd/compile: fix conditional move rule on PPC64 + 2022-12-10 9b8750f53e os: skip size test in TestLstat if the file is a symlink + 2022-12-09 e8f78cb60c cmd/compile: fix conditional select rule + 2022-12-09 e76c87b191 doc: fix typo in 1.20 release notes + 2022-12-09 80f7484af7 os/user: zero-initialize C structs returned to Go + 2022-12-08 e738a2f19b go/types, types2: always rename type parameters during inference + 2022-12-08 8247b9f17a doc: fix typo + 2022-12-08 f368abb46e doc/go1.20: correct test binary -v flag value for test2json + 2022-12-08 7973b0e508 cmd/{go,cover,covdata}: fix 'package main' inconsistent handling + 2022-12-08 0aad4d3257 cmd/link: fix dynamic interpreter path for musl-based linux amd64 + 2022-12-08 eaf0e3d465 runtime: remove arbitrary timeouts in finalizer tests + 2022-12-08 c8313d4fa8 cmd/go: deflake TestScript/test2json_interrupt + 2022-12-08 b9747e0e6b os/user: on AIX getpwuid_r seems to return -1 on overflow + 2022-12-07 9431237d77 internal/safefilepath: fix TestFromFS on Plan 9 + 2022-12-07 7c7cd56870 cmd/go: in TestTerminalPassthrough, delay subprocess exit until the PTY has been read Change-Id: I037343df0fe5b171b5f5726fcc55c01108d2563e
-rw-r--r--doc/go1.20.html29
-rw-r--r--doc/go_spec.html240
-rw-r--r--misc/cgo/testsanitizers/cc_test.go30
-rw-r--r--misc/cgo/testsanitizers/libfuzzer_test.go91
-rw-r--r--misc/cgo/testsanitizers/testdata/libfuzzer1.go16
-rw-r--r--misc/cgo/testsanitizers/testdata/libfuzzer2.c11
-rw-r--r--misc/cgo/testsanitizers/testdata/libfuzzer2.go16
-rw-r--r--misc/cgo/testshared/shared_test.go12
-rw-r--r--src/archive/tar/reader.go9
-rw-r--r--src/archive/zip/reader.go11
-rw-r--r--src/cmd/compile/internal/devirtualize/devirtualize.go3
-rw-r--r--src/cmd/compile/internal/ssa/_gen/ARM64.rules4
-rw-r--r--src/cmd/compile/internal/ssa/_gen/LOONG64.rules3
-rw-r--r--src/cmd/compile/internal/ssa/_gen/MIPS64.rules3
-rw-r--r--src/cmd/compile/internal/ssa/_gen/PPC64.rules4
-rw-r--r--src/cmd/compile/internal/ssa/_gen/PPC64Ops.go4
-rw-r--r--src/cmd/compile/internal/ssa/_gen/RISCV64.rules2
-rw-r--r--src/cmd/compile/internal/ssa/rewriteARM64.go6
-rw-r--r--src/cmd/compile/internal/ssa/rewriteLOONG64.go24
-rw-r--r--src/cmd/compile/internal/ssa/rewriteMIPS64.go24
-rw-r--r--src/cmd/compile/internal/ssa/rewritePPC64.go25
-rw-r--r--src/cmd/compile/internal/ssa/rewriteRISCV64.go24
-rw-r--r--src/cmd/compile/internal/types2/expr.go36
-rw-r--r--src/cmd/compile/internal/types2/infer.go40
-rw-r--r--src/cmd/compile/internal/types2/typeset.go2
-rw-r--r--src/cmd/covdata/tool_test.go35
-rw-r--r--src/cmd/cover/cover.go3
-rw-r--r--src/cmd/go/internal/test/test.go2
-rw-r--r--src/cmd/go/terminal_test.go57
-rw-r--r--src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt4
-rw-r--r--src/cmd/go/testdata/script/cover_main_import_path.txt54
-rw-r--r--src/cmd/go/testdata/script/test2json_interrupt.txt29
-rw-r--r--src/cmd/link/internal/amd64/obj.go2
-rw-r--r--src/cmd/link/internal/ld/data.go12
-rw-r--r--src/cmd/link/internal/ld/elf.go2
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.go7
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.s2
-rw-r--r--src/crypto/x509/internal/macos/security.go19
-rw-r--r--src/crypto/x509/root_darwin.go14
-rw-r--r--src/crypto/x509/root_darwin_test.go10
-rw-r--r--src/debug/buildinfo/buildinfo.go4
-rw-r--r--src/debug/buildinfo/buildinfo_test.go12
-rw-r--r--src/go/types/expr.go36
-rw-r--r--src/go/types/infer.go40
-rw-r--r--src/go/types/typeset.go2
-rw-r--r--src/internal/poll/sock_cloexec.go2
-rw-r--r--src/internal/poll/sock_cloexec_accept.go51
-rw-r--r--src/internal/safefilepath/path_other.go2
-rw-r--r--src/internal/types/errors/codes.go2
-rw-r--r--src/internal/types/testdata/fixedbugs/issue57155.go14
-rw-r--r--src/internal/types/testdata/fixedbugs/issue57160.go10
-rw-r--r--src/internal/types/testdata/fixedbugs/issue57486.go29
-rw-r--r--src/net/cgo_unix.go5
-rw-r--r--src/net/cgo_unix_cgo.go1
-rw-r--r--src/net/http/httputil/reverseproxy.go30
-rw-r--r--src/net/http/httputil/reverseproxy_test.go47
-rw-r--r--src/net/http/transport_test.go18
-rw-r--r--src/os/exec/lp_linux_test.go17
-rw-r--r--src/os/os_test.go11
-rw-r--r--src/os/user/cgo_lookup_cgo.go38
-rw-r--r--src/os/user/cgo_lookup_unix.go4
-rw-r--r--src/runtime/coverage/emitdata_test.go9
-rw-r--r--src/runtime/coverage/testsupport.go15
-rw-r--r--src/runtime/internal/atomic/atomic_test.go30
-rw-r--r--src/runtime/internal/startlinetest/func_amd64.go3
-rw-r--r--src/runtime/internal/startlinetest/func_amd64.s3
-rw-r--r--src/runtime/libfuzzer.go9
-rw-r--r--src/runtime/mfinal_test.go26
-rw-r--r--src/runtime/os_darwin.go2
-rw-r--r--src/runtime/race/internal/amd64v1/doc.go2
-rw-r--r--src/runtime/race/internal/amd64v3/doc.go2
-rw-r--r--src/runtime/race/race_v1_amd64.go1
-rw-r--r--src/runtime/race/race_v3_amd64.go1
-rw-r--r--src/runtime/start_line_amd64_test.go2
-rw-r--r--src/runtime/string_test.go27
-rw-r--r--src/runtime/sys_darwin.go48
-rw-r--r--src/runtime/sys_darwin_amd64.s9
-rw-r--r--src/runtime/sys_darwin_arm64.s6
-rw-r--r--src/sync/map_bench_test.go1
-rw-r--r--src/syscall/exec_freebsd.go2
-rw-r--r--src/syscall/exec_libc2.go4
-rw-r--r--src/syscall/exec_linux.go6
-rw-r--r--src/syscall/exec_linux_test.go4
-rw-r--r--src/syscall/exec_plan9.go2
-rw-r--r--src/syscall/syscall_darwin.go32
-rw-r--r--src/syscall/syscall_linux.go29
-rw-r--r--src/syscall/syscall_linux_accept.go34
-rw-r--r--src/syscall/syscall_linux_accept4.go25
-rw-r--r--src/syscall/syscall_openbsd_libc.go6
-rw-r--r--src/syscall/zsyscall_darwin_amd64.go22
-rw-r--r--src/syscall/zsyscall_darwin_amd64.s6
-rw-r--r--src/syscall/zsyscall_darwin_arm64.go22
-rw-r--r--src/syscall/zsyscall_darwin_arm64.s6
-rw-r--r--src/syscall/zsyscall_linux_arm.go13
-rw-r--r--src/testing/testing.go5
-rw-r--r--test/fixedbugs/issue57184.go40
-rw-r--r--test/fixedbugs/issue57309.go23
97 files changed, 1266 insertions, 507 deletions
diff --git a/doc/go1.20.html b/doc/go1.20.html
index 28d3c8224d..aec3e25285 100644
--- a/doc/go1.20.html
+++ b/doc/go1.20.html
@@ -30,7 +30,7 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<p><!-- https://go.dev/issue/46505 -->
- Go 1.17 added <a href="/ref/spec#Conversions_from_slice_to_array_pointer">conversions from slice to an array pointer</a>.
+ Go 1.17 added <a href="/ref/spec#Conversions_from_slice_to_array_or_array_pointer">conversions from slice to an array pointer</a>.
Go 1.20 extends this to allow conversions from a slice to an array:
given a slice <code>x</code>, <code>[4]byte(x)</code> can now be written
instead of <code>*(*[4]byte)(x)</code>.
@@ -67,9 +67,16 @@ Do not send CLs removing the interior tags from such phrases.
<h2 id="ports">Ports</h2>
+<h3 id="windows">Windows</h3>
+
+<p><!-- https://go.dev/issue/57003, https://go.dev/issue/57004 -->
+ Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012.
+ Go 1.21 will require at least Windows 10 or Server 2016.
+</p>
+
<h3 id="darwin">Darwin and iOS</h3>
-<p><!-- golang.org/issue/23011 -->
+<p><!-- https://go.dev/issue/23011 -->
Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave.
Go 1.21 will require macOS 10.15 Catalina or later.
</p>
@@ -102,9 +109,9 @@ Do not send CLs removing the interior tags from such phrases.
Programs that run <code>go</code> <code>test</code> <code>-json</code>
do not need any updates.
Programs that invoke <code>go</code> <code>tool</code> <code>test2json</code>
- directly should now run the test binary with <code>-v=json</code>
- (for example, <code>go</code> <code>test</code> <code>-v=json</code>
- or <code>./pkg.test</code> <code>-test.v=json</code>)
+ directly should now run the test binary with <code>-v=test2json</code>
+ (for example, <code>go</code> <code>test</code> <code>-v=test2json</code>
+ or <code>./pkg.test</code> <code>-test.v=test2json</code>)
instead of plain <code>-v</code>.
</p>
@@ -285,7 +292,7 @@ Do not send CLs removing the interior tags from such phrases.
<p><!-- CL 423359, https://go.dev/issue/51317 -->
The runtime now has experimental support for memory-safe arena allocation
that makes it possible to eagerly free memory in bulk.
- When used appopriately, it has the potential to improve CPU performance by
+ When used appropriately, it has the potential to improve CPU performance by
up to 15% in memory-allocation-heavy applications.
To try it out, build your Go program with <code>GOEXPERIMENT=arenas</code>,
which will make the <code>arena</code> package visible to your program.
@@ -308,7 +315,7 @@ Do not send CLs removing the interior tags from such phrases.
<p><!-- https://go.dev/issue/51430 -->
Go 1.20 adds a new <code>runtime/coverage</code> package
containing APIs for writing coverage profile data at
- runtime from a long-running and/or server programs that
+ runtime from long-running and/or server programs that
do not terminate via <code>os.Exit()</code>.
</p>
@@ -374,7 +381,7 @@ Do not send CLs removing the interior tags from such phrases.
<code>$HOME/go1.4</code> (<code>%HOMEDRIVE%%HOMEPATH%\go1.4</code> on Windows).
Go 1.18 and Go 1.19 looked first for <code>$HOME/go1.17</code> or <code>$HOME/sdk/go1.17</code>
before falling back to <code>$HOME/go1.4</code>,
- in ancitipation of requiring Go 1.17 for use when bootstrapping Go 1.20.
+ in anticipation of requiring Go 1.17 for use when bootstrapping Go 1.20.
Go 1.20 does require a Go 1.17 release for bootstrapping, but we realized that we should
adopt the latest point release of the bootstrap toolchain, so it requires Go 1.17.13.
Go 1.20 looks for <code>$HOME/go1.17.13</code> or <code>$HOME/sdk/go1.17.13</code>
@@ -417,7 +424,7 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<p>
The <a href="/pkg/fmt/#Errorf"><code>fmt.Errorf</code></a> function
- now supports multiple occurrances of the <code>%w</code> format verb,
+ now supports multiple occurrences of the <code>%w</code> format verb,
which will cause it to return an error that wraps all of those error operands.
</p>
<p>
@@ -808,7 +815,7 @@ proxyHandler := &httputil.ReverseProxy{
The new <a href="/pkg/go/types/#Satisfies"><code>Satisfies</code></a> function reports
whether a type satisfies a constraint.
This change aligns with the <a href="#language">new language semantics</a>
- that distinguish satsifying a constraint from implementing an interface.
+ that distinguish satisfying a constraint from implementing an interface.
</p>
</dd>
</dl><!-- go/types -->
@@ -883,7 +890,7 @@ proxyHandler := &httputil.ReverseProxy{
if a <code>CNAME</code> record referred to a name that with no <code>A</code>,
<code>AAAA</code>, or <code>CNAME</code> record. This change modifies
<code>LookupCNAME</code> to match the previous behavior on Windows,
- allowing allowing <code>LookupCNAME</code> to succeed whenever a
+ allowing <code>LookupCNAME</code> to succeed whenever a
<code>CNAME</code> exists.
</p>
diff --git a/doc/go_spec.html b/doc/go_spec.html
index 3bc610fb02..f93f2ab9f1 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
<!--{
"Title": "The Go Programming Language Specification",
- "Subtitle": "Version of November 21, 2022",
+ "Subtitle": "Version of December 15, 2022",
"Path": "/ref/spec"
}-->
@@ -944,6 +944,29 @@ multi-dimensional types.
[2][2][2]float64 // same as [2]([2]([2]float64))
</pre>
+<p>
+An array type <code>T</code> may not have an element of type <code>T</code>,
+or of a type containing <code>T</code> as a component, directly or indirectly,
+if those containing types are only array or struct types.
+</p>
+
+<pre>
+// invalid array types
+type (
+ T1 [10]T1 // element type of T1 is T1
+ T2 [10]struct{ f T2 } // T2 contains T2 as component of a struct
+ T3 [10]T4 // T3 contains T3 as component of a struct in T4
+ T4 struct{ f T3 } // T4 contains T4 as component of array T3 in a struct
+)
+
+// valid array types
+type (
+ T5 [10]*T5 // T5 contains T5 as component of a pointer
+ T6 [10]func() T6 // T6 contains T6 as component of a function type
+ T7 [10]struct{ f []T7 } // T7 contains T7 as component of a slice in a struct
+)
+</pre>
+
<h3 id="Slice_types">Slice types</h3>
<p>
@@ -1136,6 +1159,29 @@ struct {
}
</pre>
+<p>
+A struct type <code>T</code> may not contain a field of type <code>T</code>,
+or of a type containing <code>T</code> as a component, directly or indirectly,
+if those containing types are only array or struct types.
+</p>
+
+<pre>
+// invalid struct types
+type (
+ T1 struct{ T1 } // T1 contains a field of T1
+ T2 struct{ f [10]T2 } // T2 contains T2 as component of an array
+ T3 struct{ T4 } // T3 contains T3 as component of an array in struct T4
+ T4 struct{ f [10]T3 } // T4 contains T4 as component of struct T3 in an array
+)
+
+// valid struct types
+type (
+ T5 struct{ f *T5 } // T5 contains T5 as component of a pointer
+ T6 struct{ f func() T6 } // T6 contains T6 as component of a function type
+ T7 struct{ f [10][]T7 } // T7 contains T7 as component of a slice in an array
+)
+</pre>
+
<h3 id="Pointer_types">Pointer types</h3>
<p>
@@ -1511,17 +1557,17 @@ type Floatish struct {
</pre>
<p>
-An interface type <code>T</code> may not embed any type element
-that is, contains, or embeds <code>T</code>, recursively.
+An interface type <code>T</code> may not embed a type element
+that is, contains, or embeds <code>T</code>, directly or indirectly.
</p>
<pre>
-// illegal: Bad cannot embed itself
+// illegal: Bad may not embed itself
type Bad interface {
Bad
}
-// illegal: Bad1 cannot embed itself using Bad2
+// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
Bad2
}
@@ -1529,10 +1575,15 @@ type Bad2 interface {
Bad1
}
-// illegal: Bad3 cannot embed a union containing Bad3
+// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
~int | ~string | Bad3
}
+
+// illegal: Bad4 may not embed an array containing Bad4 as element type
+type Bad4 interface {
+ [10]Bad4
+}
</pre>
<h4 id="Implementing_an_interface">Implementing an interface</h4>
@@ -2644,15 +2695,26 @@ of a <a href="#Method_declarations">method declaration</a> associated
with a generic type.
</p>
-<!--
-This section needs to explain if and what kind of cycles are permitted
-using type parameters in a type parameter list.
--->
+<p>
+Within a type parameter list of a generic type <code>T</code>, a type constraint
+may not (directly, or indirectly through the type parameter list of another
+generic type) refer to <code>T</code>.
+</p>
+
+<pre>
+type T1[P T1[P]] … // illegal: T1 refers to itself
+type T2[P interface{ T2[int] }] … // illegal: T2 refers to itself
+type T3[P interface{ m(T3[int])}] … // illegal: T3 refers to itself
+type T4[P T5[P]] … // illegal: T4 refers to T5 and
+type T5[P T4[P]] … // T5 refers to T4
+
+type T6[P int] struct{ f *T6[P] } // ok: reference to T6 is not in type parameter list
+</pre>
<h4 id="Type_constraints">Type constraints</h4>
<p>
-A type constraint is an <a href="#Interface_types">interface</a> that defines the
+A <i>type constraint</i> is an <a href="#Interface_types">interface</a> that defines the
set of permissible type arguments for the respective type parameter and controls the
operations supported by values of that type parameter.
</p>
@@ -2663,7 +2725,7 @@ TypeConstraint = TypeElem .
<p>
If the constraint is an interface literal of the form <code>interface{E}</code> where
-<code>E</code> is an embedded type element (not a method), in a type parameter list
+<code>E</code> is an embedded <a href="#Interface_types">type element</a> (not a method), in a type parameter list
the enclosing <code>interface{ … }</code> may be omitted for convenience:
</p>
@@ -2671,7 +2733,7 @@ the enclosing <code>interface{ … }</code> may be omitted for convenience:
[T []P] // = [T interface{[]P}]
[T ~int] // = [T interface{~int}]
[T int|string] // = [T interface{int|string}]
-type Constraint ~int // illegal: ~int is not inside a type parameter list
+type Constraint ~int // illegal: ~int is not in a type parameter list
</pre>
<!--
@@ -2684,35 +2746,23 @@ other interfaces based on their type sets. But this should get us going for now.
The <a href="#Predeclared_identifiers">predeclared</a>
<a href="#Interface_types">interface type</a> <code>comparable</code>
denotes the set of all non-interface types that are
-<a href="#Comparison_operators">comparable</a>. Specifically,
-a type <code>T</code> implements <code>comparable</code> if:
+<a href="#Comparison_operators">strictly comparable</a>.
</p>
-<ul>
-<li>
- <code>T</code> is not an interface type and <code>T</code> supports the operations
- <code>==</code> and <code>!=</code>; or
-</li>
-<li>
- <code>T</code> is an interface type and each type in <code>T</code>'s
- <a href="#Interface_types">type set</a> implements <code>comparable</code>.
-</li>
-</ul>
-
<p>
-Even though interfaces that are not type parameters can be
-<a href="#Comparison_operators">compared</a>
-(possibly causing a run-time panic) they do not implement
-<code>comparable</code>.
+Even though interfaces that are not type parameters are <a href="#Comparison_operators">comparable</a>,
+they are not strictly comparable and therefore they do not implement <code>comparable</code>.
+However, they <a href="#Satisfying_a_type_constraint">satisfy</a> <code>comparable</code>.
</p>
<pre>
-int // implements comparable
+int // implements comparable (int is strictly comparable)
[]byte // does not implement comparable (slices cannot be compared)
interface{} // does not implement comparable (see above)
-interface{ ~int | ~string } // type parameter only: implements comparable
-interface{ comparable } // type parameter only: implements comparable
-interface{ ~int | ~[]byte } // type parameter only: does not implement comparable (not all types in the type set are comparable)
+interface{ ~int | ~string } // type parameter only: implements comparable (int, string types are stricly comparable)
+interface{ comparable } // type parameter only: implements comparable (comparable implements itself)
+interface{ ~int | ~[]byte } // type parameter only: does not implement comparable (slices are not comparable)
+interface{ ~struct{ any } } // type parameter only: does not implement comparable (field any is not strictly comparable)
</pre>
<p>
@@ -2721,6 +2771,51 @@ The <code>comparable</code> interface and interfaces that (directly or indirectl
values or variables, or components of other, non-interface types.
</p>
+<h4 id="Satisfying_a_type_constraint">Satisfying a type constraint</h4>
+
+<p>
+A type argument <code>T</code><i> satisfies</i> a type constraint <code>C</code>
+if <code>T</code> is an element of the type set defined by <code>C</code>; i.e.,
+if <code>T</code> <a href="#Implementing_an_interface">implements</a> <code>C</code>.
+As an exception, a <a href="#Comparison_operators">strictly comparable</a>
+type constraint may also be satisfied by a <a href="#Comparison_operators">comparable</a>
+(not necessarily strictly comparable) type argument.
+More precisely:
+</p>
+
+<p>
+A type T <i>satisfies</i> a constraint <code>C</code> if
+</p>
+
+<ul>
+<li>
+ <code>T</code> <a href="#Implementing_an_interface">implements</a> <code>C</code>; or
+</li>
+<li>
+ <code>C</code> can be written in the form <code>interface{ comparable; E }</code>,
+ where <code>E</code> is a <a href="#Basic_interfaces">basic interface</a> and
+ <code>T</code> is <a href="#Comparison_operators">comparable</a> and implements <code>E</code>.
+</li>
+</ul>
+
+<pre>
+type argument type constraint // constraint satisfaction
+
+int interface{ ~int } // satisfied: int implements interface{ ~int }
+string comparable // satisfied: string implements comparable (string is stricty comparable)
+[]byte comparable // not satisfied: slices are not comparable
+any interface{ comparable; int } // not satisfied: any does not implement interface{ int }
+any comparable // satisfied: any is comparable and implements the basic interface any
+struct{f any} comparable // satisfied: struct{f any} is comparable and implements the basic interface any
+any interface{ comparable; m() } // not satisfied: any does not implement the basic interface interface{ m() }
+interface{ m() } interface{ comparable; m() } // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }
+</pre>
+
+<p>
+Because of the exception in the constraint satisfaction rule, comparing operands of type parameter type
+may panic at run-time (even though comparable type parameters are always strictly comparable).
+</p>
+
<h3 id="Variable_declarations">Variable declarations</h3>
<p>
@@ -4221,7 +4316,7 @@ including the type parameter list itself and any types in that list.
</li>
<li>
-After substitution, each type argument must <a href="#Interface_types">implement</a>
+After substitution, each type argument must <a href="#Satisfying_a_type_constraint">satisfy</a>
the <a href="#Type_parameter_declarations">constraint</a> (instantiated, if necessary)
of the corresponding type parameter. Otherwise instantiation fails.
</li>
@@ -4235,9 +4330,10 @@ instantiating a function produces a new non-generic function.
<pre>
type parameter list type arguments after substitution
-[P any] int int implements any
-[S ~[]E, E any] []int, int []int implements ~[]int, int implements any
-[P io.Writer] string illegal: string doesn't implement io.Writer
+[P any] int int satisfies any
+[S ~[]E, E any] []int, int []int satisfies ~[]int, int satisfies any
+[P io.Writer] string illegal: string doesn't satisfy io.Writer
+[P comparable] any any satisfies (but does not implement) comparable
</pre>
<p>
@@ -5019,69 +5115,71 @@ to the type of the second operand, or vice versa.
</p>
<p>
The equality operators <code>==</code> and <code>!=</code> apply
-to operands that are <i>comparable</i>.
+to operands of <i>comparable</i> types.
The ordering operators <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;</code>, and <code>&gt;=</code>
-apply to operands that are <i>ordered</i>.
+apply to operands of <i>ordered</i> types.
These terms and the result of the comparisons are defined as follows:
</p>
<ul>
<li>
- Boolean values are comparable.
+ Boolean types are comparable.
Two boolean values are equal if they are either both
<code>true</code> or both <code>false</code>.
</li>
<li>
- Integer values are comparable and ordered, in the usual way.
+ Integer types are comparable and ordered.
+ Two integer values are compared in the usual way.
</li>
<li>
- Floating-point values are comparable and ordered,
- as defined by the IEEE-754 standard.
+ Floating-point types are comparable and ordered.
+ Two floating-point values are compared as defined by the IEEE-754 standard.
</li>
<li>
- Complex values are comparable.
+ Complex types are comparable.
Two complex values <code>u</code> and <code>v</code> are
equal if both <code>real(u) == real(v)</code> and
<code>imag(u) == imag(v)</code>.
</li>
<li>
- String values are comparable and ordered, lexically byte-wise.
+ String types are comparable and ordered.
+ Two string values are compared lexically byte-wise.
</li>
<li>
- Pointer values are comparable.
+ Pointer types are comparable.
Two pointer values are equal if they point to the same variable or if both have value <code>nil</code>.
Pointers to distinct <a href="#Size_and_alignment_guarantees">zero-size</a> variables may or may not be equal.
</li>
<li>
- Channel values are comparable.
+ Channel types are comparable.
Two channel values are equal if they were created by the same call to
<a href="#Making_slices_maps_and_channels"><code>make</code></a>
or if both have value <code>nil</code>.
</li>
<li>
- Interface values are comparable.
+ Interface types that are not type parameters are comparable.
Two interface values are equal if they have <a href="#Type_identity">identical</a> dynamic types
and equal dynamic values or if both have value <code>nil</code>.
</li>
<li>
A value <code>x</code> of non-interface type <code>X</code> and
- a value <code>t</code> of interface type <code>T</code> are comparable when values
- of type <code>X</code> are comparable and
+ a value <code>t</code> of interface type <code>T</code> can be compared
+ if type <code>X</code> is comparable and
<code>X</code> <a href="#Implementing_an_interface">implements</a> <code>T</code>.
They are equal if <code>t</code>'s dynamic type is identical to <code>X</code>
and <code>t</code>'s dynamic value is equal to <code>x</code>.
</li>
<li>
- Struct values are comparable if all their fields are comparable.
+ Struct types are comparable if all their field types are comparable.
Two struct values are equal if their corresponding
non-<a href="#Blank_identifier">blank</a> field values are equal.
The fields are compared in source order, and comparison stops as
@@ -5089,23 +5187,27 @@ These terms and the result of the comparisons are defined as follows:
</li>
<li>
- Array values are comparable if values of the array element type are comparable.
+ Array types are comparable if their array element types are comparable.
Two array values are equal if their corresponding element values are equal.
The elements are compared in ascending index order, and comparison stops
as soon as two element values differ (or all elements have been compared).
</li>
+
+ <li>
+ Type parameters are comparable if they are strictly comparable (see below).
+ </li>
</ul>
<p>
A comparison of two interface values with identical dynamic types
-causes a <a href="#Run_time_panics">run-time panic</a> if values
-of that type are not comparable. This behavior applies not only to direct interface
+causes a <a href="#Run_time_panics">run-time panic</a> if that type
+is not comparable. This behavior applies not only to direct interface
value comparisons but also when comparing arrays of interface values
or structs with interface-valued fields.
</p>
<p>
-Slice, map, and function values are not comparable.
+Slice, map, and function types are not comparable.
However, as a special case, a slice, map, or function value may
be compared to the predeclared identifier <code>nil</code>.
Comparison of pointer, channel, and interface values to <code>nil</code>
@@ -5126,6 +5228,30 @@ var (
)
</pre>
+<p>
+A type is <i>strictly comparable</i> if it is comparable and not an interface
+type nor composed of interface types.
+Specifically:
+</p>
+
+<ul>
+ <li>
+ Boolean, numeric, string, pointer, and channel types are strictly comparable.
+ </li>
+
+ <li>
+ Struct types are strictly comparable if all their field types are strictly comparable.
+ </li>
+
+ <li>
+ Array types are strictly comparable if their array element types are strictly comparable.
+ </li>
+
+ <li>
+ Type parameters are strictly comparable if all types in their type set are strictly comparable.
+ </li>
+</ul>
+
<h3 id="Logical_operators">Logical operators</h3>
<p>
@@ -8013,7 +8139,7 @@ func StringData(str string) *byte
<!--
These conversions also apply to type parameters with suitable core types.
-Determine if we can simply use core type insted of underlying type here,
+Determine if we can simply use core type instead of underlying type here,
of if the general conversion rules take care of this.
-->
diff --git a/misc/cgo/testsanitizers/cc_test.go b/misc/cgo/testsanitizers/cc_test.go
index af85f99325..8eda1372f6 100644
--- a/misc/cgo/testsanitizers/cc_test.go
+++ b/misc/cgo/testsanitizers/cc_test.go
@@ -353,6 +353,9 @@ func configure(sanitizer string) *config {
// Set the debug mode to print the C stack trace.
c.cFlags = append(c.cFlags, "-g")
+ case "fuzzer":
+ c.goFlags = append(c.goFlags, "-tags=libfuzzer", "-gcflags=-d=libfuzzer")
+
default:
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
}
@@ -405,6 +408,13 @@ int main() {
}
`)
+var cLibFuzzerInput = []byte(`
+#include <stddef.h>
+int LLVMFuzzerTestOneInput(char *data, size_t size) {
+ return 0;
+}
+`)
+
func (c *config) checkCSanitizer() (skip bool, err error) {
dir, err := os.MkdirTemp("", c.sanitizer)
if err != nil {
@@ -413,7 +423,12 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
defer os.RemoveAll(dir)
src := filepath.Join(dir, "return0.c")
- if err := os.WriteFile(src, cMain, 0600); err != nil {
+ cInput := cMain
+ if c.sanitizer == "fuzzer" {
+ // libFuzzer generates the main function itself, and uses a different input.
+ cInput = cLibFuzzerInput
+ }
+ if err := os.WriteFile(src, cInput, 0600); err != nil {
return false, fmt.Errorf("failed to write C source file: %v", err)
}
@@ -434,6 +449,11 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
return true, fmt.Errorf("%#q failed: %v\n%s", strings.Join(cmd.Args, " "), err, out)
}
+ if c.sanitizer == "fuzzer" {
+ // For fuzzer, don't try running the test binary. It never finishes.
+ return false, nil
+ }
+
if out, err := exec.Command(dst).CombinedOutput(); err != nil {
if os.IsNotExist(err) {
return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err)
@@ -505,6 +525,10 @@ func (d *tempDir) RemoveAll(t *testing.T) {
}
}
+func (d *tempDir) Base() string {
+ return d.base
+}
+
func (d *tempDir) Join(name string) string {
return filepath.Join(d.base, name)
}
@@ -535,7 +559,7 @@ func hangProneCmd(name string, arg ...string) *exec.Cmd {
}
// mSanSupported is a copy of the function cmd/internal/sys.MSanSupported,
-// because the internal pacakage can't be used here.
+// because the internal package can't be used here.
func mSanSupported(goos, goarch string) bool {
switch goos {
case "linux":
@@ -548,7 +572,7 @@ func mSanSupported(goos, goarch string) bool {
}
// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported,
-// because the internal pacakage can't be used here.
+// because the internal package can't be used here.
func aSanSupported(goos, goarch string) bool {
switch goos {
case "linux":
diff --git a/misc/cgo/testsanitizers/libfuzzer_test.go b/misc/cgo/testsanitizers/libfuzzer_test.go
new file mode 100644
index 0000000000..345751b9c7
--- /dev/null
+++ b/misc/cgo/testsanitizers/libfuzzer_test.go
@@ -0,0 +1,91 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sanitizers_test
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestLibFuzzer(t *testing.T) {
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !libFuzzerSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; libfuzzer option is not supported.", goos, goarch)
+ }
+ config := configure("fuzzer")
+ config.skipIfCSanitizerBroken(t)
+
+ cases := []struct {
+ goSrc string
+ cSrc string
+ expectedError string
+ }{
+ {goSrc: "libfuzzer1.go", expectedError: "panic: found it"},
+ {goSrc: "libfuzzer2.go", cSrc: "libfuzzer2.c", expectedError: "panic: found it"},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.goSrc, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ // build Go code in libfuzzer mode to a c-archive
+ outPath := dir.Join(name)
+ archivePath := dir.Join(name + ".a")
+ mustRun(t, config.goCmd("build", "-buildmode=c-archive", "-o", archivePath, srcPath(tc.goSrc)))
+
+ // build C code (if any) and link with Go code
+ cmd, err := cc(config.cFlags...)
+ if err != nil {
+ t.Fatalf("error running cc: %v", err)
+ }
+ cmd.Args = append(cmd.Args, config.ldFlags...)
+ cmd.Args = append(cmd.Args, "-o", outPath, "-I", dir.Base())
+ if tc.cSrc != "" {
+ cmd.Args = append(cmd.Args, srcPath(tc.cSrc))
+ }
+ cmd.Args = append(cmd.Args, archivePath)
+ mustRun(t, cmd)
+
+ cmd = hangProneCmd(outPath)
+ cmd.Dir = dir.Base()
+ outb, err := cmd.CombinedOutput()
+ out := string(outb)
+ if err == nil {
+ t.Fatalf("fuzzing succeeded unexpectedly; output:\n%s", out)
+ }
+ if !strings.Contains(out, tc.expectedError) {
+ t.Errorf("exited without expected error %q; got\n%s", tc.expectedError, out)
+ }
+ })
+ }
+}
+
+// libFuzzerSupported is a copy of the function internal/platform.FuzzInstrumented,
+// because the internal package can't be used here.
+func libFuzzerSupported(goos, goarch string) bool {
+ switch goarch {
+ case "amd64", "arm64":
+ // TODO(#14565): support more architectures.
+ switch goos {
+ case "darwin", "freebsd", "linux", "windows":
+ return true
+ default:
+ return false
+ }
+ default:
+ return false
+ }
+}
diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer1.go b/misc/cgo/testsanitizers/testdata/libfuzzer1.go
new file mode 100644
index 0000000000..d178fb1ca0
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/libfuzzer1.go
@@ -0,0 +1,16 @@
+package main
+
+import "C"
+
+import "unsafe"
+
+//export LLVMFuzzerTestOneInput
+func LLVMFuzzerTestOneInput(p unsafe.Pointer, sz C.int) C.int {
+ b := C.GoBytes(p, sz)
+ if len(b) >= 6 && b[0] == 'F' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' && b[4] == 'M' && b[5] == 'e' {
+ panic("found it")
+ }
+ return 0
+}
+
+func main() {}
diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer2.c b/misc/cgo/testsanitizers/testdata/libfuzzer2.c
new file mode 100644
index 0000000000..567ff5a1cc
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/libfuzzer2.c
@@ -0,0 +1,11 @@
+#include <stddef.h>
+
+#include "libfuzzer2.h"
+
+int LLVMFuzzerTestOneInput(char *data, size_t size) {
+ if (size > 0 && data[0] == 'H')
+ if (size > 1 && data[1] == 'I')
+ if (size > 2 && data[2] == '!')
+ FuzzMe(data, size);
+ return 0;
+}
diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer2.go b/misc/cgo/testsanitizers/testdata/libfuzzer2.go
new file mode 100644
index 0000000000..c7a4325976
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/libfuzzer2.go
@@ -0,0 +1,16 @@
+package main
+
+import "C"
+
+import "unsafe"
+
+//export FuzzMe
+func FuzzMe(p unsafe.Pointer, sz C.int) {
+ b := C.GoBytes(p, sz)
+ b = b[3:]
+ if len(b) >= 4 && b[0] == 'f' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' {
+ panic("found it")
+ }
+}
+
+func main() {}
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index cd8a144d73..b14fb1cb3a 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -1105,3 +1105,15 @@ func TestIssue47873(t *testing.T) {
goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a")
goCmd(t, "run", "-linkshared", "./issue47837/main")
}
+
+// Test that we can build std in shared mode.
+func TestStd(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skip in short mode")
+ }
+ t.Parallel()
+ // Use a temporary pkgdir to not interfere with other tests, and not write to GOROOT.
+ // Cannot use goCmd as it runs with cloned GOROOT which is incomplete.
+ runWithEnv(t, "building std", []string{"GOROOT=" + oldGOROOT},
+ filepath.Join(oldGOROOT, "bin", "go"), "install", "-buildmode=shared", "-pkgdir="+t.TempDir(), "std")
+}
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 52a3150fc4..82a5a5a293 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -45,15 +45,6 @@ func NewReader(r io.Reader) *Reader {
// Any remaining data in the current file is automatically discarded.
//
// io.EOF is returned at the end of the input.
-//
-// ErrInsecurePath and a valid *Header are returned if the next file's name is:
-//
-// - absolute;
-// - a relative path escaping the current directory, such as "../a"; or
-// - on Windows, a reserved file name such as "NUL".
-//
-// The caller may ignore the ErrInsecurePath error,
-// but is then responsible for sanitizing paths as appropriate.
func (tr *Reader) Next() (*Header, error) {
if tr.err != nil {
return nil, tr.err
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 10e835fe86..a2ae74e541 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -87,17 +87,6 @@ func OpenReader(name string) (*ReadCloser, error) {
// NewReader returns a new Reader reading from r, which is assumed to
// have the given size in bytes.
-//
-// ErrInsecurePath and a valid *Reader are returned if the names of any
-// files in the archive:
-//
-// - are absolute;
-// - are a relative path escaping the current directory, such as "../a";
-// - contain a backslash (\) character; or
-// - on Windows, are a reserved file name such as "NUL".
-//
-// The caller may ignore the ErrInsecurePath error,
-// but is then responsible for sanitizing paths as appropriate.
func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
if size < 0 {
return nil, errors.New("zip: size cannot be negative")
diff --git a/src/cmd/compile/internal/devirtualize/devirtualize.go b/src/cmd/compile/internal/devirtualize/devirtualize.go
index 7350a6f171..554e935c3e 100644
--- a/src/cmd/compile/internal/devirtualize/devirtualize.go
+++ b/src/cmd/compile/internal/devirtualize/devirtualize.go
@@ -152,4 +152,7 @@ func Call(call *ir.CallExpr) {
default:
call.SetType(ft.Results())
}
+
+ // Desugar OCALLMETH, if we created one (#57309).
+ typecheck.FixMethodCall(call)
}
diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64.rules b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
index 727204d80a..0c5a2e66a8 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
@@ -342,9 +342,9 @@
(FCMPD x (FMOVDconst [0])) => (FCMPD0 x)
(FCMPD (FMOVDconst [0]) x) => (InvertFlags (FCMPD0 x))
-// CSEL needs a flag-generating argument. Synthesize a CMPW if necessary.
+// CSEL needs a flag-generating argument. Synthesize a TSTW if necessary.
(CondSelect x y boolval) && flagArg(boolval) != nil => (CSEL [boolval.Op] x y flagArg(boolval))
-(CondSelect x y boolval) && flagArg(boolval) == nil => (CSEL [OpARM64NotEqual] x y (CMPWconst [0] boolval))
+(CondSelect x y boolval) && flagArg(boolval) == nil => (CSEL [OpARM64NotEqual] x y (TSTWconst [1] boolval))
(OffPtr [off] ptr:(SP)) && is32Bit(off) => (MOVDaddr [int32(off)] ptr)
(OffPtr [off] ptr) => (ADDconst [off] ptr)
diff --git a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
index 2810f0afe1..1caaf13600 100644
--- a/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/LOONG64.rules
@@ -400,7 +400,8 @@
(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...)
-(AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...)
+(AtomicCompareAndSwap32 ptr old new mem) => (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
+(AtomicCompareAndSwap64 ...) => (LoweredAtomicCas64 ...)
// checks
(NilCheck ...) => (LoweredNilCheck ...)
diff --git a/src/cmd/compile/internal/ssa/_gen/MIPS64.rules b/src/cmd/compile/internal/ssa/_gen/MIPS64.rules
index 17634afd72..a594df2b26 100644
--- a/src/cmd/compile/internal/ssa/_gen/MIPS64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/MIPS64.rules
@@ -392,7 +392,8 @@
(AtomicAdd(32|64) ...) => (LoweredAtomicAdd(32|64) ...)
-(AtomicCompareAndSwap(32|64) ...) => (LoweredAtomicCas(32|64) ...)
+(AtomicCompareAndSwap32 ptr old new mem) => (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
+(AtomicCompareAndSwap64 ...) => (LoweredAtomicCas64 ...)
// checks
(NilCheck ...) => (LoweredNilCheck ...)
diff --git a/src/cmd/compile/internal/ssa/_gen/PPC64.rules b/src/cmd/compile/internal/ssa/_gen/PPC64.rules
index aee53d4f0f..5a68de0ca4 100644
--- a/src/cmd/compile/internal/ssa/_gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/PPC64.rules
@@ -409,9 +409,9 @@
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(XOR x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (Select1 <types.TypeFlags> (XORCC x y)) yes no)
// Only lower after bool is lowered. It should always lower. This helps ensure the folding below happens reliably.
-(CondSelect x y bool) && flagArg(bool) == nil => (ISEL [6] x y (CMPWconst [0] bool))
+(CondSelect x y bool) && flagArg(bool) == nil => (ISEL [6] x y (Select1 <types.TypeFlags> (ANDCCconst [1] bool)))
// Fold any CR -> GPR -> CR transfers when applying the above rule.
-(ISEL [6] x y (CMPWconst [0] (ISELB [c] one cmp))) => (ISEL [c] x y cmp)
+(ISEL [6] x y (Select1 (ANDCCconst [1] (ISELB [c] one cmp)))) => (ISEL [c] x y cmp)
// Lowering loads
(Load <t> ptr mem) && (is64BitInt(t) || isPtr(t)) => (MOVDload ptr mem)
diff --git a/src/cmd/compile/internal/ssa/_gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/_gen/PPC64Ops.go
index baa783e30b..2d651dd780 100644
--- a/src/cmd/compile/internal/ssa/_gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/_gen/PPC64Ops.go
@@ -8,8 +8,8 @@ import "strings"
// Notes:
// - Less-than-64-bit integer types live in the low portion of registers.
-// For now, the upper portion is junk; sign/zero-extension might be optimized in the future, but not yet.
-// - Boolean types are zero or 1; stored in a byte, but loaded with AMOVBZ so the upper bytes of a register are zero.
+// The upper portion is junk.
+// - Boolean types are zero or 1; stored in a byte, with upper bytes of the register containing junk.
// - *const instructions may use a constant larger than the instruction can encode.
// In this case the assembler expands to multiple instructions and uses tmp
// register (R31).
diff --git a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
index 78c3375e2d..59f71be5ba 100644
--- a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules
@@ -577,7 +577,7 @@
(AtomicAnd32 ...) => (LoweredAtomicAnd32 ...)
-(AtomicCompareAndSwap32 ...) => (LoweredAtomicCas32 ...)
+(AtomicCompareAndSwap32 ptr old new mem) => (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
(AtomicCompareAndSwap64 ...) => (LoweredAtomicCas64 ...)
(AtomicExchange32 ...) => (LoweredAtomicExchange32 ...)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index d7386729e7..e82a49c331 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -23561,7 +23561,7 @@ func rewriteValueARM64_OpCondSelect(v *Value) bool {
}
// match: (CondSelect x y boolval)
// cond: flagArg(boolval) == nil
- // result: (CSEL [OpARM64NotEqual] x y (CMPWconst [0] boolval))
+ // result: (CSEL [OpARM64NotEqual] x y (TSTWconst [1] boolval))
for {
x := v_0
y := v_1
@@ -23571,8 +23571,8 @@ func rewriteValueARM64_OpCondSelect(v *Value) bool {
}
v.reset(OpARM64CSEL)
v.AuxInt = opToAuxInt(OpARM64NotEqual)
- v0 := b.NewValue0(v.Pos, OpARM64CMPWconst, types.TypeFlags)
- v0.AuxInt = int32ToAuxInt(0)
+ v0 := b.NewValue0(v.Pos, OpARM64TSTWconst, types.TypeFlags)
+ v0.AuxInt = int32ToAuxInt(1)
v0.AddArg(boolval)
v.AddArg3(x, y, v0)
return true
diff --git a/src/cmd/compile/internal/ssa/rewriteLOONG64.go b/src/cmd/compile/internal/ssa/rewriteLOONG64.go
index 26d6594fef..f6da0b7ff0 100644
--- a/src/cmd/compile/internal/ssa/rewriteLOONG64.go
+++ b/src/cmd/compile/internal/ssa/rewriteLOONG64.go
@@ -52,8 +52,7 @@ func rewriteValueLOONG64(v *Value) bool {
v.Op = OpLOONG64LoweredAtomicAdd64
return true
case OpAtomicCompareAndSwap32:
- v.Op = OpLOONG64LoweredAtomicCas32
- return true
+ return rewriteValueLOONG64_OpAtomicCompareAndSwap32(v)
case OpAtomicCompareAndSwap64:
v.Op = OpLOONG64LoweredAtomicCas64
return true
@@ -705,6 +704,27 @@ func rewriteValueLOONG64_OpAddr(v *Value) bool {
return true
}
}
+func rewriteValueLOONG64_OpAtomicCompareAndSwap32(v *Value) bool {
+ v_3 := v.Args[3]
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (AtomicCompareAndSwap32 ptr old new mem)
+ // result: (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
+ for {
+ ptr := v_0
+ old := v_1
+ new := v_2
+ mem := v_3
+ v.reset(OpLOONG64LoweredAtomicCas32)
+ v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64)
+ v0.AddArg(old)
+ v.AddArg4(ptr, v0, new, mem)
+ return true
+ }
+}
func rewriteValueLOONG64_OpAvg64u(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
index 998b27dbb5..c0d42b55f5 100644
--- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
@@ -52,8 +52,7 @@ func rewriteValueMIPS64(v *Value) bool {
v.Op = OpMIPS64LoweredAtomicAdd64
return true
case OpAtomicCompareAndSwap32:
- v.Op = OpMIPS64LoweredAtomicCas32
- return true
+ return rewriteValueMIPS64_OpAtomicCompareAndSwap32(v)
case OpAtomicCompareAndSwap64:
v.Op = OpMIPS64LoweredAtomicCas64
return true
@@ -697,6 +696,27 @@ func rewriteValueMIPS64_OpAddr(v *Value) bool {
return true
}
}
+func rewriteValueMIPS64_OpAtomicCompareAndSwap32(v *Value) bool {
+ v_3 := v.Args[3]
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (AtomicCompareAndSwap32 ptr old new mem)
+ // result: (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
+ for {
+ ptr := v_0
+ old := v_1
+ new := v_2
+ mem := v_3
+ v.reset(OpMIPS64LoweredAtomicCas32)
+ v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64)
+ v0.AddArg(old)
+ v.AddArg4(ptr, v0, new, mem)
+ return true
+ }
+}
func rewriteValueMIPS64_OpAvg64u(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 8b5ea14757..bc593128da 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -1172,9 +1172,10 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ typ := &b.Func.Config.Types
// match: (CondSelect x y bool)
// cond: flagArg(bool) == nil
- // result: (ISEL [6] x y (CMPWconst [0] bool))
+ // result: (ISEL [6] x y (Select1 <types.TypeFlags> (ANDCCconst [1] bool)))
for {
x := v_0
y := v_1
@@ -1184,9 +1185,11 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool {
}
v.reset(OpPPC64ISEL)
v.AuxInt = int32ToAuxInt(6)
- v0 := b.NewValue0(v.Pos, OpPPC64CMPWconst, types.TypeFlags)
- v0.AuxInt = int32ToAuxInt(0)
- v0.AddArg(bool)
+ v0 := b.NewValue0(v.Pos, OpSelect1, types.TypeFlags)
+ v1 := b.NewValue0(v.Pos, OpPPC64ANDCCconst, types.NewTuple(typ.Int, types.TypeFlags))
+ v1.AuxInt = int64ToAuxInt(1)
+ v1.AddArg(bool)
+ v0.AddArg(v1)
v.AddArg3(x, y, v0)
return true
}
@@ -5355,7 +5358,7 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool {
v.AddArg(v0)
return true
}
- // match: (ISEL [6] x y (CMPWconst [0] (ISELB [c] one cmp)))
+ // match: (ISEL [6] x y (Select1 (ANDCCconst [1] (ISELB [c] one cmp))))
// result: (ISEL [c] x y cmp)
for {
if auxIntToInt32(v.AuxInt) != 6 {
@@ -5363,15 +5366,19 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool {
}
x := v_0
y := v_1
- if v_2.Op != OpPPC64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 {
+ if v_2.Op != OpSelect1 {
break
}
v_2_0 := v_2.Args[0]
- if v_2_0.Op != OpPPC64ISELB {
+ if v_2_0.Op != OpPPC64ANDCCconst || auxIntToInt64(v_2_0.AuxInt) != 1 {
+ break
+ }
+ v_2_0_0 := v_2_0.Args[0]
+ if v_2_0_0.Op != OpPPC64ISELB {
break
}
- c := auxIntToInt32(v_2_0.AuxInt)
- cmp := v_2_0.Args[1]
+ c := auxIntToInt32(v_2_0_0.AuxInt)
+ cmp := v_2_0_0.Args[1]
v.reset(OpPPC64ISEL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, cmp)
diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
index f94e90f01a..961230d8bb 100644
--- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go
+++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
@@ -61,8 +61,7 @@ func rewriteValueRISCV64(v *Value) bool {
case OpAtomicAnd8:
return rewriteValueRISCV64_OpAtomicAnd8(v)
case OpAtomicCompareAndSwap32:
- v.Op = OpRISCV64LoweredAtomicCas32
- return true
+ return rewriteValueRISCV64_OpAtomicCompareAndSwap32(v)
case OpAtomicCompareAndSwap64:
v.Op = OpRISCV64LoweredAtomicCas64
return true
@@ -776,6 +775,27 @@ func rewriteValueRISCV64_OpAtomicAnd8(v *Value) bool {
return true
}
}
+func rewriteValueRISCV64_OpAtomicCompareAndSwap32(v *Value) bool {
+ v_3 := v.Args[3]
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (AtomicCompareAndSwap32 ptr old new mem)
+ // result: (LoweredAtomicCas32 ptr (SignExt32to64 old) new mem)
+ for {
+ ptr := v_0
+ old := v_1
+ new := v_2
+ mem := v_3
+ v.reset(OpRISCV64LoweredAtomicCas32)
+ v0 := b.NewValue0(v.Pos, OpSignExt32to64, typ.Int64)
+ v0.AddArg(old)
+ v.AddArg4(ptr, v0, new, mem)
+ return true
+ }
+}
func rewriteValueRISCV64_OpAtomicOr8(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 40d6e5da69..9a0348e025 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -1103,26 +1103,50 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
return
}
- // TODO(gri) make canMix more efficient - called for each binary operation
- canMix := func(x, y *operand) bool {
+ // mayConvert reports whether the operands x and y may
+ // possibly have matching types after converting one
+ // untyped operand to the type of the other.
+ // If mayConvert returns true, we try to convert the
+ // operands to each other's types, and if that fails
+ // we report a conversion failure.
+ // If mayConvert returns false, we continue without an
+ // attempt at conversion, and if the operand types are
+ // not compatible, we report a type mismatch error.
+ mayConvert := func(x, y *operand) bool {
+ // If both operands are typed, there's no need for an implicit conversion.
+ if isTyped(x.typ) && isTyped(y.typ) {
+ return false
+ }
+ // An untyped operand may convert to its default type when paired with an empty interface
+ // TODO(gri) This should only matter for comparisons (the only binary operation that is
+ // valid with interfaces), but in that case the assignability check should take
+ // care of the conversion. Verify and possibly eliminate this extra test.
if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
return true
}
+ // A boolean type can only convert to another boolean type.
if allBoolean(x.typ) != allBoolean(y.typ) {
return false
}
+ // A string type can only convert to another string type.
if allString(x.typ) != allString(y.typ) {
return false
}
- if x.isNil() && !hasNil(y.typ) {
- return false
+ // Untyped nil can only convert to a type that has a nil.
+ if x.isNil() {
+ return hasNil(y.typ)
+ }
+ if y.isNil() {
+ return hasNil(x.typ)
}
- if y.isNil() && !hasNil(x.typ) {
+ // An untyped operand cannot convert to a pointer.
+ // TODO(gri) generalize to type parameters
+ if isPointer(x.typ) || isPointer(y.typ) {
return false
}
return true
}
- if canMix(x, &y) {
+ if mayConvert(x, &y) {
check.convertUntyped(x, y.typ)
if x.mode == invalid {
return
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index 1075457aca..5750ece32f 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -89,34 +89,22 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// f(p)
// }
//
- // We can turn the first example into the second example by renaming type
- // parameters in the original signature to give them a new identity. As an
- // optimization, we do this only for self-recursive calls.
-
- // We can detect if we are in a self-recursive call by comparing the
- // identity of the first type parameter in the current function with the
- // first type parameter in tparams. This works because type parameters are
- // unique to their type parameter list.
- selfRecursive := check.sig != nil && check.sig.tparams.Len() > 0 && tparams[0] == check.sig.tparams.At(0)
-
- if selfRecursive {
- // In self-recursive inference, rename the type parameters with new type
- // parameters that are the same but for their pointer identity.
- tparams2 := make([]*TypeParam, len(tparams))
- for i, tparam := range tparams {
- tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
- tparams2[i] = NewTypeParam(tname, nil)
- tparams2[i].index = tparam.index // == i
- }
-
- renameMap := makeRenameMap(tparams, tparams2)
- for i, tparam := range tparams {
- tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context())
- }
+ // We turn the first example into the second example by renaming type
+ // parameters in the original signature to give them a new identity.
+ tparams2 := make([]*TypeParam, len(tparams))
+ for i, tparam := range tparams {
+ tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
+ tparams2[i] = NewTypeParam(tname, nil)
+ tparams2[i].index = tparam.index // == i
+ }
- tparams = tparams2
- params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple)
+ renameMap := makeRenameMap(tparams, tparams2)
+ for i, tparam := range tparams {
+ tparams2[i].bound = check.subst(pos, tparam.bound, renameMap, nil, check.context())
}
+
+ tparams = tparams2
+ params = check.subst(pos, params, renameMap, nil, check.context()).(*Tuple)
}
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 391ea8cd79..673cadca90 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -352,7 +352,7 @@ func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if Comparable(t.typ) {
+ if comparable(t.typ, false /* strictly comparable */, nil, nil) {
terms[i] = t
i++
}
diff --git a/src/cmd/covdata/tool_test.go b/src/cmd/covdata/tool_test.go
index 9396266776..42334eae94 100644
--- a/src/cmd/covdata/tool_test.go
+++ b/src/cmd/covdata/tool_test.go
@@ -111,6 +111,8 @@ func emitFile(t *testing.T, dst, src string) {
}
}
+const mainPkgPath = "prog"
+
func buildProg(t *testing.T, prog string, dir string, tag string, flags []string) (string, string) {
// Create subdirs.
subdir := filepath.Join(dir, prog+"dir"+tag)
@@ -132,7 +134,7 @@ func buildProg(t *testing.T, prog string, dir string, tag string, flags []string
// Emit go.mod.
mod := filepath.Join(subdir, "go.mod")
- modsrc := "\nmodule prog\n\ngo 1.19\n"
+ modsrc := "\nmodule " + mainPkgPath + "\n\ngo 1.19\n"
if err := os.WriteFile(mod, []byte(modsrc), 0666); err != nil {
t.Fatal(err)
}
@@ -305,7 +307,7 @@ func runToolOp(t *testing.T, s state, op string, args []string) []string {
func testDump(t *testing.T, s state) {
// Run the dumper on the two dirs we generated.
- dargs := []string{"-pkg=main", "-live", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
+ dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "debugdump", dargs)
// Sift through the output to make sure it has some key elements.
@@ -319,7 +321,7 @@ func testDump(t *testing.T, s state) {
},
{
"main package",
- regexp.MustCompile(`^Package path: main\s*$`),
+ regexp.MustCompile(`^Package path: ` + mainPkgPath + `\s*$`),
},
{
"main function",
@@ -337,7 +339,7 @@ func testDump(t *testing.T, s state) {
}
}
if !found {
- t.Errorf("dump output regexp match failed for %s", testpoint.tag)
+ t.Errorf("dump output regexp match failed for %q", testpoint.tag)
bad = true
}
}
@@ -348,7 +350,7 @@ func testDump(t *testing.T, s state) {
func testPercent(t *testing.T, s state) {
// Run the dumper on the two dirs we generated.
- dargs := []string{"-pkg=main", "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
+ dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "percent", dargs)
// Sift through the output to make sure it has the needful.
@@ -380,11 +382,12 @@ func testPercent(t *testing.T, s state) {
dumplines(lines)
}
}
+
func testPkgList(t *testing.T, s state) {
dargs := []string{"-i=" + s.outdirs[0] + "," + s.outdirs[1]}
lines := runToolOp(t, s, "pkglist", dargs)
- want := []string{"main", "prog/dep"}
+ want := []string{mainPkgPath, mainPkgPath + "/dep"}
bad := false
if len(lines) != 2 {
t.Errorf("expect pkglist to return two lines")
@@ -405,7 +408,7 @@ func testPkgList(t *testing.T, s state) {
func testTextfmt(t *testing.T, s state) {
outf := s.dir + "/" + "t.txt"
- dargs := []string{"-pkg=main", "-i=" + s.outdirs[0] + "," + s.outdirs[1],
+ dargs := []string{"-pkg=" + mainPkgPath, "-i=" + s.outdirs[0] + "," + s.outdirs[1],
"-o", outf}
lines := runToolOp(t, s, "textfmt", dargs)
@@ -426,7 +429,7 @@ func testTextfmt(t *testing.T, s state) {
dumplines(lines[0:10])
t.Errorf("textfmt: want %s got %s", want0, lines[0])
}
- want1 := "prog/prog1.go:13.14,15.2 1 1"
+ want1 := mainPkgPath + "/prog1.go:13.14,15.2 1 1"
if lines[1] != want1 {
dumplines(lines[0:10])
t.Errorf("textfmt: want %s got %s", want1, lines[1])
@@ -571,7 +574,7 @@ func testMergeSimple(t *testing.T, s state, indir1, indir2, tag string) {
nonzero: true,
},
}
- flags := []string{"-live", "-pkg=main"}
+ flags := []string{"-live", "-pkg=" + mainPkgPath}
runDumpChecks(t, s, outdir, flags, testpoints)
}
@@ -585,7 +588,7 @@ func testMergeSelect(t *testing.T, s state, indir1, indir2 string, tag string) {
// based on package.
ins := fmt.Sprintf("-i=%s,%s", indir1, indir2)
out := fmt.Sprintf("-o=%s", outdir)
- margs := []string{"-pkg=prog/dep", ins, out}
+ margs := []string{"-pkg=" + mainPkgPath + "/dep", ins, out}
lines := runToolOp(t, s, "merge", margs)
if len(lines) != 0 {
t.Errorf("merge run produced %d lines of unexpected output", len(lines))
@@ -600,9 +603,9 @@ func testMergeSelect(t *testing.T, s state, indir1, indir2 string, tag string) {
t.Fatalf("dump run produced no output")
}
want := map[string]int{
- "Package path: prog/dep": 0,
- "Func: Dep1": 0,
- "Func: PDep": 0,
+ "Package path: " + mainPkgPath + "/dep": 0,
+ "Func: Dep1": 0,
+ "Func: PDep": 0,
}
bad := false
for _, line := range lines {
@@ -696,7 +699,7 @@ func testMergeCombinePrograms(t *testing.T, s state) {
},
}
- flags := []string{"-live", "-pkg=main"}
+ flags := []string{"-live", "-pkg=" + mainPkgPath}
runDumpChecks(t, s, moutdir, flags, testpoints)
}
@@ -717,7 +720,7 @@ func testSubtract(t *testing.T, s state) {
}
// Dump the files in the subtract output dir and examine the result.
- dargs := []string{"-pkg=main", "-live", "-i=" + soutdir}
+ dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + soutdir}
lines = runToolOp(t, s, "debugdump", dargs)
if len(lines) == 0 {
t.Errorf("dump run produced no output")
@@ -774,7 +777,7 @@ func testIntersect(t *testing.T, s state, indir1, indir2, tag string) {
}
// Dump the files in the subtract output dir and examine the result.
- dargs := []string{"-pkg=main", "-live", "-i=" + ioutdir}
+ dargs := []string{"-pkg=" + mainPkgPath, "-live", "-i=" + ioutdir}
lines = runToolOp(t, s, "debugdump", dargs)
if len(lines) == 0 {
t.Errorf("dump run produced no output")
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index 989c109a79..f4f225ef20 100644
--- a/src/cmd/cover/cover.go
+++ b/src/cmd/cover/cover.go
@@ -542,9 +542,6 @@ func annotate(names []string) {
if *pkgcfg != "" {
pp := pkgconfig.PkgPath
pn := pkgconfig.PkgName
- if pn == "main" {
- pp = "main"
- }
mp := pkgconfig.ModulePath
mdb, err := encodemeta.NewCoverageMetaDataBuilder(pp, pn, mp)
if err != nil {
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 5a56009829..0051970cfc 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -1306,6 +1306,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
cmd.Env = env
}
+ base.StartSigHandlers()
t0 := time.Now()
err = cmd.Start()
@@ -1314,7 +1315,6 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
// running.
if err == nil {
tick := time.NewTimer(testKillTimeout)
- base.StartSigHandlers()
done := make(chan error)
go func() {
done <- cmd.Wait()
diff --git a/src/cmd/go/terminal_test.go b/src/cmd/go/terminal_test.go
index 03ca772700..a5ad9191c2 100644
--- a/src/cmd/go/terminal_test.go
+++ b/src/cmd/go/terminal_test.go
@@ -71,31 +71,37 @@ func runTerminalPassthrough(t *testing.T, r, w *os.File) (stdout, stderr bool) {
cmd.Env = append(cmd.Environ(), "GO_TEST_TERMINAL_PASSTHROUGH=1")
cmd.Stdout = w
cmd.Stderr = w
+
+ // The behavior of reading from a PTY after the child closes it is very
+ // strange: on Linux, Read returns EIO, and on at least some versions of
+ // macOS, unread output may be discarded (see https://go.dev/issue/57141).
+ //
+ // To avoid that situation, we keep the child process running until the
+ // parent has finished reading from the PTY, at which point we unblock the
+ // child by closing its stdin pipe.
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+
t.Logf("running %s", cmd)
- err := cmd.Start()
+ err = cmd.Start()
if err != nil {
t.Fatalf("starting subprocess: %s", err)
}
w.Close()
- // Read the subprocess output. The behavior of reading from a PTY after the
- // child closes it is very strange (e.g., on Linux, read returns EIO), so we
- // ignore errors as long as we get everything we need. We still try to read
- // all of the output so we can report it in case of failure.
- buf, err := io.ReadAll(r)
- if len(buf) != 2 || !(buf[0] == '1' || buf[0] == 'X') || !(buf[1] == '2' || buf[1] == 'X') {
- t.Errorf("expected exactly 2 bytes matching [1X][2X]")
- if err != nil {
- // An EIO here might be expected depending on OS.
- t.Errorf("error reading from subprocess: %s", err)
+ t.Cleanup(func() {
+ stdin.Close()
+ if err := cmd.Wait(); err != nil {
+ t.Errorf("suprocess failed with: %s", err)
}
- }
- err = cmd.Wait()
- if err != nil {
- t.Errorf("suprocess failed with: %s", err)
- }
- if t.Failed() {
- t.Logf("subprocess output:\n%s", string(buf))
- t.FailNow()
+ })
+
+ buf := make([]byte, 2)
+ n, err := io.ReadFull(r, buf)
+ if err != nil || !(buf[0] == '1' || buf[0] == 'X') || !(buf[1] == '2' || buf[1] == 'X') {
+ t.Logf("read error: %v", err)
+ t.Fatalf("expected 2 bytes matching `[1X][2X]`; got %q", buf[:n])
}
return buf[0] == '1', buf[1] == '2'
}
@@ -106,14 +112,19 @@ func init() {
}
if term.IsTerminal(1) {
- print("1")
+ os.Stdout.WriteString("1")
} else {
- print("X")
+ os.Stdout.WriteString("X")
}
if term.IsTerminal(2) {
- print("2")
+ os.Stdout.WriteString("2")
} else {
- print("X")
+ os.Stdout.WriteString("X")
}
+
+ // Before exiting, wait for the parent process to read the PTY output,
+ // at which point it will close stdin.
+ io.Copy(io.Discard, os.Stdin)
+
os.Exit(0)
}
diff --git a/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt b/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt
index 4748a85f5e..ba382639e9 100644
--- a/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt
+++ b/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt
@@ -26,7 +26,7 @@ env GOCOVERDIR=$SAVEGOCOVERDIR
# Check to make sure we instrumented just the main package, not
# any dependencies.
go tool covdata pkglist -i=$WORK/covdata
-stdout main
+stdout cmd/nm
! stdout cmd/internal/goobj pkglist.txt
# ... now collect a coverage profile from a Go file
@@ -41,7 +41,7 @@ env GOCOVERDIR=$SAVEGOCOVERDIR
# Check to make sure we instrumented just the main package.
go tool covdata pkglist -i=$WORK/covdata2
-stdout main
+stdout command-line-arguments
! stdout fmt
-- go.mod --
diff --git a/src/cmd/go/testdata/script/cover_main_import_path.txt b/src/cmd/go/testdata/script/cover_main_import_path.txt
new file mode 100644
index 0000000000..3a2f3c3ee2
--- /dev/null
+++ b/src/cmd/go/testdata/script/cover_main_import_path.txt
@@ -0,0 +1,54 @@
+
+# This test is intended to verify that coverage reporting is consistent
+# between "go test -cover" and "go build -cover" with respect to how
+# the "main" package is handled. See issue 57169 for details.
+
+[short] skip
+
+# Build this program with -cover and run to collect a profile.
+
+go build -cover -o $WORK/prog.exe .
+
+# Save off old GOCOVERDIR setting
+env SAVEGOCOVERDIR=$GOCOVERDIR
+
+mkdir $WORK/covdata
+env GOCOVERDIR=$WORK/covdata
+exec $WORK/prog.exe
+
+# Restore previous GOCOVERDIR setting
+env GOCOVERDIR=$SAVEGOCOVERDIR
+
+# Report percent lines covered.
+go tool covdata percent -i=$WORK/covdata
+stdout '\s*mainwithtest\s+coverage:'
+! stdout 'main\s+coverage:'
+
+# Go test -cover should behave the same way.
+go test -cover .
+stdout 'ok\s+mainwithtest\s+\S+\s+coverage:'
+! stdout 'ok\s+main\s+.*'
+
+
+-- go.mod --
+module mainwithtest
+
+go 1.20
+-- mymain.go --
+package main
+
+func main() {
+ println("hi mom")
+}
+
+func Mainer() int {
+ return 42
+}
+-- main_test.go --
+package main
+
+import "testing"
+
+func TestCoverage(t *testing.T) {
+ println(Mainer())
+}
diff --git a/src/cmd/go/testdata/script/test2json_interrupt.txt b/src/cmd/go/testdata/script/test2json_interrupt.txt
index 5828e86136..763c336991 100644
--- a/src/cmd/go/testdata/script/test2json_interrupt.txt
+++ b/src/cmd/go/testdata/script/test2json_interrupt.txt
@@ -7,8 +7,8 @@ stdout -count=1 '"Action":"pass","Package":"example","Test":"FuzzInterrupt"'
stdout -count=1 '"Action":"pass","Package":"example","Elapsed":'
mkdir $WORK/fuzzcache
-go test -c . -fuzz=. -o test2json_interrupt_obj
-? go tool test2json -p example -t ./test2json_interrupt_obj -test.v -test.paniconexit0 -test.fuzzcachedir $WORK/fuzzcache -test.fuzz FuzzInterrupt -test.run '^$' -test.parallel 1
+go test -c . -fuzz=. -o example_test.exe
+? go tool test2json -p example -t ./example_test.exe -test.v -test.paniconexit0 -test.fuzzcachedir $WORK/fuzzcache -test.fuzz FuzzInterrupt -test.run '^$' -test.parallel 1
stdout -count=1 '"Action":"pass","Package":"example","Test":"FuzzInterrupt"'
stdout -count=1 '"Action":"pass","Package":"example","Elapsed":'
@@ -37,19 +37,22 @@ func FuzzInterrupt(f *testing.F) {
os.Setenv("GO_TEST_INTERRUPT_PIDS", fmt.Sprintf("%d,%d", ppid, pid))
}
+ sentInterrupt := false
f.Fuzz(func(t *testing.T, orig string) {
- // Simulate a ctrl-C on the keyboard by sending SIGINT
- // to the main test process and its parent.
- for _, pid := range strings.Split(pids, ",") {
- i, err := strconv.Atoi(pid)
- if err != nil {
- t.Fatal(err)
- }
- if p, err := os.FindProcess(i); err == nil {
- p.Signal(os.Interrupt)
- time.Sleep(10 * time.Millisecond)
- pids = "" // Only interrupt once.
+ if !sentInterrupt {
+ // Simulate a ctrl-C on the keyboard by sending SIGINT
+ // to the main test process and its parent.
+ for _, pid := range strings.Split(pids, ",") {
+ i, err := strconv.Atoi(pid)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if p, err := os.FindProcess(i); err == nil {
+ p.Signal(os.Interrupt)
+ sentInterrupt = true // Only send interrupts once.
+ }
}
}
+ time.Sleep(1 * time.Millisecond) // Delay the fuzzer a bit to avoid wasting CPU.
})
}
diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go
index f46045bc9d..c5e2117f90 100644
--- a/src/cmd/link/internal/amd64/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -65,7 +65,7 @@ func Init() (*sys.Arch, ld.Arch) {
TLSIEtoLE: tlsIEtoLE,
Linuxdynld: "/lib64/ld-linux-x86-64.so.2",
- LinuxdynldMusl: "/lib/ld-musl-x84_64.so.1",
+ LinuxdynldMusl: "/lib/ld-musl-x86_64.so.1",
Freebsddynld: "/libexec/ld-elf.so.1",
Openbsddynld: "/usr/libexec/ld.so",
Netbsddynld: "/libexec/ld.elf_so",
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index faae153bab..94f8fc32d6 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1860,9 +1860,9 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
// Coverage instrumentation counters for libfuzzer.
if len(state.data[sym.SLIBFUZZER_8BIT_COUNTER]) > 0 {
- sect := state.allocateNamedSectionAndAssignSyms(&Segdata, "__sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06)
- ldr.SetSymSect(ldr.LookupOrCreateSym("__start___sancov_cntrs", 0), sect)
- ldr.SetSymSect(ldr.LookupOrCreateSym("__stop___sancov_cntrs", 0), sect)
+ sect := state.allocateNamedSectionAndAssignSyms(&Segdata, ".go.fuzzcntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__start___sancov_cntrs", 0), sect)
+ ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.__stop___sancov_cntrs", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._counters", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect)
}
@@ -2643,7 +2643,7 @@ func (ctxt *Link) address() []*sym.Segment {
bss = s
case ".noptrbss":
noptrbss = s
- case "__sancov_cntrs":
+ case ".go.fuzzcntrs":
fuzzCounters = s
}
}
@@ -2764,8 +2764,8 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
if fuzzCounters != nil {
- ctxt.xdefine("__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
- ctxt.xdefine("__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
+ ctxt.xdefine("runtime.__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
+ ctxt.xdefine("runtime.__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
ctxt.xdefine("internal/fuzz._counters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
ctxt.xdefine("internal/fuzz._ecounters", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
}
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 082adcc3c7..a1ae7eab57 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -1371,7 +1371,7 @@ func (ctxt *Link) doelf() {
shstrtab.Addstring(".data")
shstrtab.Addstring(".bss")
shstrtab.Addstring(".noptrbss")
- shstrtab.Addstring("__sancov_cntrs")
+ shstrtab.Addstring(".go.fuzzcntrs")
shstrtab.Addstring(".go.buildinfo")
if ctxt.IsMIPS() {
shstrtab.Addstring(".MIPS.abiflags")
diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go
index 5387c5a015..b4032a5d91 100644
--- a/src/crypto/x509/internal/macos/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
@@ -186,6 +186,13 @@ func CFErrorCopyDescription(errRef CFRef) CFRef {
}
func x509_CFErrorCopyDescription_trampoline()
+//go:cgo_import_dynamic x509_CFErrorGetCode CFErrorGetCode "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
+
+func CFErrorGetCode(errRef CFRef) int {
+ return int(syscall(abi.FuncPCABI0(x509_CFErrorGetCode_trampoline), uintptr(errRef), 0, 0, 0, 0, 0))
+}
+func x509_CFErrorGetCode_trampoline()
+
//go:cgo_import_dynamic x509_CFStringCreateExternalRepresentation CFStringCreateExternalRepresentation "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
func CFStringCreateExternalRepresentation(strRef CFRef) (CFRef, error) {
diff --git a/src/crypto/x509/internal/macos/corefoundation.s b/src/crypto/x509/internal/macos/corefoundation.s
index d69f72f795..49cd084467 100644
--- a/src/crypto/x509/internal/macos/corefoundation.s
+++ b/src/crypto/x509/internal/macos/corefoundation.s
@@ -37,5 +37,7 @@ TEXT ·x509_CFDataCreate_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDataCreate(SB)
TEXT ·x509_CFErrorCopyDescription_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFErrorCopyDescription(SB)
+TEXT ·x509_CFErrorGetCode_trampoline(SB),NOSPLIT,$0-0
+ JMP x509_CFErrorGetCode(SB)
TEXT ·x509_CFStringCreateExternalRepresentation_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFStringCreateExternalRepresentation(SB)
diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go
index 0fc218c552..a6972c0c09 100644
--- a/src/crypto/x509/internal/macos/security.go
+++ b/src/crypto/x509/internal/macos/security.go
@@ -8,7 +8,6 @@ package macOS
import (
"errors"
- "fmt"
"internal/abi"
"strconv"
"unsafe"
@@ -52,6 +51,15 @@ const (
SecTrustSettingsDomainSystem
)
+const (
+ // various macOS error codes that can be returned from
+ // SecTrustEvaluateWithError that we can map to Go cert
+ // verification error types.
+ ErrSecCertificateExpired = -67818
+ ErrSecHostNameMismatch = -67602
+ ErrSecNotTrusted = -67843
+)
+
type OSStatus struct {
call string
status int32
@@ -191,17 +199,18 @@ func x509_SecTrustGetResult_trampoline()
//go:cgo_import_dynamic x509_SecTrustEvaluateWithError SecTrustEvaluateWithError "/System/Library/Frameworks/Security.framework/Versions/A/Security"
-func SecTrustEvaluateWithError(trustObj CFRef) error {
+func SecTrustEvaluateWithError(trustObj CFRef) (int, error) {
var errRef CFRef
ret := syscall(abi.FuncPCABI0(x509_SecTrustEvaluateWithError_trampoline), uintptr(trustObj), uintptr(unsafe.Pointer(&errRef)), 0, 0, 0, 0)
if int32(ret) != 1 {
errStr := CFErrorCopyDescription(errRef)
- err := fmt.Errorf("x509: %s", CFStringToString(errStr))
+ err := errors.New(CFStringToString(errStr))
+ errCode := CFErrorGetCode(errRef)
CFRelease(errRef)
CFRelease(errStr)
- return err
+ return errCode, err
}
- return nil
+ return 0, nil
}
func x509_SecTrustEvaluateWithError_trampoline()
diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go
index 20f627c277..de2ff894a9 100644
--- a/src/crypto/x509/root_darwin.go
+++ b/src/crypto/x509/root_darwin.go
@@ -7,6 +7,7 @@ package x509
import (
macOS "crypto/x509/internal/macos"
"errors"
+ "fmt"
)
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
@@ -57,8 +58,17 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
// always enforce its SCT requirements, and there are still _some_ people
// using TLS or OCSP for that.
- if err := macOS.SecTrustEvaluateWithError(trustObj); err != nil {
- return nil, err
+ if ret, err := macOS.SecTrustEvaluateWithError(trustObj); err != nil {
+ switch ret {
+ case macOS.ErrSecCertificateExpired:
+ return nil, CertificateInvalidError{c, Expired, err.Error()}
+ case macOS.ErrSecHostNameMismatch:
+ return nil, HostnameError{c, opts.DNSName}
+ case macOS.ErrSecNotTrusted:
+ return nil, UnknownAuthorityError{Cert: c}
+ default:
+ return nil, fmt.Errorf("x509: %s", err)
+ }
}
chain := [][]*Certificate{{}}
diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go
index 90a464f624..299cecf556 100644
--- a/src/crypto/x509/root_darwin_test.go
+++ b/src/crypto/x509/root_darwin_test.go
@@ -42,23 +42,23 @@ func TestPlatformVerifier(t *testing.T) {
{
name: "expired leaf",
host: "expired.badssl.com",
- expectedErr: "x509: “*.badssl.com” certificate is expired",
+ expectedErr: "x509: certificate has expired or is not yet valid: “*.badssl.com” certificate is expired",
},
{
name: "wrong host for leaf",
host: "wrong.host.badssl.com",
verifyName: "wrong.host.badssl.com",
- expectedErr: "x509: “*.badssl.com” certificate name does not match input",
+ expectedErr: "x509: certificate is valid for *.badssl.com, badssl.com, not wrong.host.badssl.com",
},
{
name: "self-signed leaf",
host: "self-signed.badssl.com",
- expectedErr: "x509: “*.badssl.com” certificate is not trusted",
+ expectedErr: "x509: certificate signed by unknown authority",
},
{
name: "untrusted root",
host: "untrusted-root.badssl.com",
- expectedErr: "x509: “BadSSL Untrusted Root Certificate Authority” certificate is not trusted",
+ expectedErr: "x509: certificate signed by unknown authority",
},
{
name: "revoked leaf",
@@ -74,7 +74,7 @@ func TestPlatformVerifier(t *testing.T) {
name: "expired leaf (custom time)",
host: "google.com",
verifyTime: time.Time{}.Add(time.Hour),
- expectedErr: "x509: “*.google.com” certificate is expired",
+ expectedErr: "x509: certificate has expired or is not yet valid: “*.google.com” certificate is expired",
},
{
name: "valid chain (custom time)",
diff --git a/src/debug/buildinfo/buildinfo.go b/src/debug/buildinfo/buildinfo.go
index 255e6d3d50..8bc5753a2d 100644
--- a/src/debug/buildinfo/buildinfo.go
+++ b/src/debug/buildinfo/buildinfo.go
@@ -192,8 +192,10 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
var readPtr func([]byte) uint64
if ptrSize == 4 {
readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
- } else {
+ } else if ptrSize == 8 {
readPtr = bo.Uint64
+ } else {
+ return "", "", errNotGoExe
}
vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
diff --git a/src/debug/buildinfo/buildinfo_test.go b/src/debug/buildinfo/buildinfo_test.go
index ae04b4cb1d..290e3705bc 100644
--- a/src/debug/buildinfo/buildinfo_test.go
+++ b/src/debug/buildinfo/buildinfo_test.go
@@ -248,6 +248,18 @@ func TestReadFile(t *testing.T) {
}
}
+// FuzzIssue57002 is a regression test for golang.org/issue/57002.
+//
+// The cause of issue 57002 is when pointerSize is not being checked,
+// the read can panic with slice bounds out of range
+func FuzzIssue57002(f *testing.F) {
+ // input from issue
+ f.Add([]byte{0x4d, 0x5a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x50, 0x45, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3f, 0x0, 0x20, 0x0, 0x0, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb, 0x20, 0x20, 0x20, 0xfc, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x9, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x20, 0x20, 0x20, 0x20, 0xef, 0x20, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf, 0x0, 0x2, 0x0, 0x20, 0x0, 0x0, 0x9, 0x0, 0x4, 0x0, 0x20, 0xf6, 0x0, 0xd3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0x0, 0x0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0xa, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0xff, 0x20, 0x47, 0x6f, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x66, 0x3a, 0xde, 0xb5, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x7f, 0x7f, 0x7f, 0x20, 0xf4, 0xb2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x20, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x20, 0x20, 0x20, 0x20, 0x0, 0x0, 0x0, 0x0, 0x20, 0x3f, 0x27, 0x20, 0x0, 0xd, 0x0, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0xff, 0x20, 0x20, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0, 0x20, 0x20, 0x0, 0x0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20})
+ f.Fuzz(func(t *testing.T, input []byte) {
+ buildinfo.Read(bytes.NewReader(input))
+ })
+}
+
// TestIssue54968 is a regression test for golang.org/issue/54968.
//
// The cause of issue 54968 is when the first buildInfoMagic is invalid, it
diff --git a/src/go/types/expr.go b/src/go/types/expr.go
index da9cd67826..e09b461d8c 100644
--- a/src/go/types/expr.go
+++ b/src/go/types/expr.go
@@ -1084,26 +1084,50 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
return
}
- // TODO(gri) make canMix more efficient - called for each binary operation
- canMix := func(x, y *operand) bool {
+ // mayConvert reports whether the operands x and y may
+ // possibly have matching types after converting one
+ // untyped operand to the type of the other.
+ // If mayConvert returns true, we try to convert the
+ // operands to each other's types, and if that fails
+ // we report a conversion failure.
+ // If mayConvert returns false, we continue without an
+ // attempt at conversion, and if the operand types are
+ // not compatible, we report a type mismatch error.
+ mayConvert := func(x, y *operand) bool {
+ // If both operands are typed, there's no need for an implicit conversion.
+ if isTyped(x.typ) && isTyped(y.typ) {
+ return false
+ }
+ // An untyped operand may convert to its default type when paired with an empty interface
+ // TODO(gri) This should only matter for comparisons (the only binary operation that is
+ // valid with interfaces), but in that case the assignability check should take
+ // care of the conversion. Verify and possibly eliminate this extra test.
if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
return true
}
+ // A boolean type can only convert to another boolean type.
if allBoolean(x.typ) != allBoolean(y.typ) {
return false
}
+ // A string type can only convert to another string type.
if allString(x.typ) != allString(y.typ) {
return false
}
- if x.isNil() && !hasNil(y.typ) {
- return false
+ // Untyped nil can only convert to a type that has a nil.
+ if x.isNil() {
+ return hasNil(y.typ)
+ }
+ if y.isNil() {
+ return hasNil(x.typ)
}
- if y.isNil() && !hasNil(x.typ) {
+ // An untyped operand cannot convert to a pointer.
+ // TODO(gri) generalize to type parameters
+ if isPointer(x.typ) || isPointer(y.typ) {
return false
}
return true
}
- if canMix(x, &y) {
+ if mayConvert(x, &y) {
check.convertUntyped(x, y.typ)
if x.mode == invalid {
return
diff --git a/src/go/types/infer.go b/src/go/types/infer.go
index 1c1d4e03fc..dc87902c4c 100644
--- a/src/go/types/infer.go
+++ b/src/go/types/infer.go
@@ -89,34 +89,22 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
// f(p)
// }
//
- // We can turn the first example into the second example by renaming type
- // parameters in the original signature to give them a new identity. As an
- // optimization, we do this only for self-recursive calls.
-
- // We can detect if we are in a self-recursive call by comparing the
- // identity of the first type parameter in the current function with the
- // first type parameter in tparams. This works because type parameters are
- // unique to their type parameter list.
- selfRecursive := check.sig != nil && check.sig.tparams.Len() > 0 && tparams[0] == check.sig.tparams.At(0)
-
- if selfRecursive {
- // In self-recursive inference, rename the type parameters with new type
- // parameters that are the same but for their pointer identity.
- tparams2 := make([]*TypeParam, len(tparams))
- for i, tparam := range tparams {
- tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
- tparams2[i] = NewTypeParam(tname, nil)
- tparams2[i].index = tparam.index // == i
- }
-
- renameMap := makeRenameMap(tparams, tparams2)
- for i, tparam := range tparams {
- tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil, check.context())
- }
+ // We turn the first example into the second example by renaming type
+ // parameters in the original signature to give them a new identity.
+ tparams2 := make([]*TypeParam, len(tparams))
+ for i, tparam := range tparams {
+ tname := NewTypeName(tparam.Obj().Pos(), tparam.Obj().Pkg(), tparam.Obj().Name(), nil)
+ tparams2[i] = NewTypeParam(tname, nil)
+ tparams2[i].index = tparam.index // == i
+ }
- tparams = tparams2
- params = check.subst(posn.Pos(), params, renameMap, nil, check.context()).(*Tuple)
+ renameMap := makeRenameMap(tparams, tparams2)
+ for i, tparam := range tparams {
+ tparams2[i].bound = check.subst(posn.Pos(), tparam.bound, renameMap, nil, check.context())
}
+
+ tparams = tparams2
+ params = check.subst(posn.Pos(), params, renameMap, nil, check.context()).(*Tuple)
}
// If we have more than 2 arguments, we may have arguments with named and unnamed types.
diff --git a/src/go/types/typeset.go b/src/go/types/typeset.go
index 35a32972e0..d68446df66 100644
--- a/src/go/types/typeset.go
+++ b/src/go/types/typeset.go
@@ -350,7 +350,7 @@ func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool
i := 0
for _, t := range terms {
assert(t.typ != nil)
- if Comparable(t.typ) {
+ if comparable(t.typ, false /* strictly comparable */, nil, nil) {
terms[i] = t
i++
}
diff --git a/src/internal/poll/sock_cloexec.go b/src/internal/poll/sock_cloexec.go
index 4fb9f004bb..f5be2aa5f2 100644
--- a/src/internal/poll/sock_cloexec.go
+++ b/src/internal/poll/sock_cloexec.go
@@ -5,7 +5,7 @@
// This file implements accept for platforms that provide a fast path for
// setting SetNonblock and CloseOnExec.
-//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
+//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd || solaris
package poll
diff --git a/src/internal/poll/sock_cloexec_accept.go b/src/internal/poll/sock_cloexec_accept.go
new file mode 100644
index 0000000000..4b86de59e0
--- /dev/null
+++ b/src/internal/poll/sock_cloexec_accept.go
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+// This file implements accept for platforms that provide a fast path for
+// setting SetNonblock and CloseOnExec, but don't necessarily have accept4.
+// This is the code we used for accept in Go 1.17 and earlier.
+// On Linux the accept4 system call was introduced in 2.6.28 kernel,
+// and our minimum requirement is 2.6.32, so we simplified the function.
+// Unfortunately, on ARM accept4 wasn't added until 2.6.36, so for ARM
+// only we continue using the older code.
+
+//go:build linux && arm
+
+package poll
+
+import "syscall"
+
+// Wrapper around the accept system call that marks the returned file
+// descriptor as nonblocking and close-on-exec.
+func accept(s int) (int, syscall.Sockaddr, string, error) {
+ ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
+ switch err {
+ case nil:
+ return ns, sa, "", nil
+ default: // errors other than the ones listed
+ return -1, sa, "accept4", err
+ case syscall.ENOSYS: // syscall missing
+ case syscall.EINVAL: // some Linux use this instead of ENOSYS
+ case syscall.EACCES: // some Linux use this instead of ENOSYS
+ case syscall.EFAULT: // some Linux use this instead of ENOSYS
+ }
+
+ // See ../syscall/exec_unix.go for description of ForkLock.
+ // It is probably okay to hold the lock across syscall.Accept
+ // because we have put fd.sysfd into non-blocking mode.
+ // However, a call to the File method will put it back into
+ // blocking mode. We can't take that risk, so no use of ForkLock here.
+ ns, sa, err = AcceptFunc(s)
+ if err == nil {
+ syscall.CloseOnExec(ns)
+ }
+ if err != nil {
+ return -1, nil, "accept", err
+ }
+ if err = syscall.SetNonblock(ns, true); err != nil {
+ CloseFunc(ns)
+ return -1, nil, "setnonblock", err
+ }
+ return ns, sa, "", nil
+}
diff --git a/src/internal/safefilepath/path_other.go b/src/internal/safefilepath/path_other.go
index f93da18680..974e7751a2 100644
--- a/src/internal/safefilepath/path_other.go
+++ b/src/internal/safefilepath/path_other.go
@@ -11,7 +11,7 @@ import "runtime"
func fromFS(path string) (string, error) {
if runtime.GOOS == "plan9" {
if len(path) > 0 && path[0] == '#' {
- return path, errInvalidPath
+ return "", errInvalidPath
}
}
for i := range path {
diff --git a/src/internal/types/errors/codes.go b/src/internal/types/errors/codes.go
index 7a0c0e16b8..acddcbb9c5 100644
--- a/src/internal/types/errors/codes.go
+++ b/src/internal/types/errors/codes.go
@@ -882,7 +882,7 @@ const (
// context in which it is used.
//
// Example:
- // var _ = 1 + new(int)
+ // var _ = 1 + []int{}
InvalidUntypedConversion
// BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument
diff --git a/src/internal/types/testdata/fixedbugs/issue57155.go b/src/internal/types/testdata/fixedbugs/issue57155.go
new file mode 100644
index 0000000000..ec9fb2bad3
--- /dev/null
+++ b/src/internal/types/testdata/fixedbugs/issue57155.go
@@ -0,0 +1,14 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f[P *Q, Q any](p P, q Q) {
+ func() {
+ _ = f[P]
+ f(p, q)
+ f[P](p, q)
+ f[P, Q](p, q)
+ }()
+}
diff --git a/src/internal/types/testdata/fixedbugs/issue57160.go b/src/internal/types/testdata/fixedbugs/issue57160.go
new file mode 100644
index 0000000000..446d019900
--- /dev/null
+++ b/src/internal/types/testdata/fixedbugs/issue57160.go
@@ -0,0 +1,10 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func _(x *int) {
+ _ = 0 < x // ERROR "invalid operation"
+ _ = x < 0 // ERROR "invalid operation"
+}
diff --git a/src/internal/types/testdata/fixedbugs/issue57486.go b/src/internal/types/testdata/fixedbugs/issue57486.go
new file mode 100644
index 0000000000..ff9e3d1db5
--- /dev/null
+++ b/src/internal/types/testdata/fixedbugs/issue57486.go
@@ -0,0 +1,29 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type C1 interface {
+ comparable
+}
+
+type C2 interface {
+ comparable
+ [2]any | int
+}
+
+func G1[T C1](t T) { _ = t == t }
+func G2[T C2](t T) { _ = t == t }
+
+func F1[V [2]any](v V) {
+ _ = G1[V /* ERROR "V does not implement comparable" */]
+ _ = G1[[2]any]
+ _ = G1[int]
+}
+
+func F2[V [2]any](v V) {
+ _ = G2[V /* ERROR "V does not implement C2" */]
+ _ = G2[[ /* ERROR "\[2\]any does not implement C2 \(\[2\]any missing in int\)" */ 2]any]
+ _ = G2[int]
+}
diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go
index 5b0df56eae..6a2c369c66 100644
--- a/src/net/cgo_unix.go
+++ b/src/net/cgo_unix.go
@@ -383,8 +383,9 @@ func cgoResSearch(hostname string, rtype, class int) ([]dnsmessage.Resource, err
s := _C_CString(hostname)
defer _C_FreeCString(s)
+ var size int
for {
- size, _ := _C_res_nsearch(state, s, class, rtype, buf, bufSize)
+ size, _ = _C_res_nsearch(state, s, class, rtype, buf, bufSize)
if size <= 0 || size > 0xffff {
return nil, errors.New("res_nsearch failure")
}
@@ -399,7 +400,7 @@ func cgoResSearch(hostname string, rtype, class int) ([]dnsmessage.Resource, err
}
var p dnsmessage.Parser
- if _, err := p.Start(unsafe.Slice((*byte)(unsafe.Pointer(buf)), bufSize)); err != nil {
+ if _, err := p.Start(unsafe.Slice((*byte)(unsafe.Pointer(buf)), size)); err != nil {
return nil, err
}
p.SkipAllQuestions()
diff --git a/src/net/cgo_unix_cgo.go b/src/net/cgo_unix_cgo.go
index 3e7282b579..97427e695d 100644
--- a/src/net/cgo_unix_cgo.go
+++ b/src/net/cgo_unix_cgo.go
@@ -7,6 +7,7 @@
package net
/*
+#cgo CFLAGS: -fno-stack-protector
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go
index 190279ca00..58064a5332 100644
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -131,17 +131,17 @@ type ReverseProxy struct {
// Director must not access the provided Request
// after returning.
//
- // By default, the X-Forwarded-For, X-Forwarded-Host, and
- // X-Forwarded-Proto headers of the ourgoing request are
- // set as by the ProxyRequest.SetXForwarded function.
+ // By default, the X-Forwarded-For header is set to the
+ // value of the client IP address. If an X-Forwarded-For
+ // header already exists, the client IP is appended to the
+ // existing values. As a special case, if the header
+ // exists in the Request.Header map but has a nil value
+ // (such as when set by the Director func), the X-Forwarded-For
+ // header is not modified.
//
- // If an X-Forwarded-For header already exists, the client IP is
- // appended to the existing values. To prevent IP spoofing, be
- // sure to delete any pre-existing X-Forwarded-For header
- // coming from the client or an untrusted proxy.
- //
- // If a header exists in the Request.Header map but has a nil value
- // (such as when set by the Director func), it is not modified.
+ // To prevent IP spoofing, be sure to delete any pre-existing
+ // X-Forwarded-For header coming from the client or
+ // an untrusted proxy.
//
// Hop-by-hop headers are removed from the request after
// Director returns, which can remove headers added by
@@ -446,16 +446,6 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
outreq.Header.Set("X-Forwarded-For", clientIP)
}
}
- if prior, ok := outreq.Header["X-Forwarded-Host"]; !(ok && prior == nil) {
- outreq.Header.Set("X-Forwarded-Host", req.Host)
- }
- if prior, ok := outreq.Header["X-Forwarded-Proto"]; !(ok && prior == nil) {
- if req.TLS == nil {
- outreq.Header.Set("X-Forwarded-Proto", "http")
- } else {
- outreq.Header.Set("X-Forwarded-Proto", "https")
- }
- }
}
if _, ok := outreq.Header["User-Agent"]; !ok {
diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go
index 5b882d3a45..d5b0fb4244 100644
--- a/src/net/http/httputil/reverseproxy_test.go
+++ b/src/net/http/httputil/reverseproxy_test.go
@@ -52,12 +52,6 @@ func TestReverseProxy(t *testing.T) {
if r.Header.Get("X-Forwarded-For") == "" {
t.Errorf("didn't get X-Forwarded-For header")
}
- if r.Header.Get("X-Forwarded-Host") == "" {
- t.Errorf("didn't get X-Forwarded-Host header")
- }
- if r.Header.Get("X-Forwarded-Proto") == "" {
- t.Errorf("didn't get X-Forwarded-Proto header")
- }
if c := r.Header.Get("Connection"); c != "" {
t.Errorf("handler got Connection header value %q", c)
}
@@ -307,7 +301,6 @@ func TestXForwardedFor(t *testing.T) {
const prevForwardedFor = "client ip"
const backendResponse = "I am the backend"
const backendStatus = 404
- const host = "some-name"
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Forwarded-For") == "" {
t.Errorf("didn't get X-Forwarded-For header")
@@ -315,12 +308,6 @@ func TestXForwardedFor(t *testing.T) {
if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) {
t.Errorf("X-Forwarded-For didn't contain prior data")
}
- if got, want := r.Header.Get("X-Forwarded-Host"), host; got != want {
- t.Errorf("X-Forwarded-Host = %q, want %q", got, want)
- }
- if got, want := r.Header.Get("X-Forwarded-Proto"), "http"; got != want {
- t.Errorf("X-Forwarded-Proto = %q, want %q", got, want)
- }
w.WriteHeader(backendStatus)
w.Write([]byte(backendResponse))
}))
@@ -334,7 +321,6 @@ func TestXForwardedFor(t *testing.T) {
defer frontend.Close()
getReq, _ := http.NewRequest("GET", frontend.URL, nil)
- getReq.Host = host
getReq.Header.Set("Connection", "close")
getReq.Header.Set("X-Forwarded-For", prevForwardedFor)
getReq.Close = true
@@ -351,36 +337,11 @@ func TestXForwardedFor(t *testing.T) {
}
}
-func TestXForwardedProtoTLS(t *testing.T) {
- backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if got, want := r.Header.Get("X-Forwarded-Proto"), "https"; got != want {
- t.Errorf("X-Forwarded-Proto = %q, want %q", got, want)
- }
- }))
- defer backend.Close()
- backendURL, err := url.Parse(backend.URL)
- if err != nil {
- t.Fatal(err)
- }
- proxyHandler := NewSingleHostReverseProxy(backendURL)
- frontend := httptest.NewTLSServer(proxyHandler)
- defer frontend.Close()
-
- getReq, _ := http.NewRequest("GET", frontend.URL, nil)
- getReq.Host = "some-host"
- _, err = frontend.Client().Do(getReq)
- if err != nil {
- t.Fatalf("Get: %v", err)
- }
-}
-
// Issue 38079: don't append to X-Forwarded-For if it's present but nil
func TestXForwardedFor_Omit(t *testing.T) {
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- for _, h := range []string{"X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Proto"} {
- if v := r.Header.Get(h); v != "" {
- t.Errorf("got %v header: %q", h, v)
- }
+ if v := r.Header.Get("X-Forwarded-For"); v != "" {
+ t.Errorf("got X-Forwarded-For header: %q", v)
}
w.Write([]byte("hi"))
}))
@@ -396,8 +357,6 @@ func TestXForwardedFor_Omit(t *testing.T) {
oldDirector := proxyHandler.Director
proxyHandler.Director = func(r *http.Request) {
r.Header["X-Forwarded-For"] = nil
- r.Header["X-Forwarded-Host"] = nil
- r.Header["X-Forwarded-Proto"] = nil
oldDirector(r)
}
@@ -1106,8 +1065,6 @@ func TestClonesRequestHeaders(t *testing.T) {
for _, h := range []string{
"From-Director",
"X-Forwarded-For",
- "X-Forwarded-Host",
- "X-Forwarded-Proto",
} {
if req.Header.Get(h) != "" {
t.Errorf("%v header mutation modified caller's request", h)
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 2bc83fd42b..245f73bc9f 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -6565,7 +6565,8 @@ func testCancelRequestWhenSharingConnection(t *testing.T, mode testMode) {
var wg sync.WaitGroup
wg.Add(1)
- putidlec := make(chan chan struct{})
+ putidlec := make(chan chan struct{}, 1)
+ reqerrc := make(chan error, 1)
go func() {
defer wg.Done()
ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
@@ -6574,24 +6575,31 @@ func testCancelRequestWhenSharingConnection(t *testing.T, mode testMode) {
// and wait for the order to proceed.
ch := make(chan struct{})
putidlec <- ch
+ close(putidlec) // panic if PutIdleConn runs twice for some reason
<-ch
},
})
req, _ := NewRequestWithContext(ctx, "GET", ts.URL, nil)
res, err := client.Do(req)
+ reqerrc <- err
if err == nil {
res.Body.Close()
}
- if err != nil {
- t.Errorf("request 1: got err %v, want nil", err)
- }
}()
// Wait for the first request to receive a response and return the
// connection to the idle pool.
r1c := <-reqc
close(r1c)
- idlec := <-putidlec
+ var idlec chan struct{}
+ select {
+ case err := <-reqerrc:
+ if err != nil {
+ t.Fatalf("request 1: got err %v, want nil", err)
+ }
+ idlec = <-putidlec
+ case idlec = <-putidlec:
+ }
wg.Add(1)
cancelctx, cancel := context.WithCancel(context.Background())
diff --git a/src/os/exec/lp_linux_test.go b/src/os/exec/lp_linux_test.go
index 98c3a7b9e0..845573fb14 100644
--- a/src/os/exec/lp_linux_test.go
+++ b/src/os/exec/lp_linux_test.go
@@ -5,6 +5,7 @@
package exec
import (
+ "errors"
"internal/syscall/unix"
"os"
"path/filepath"
@@ -48,8 +49,20 @@ func TestFindExecutableVsNoexec(t *testing.T) {
t.Fatalf("findExecutable: got %v, want nil", err)
}
- if err := Command(path).Run(); err != nil {
- t.Fatalf("exec: got %v, want nil", err)
+ for {
+ err = Command(path).Run()
+ if err == nil {
+ break
+ }
+ if errors.Is(err, syscall.ETXTBSY) {
+ // A fork+exec in another process may be holding open the FD that we used
+ // to write the executable (see https://go.dev/issue/22315).
+ // Since the descriptor should have CLOEXEC set, the problem should resolve
+ // as soon as the forked child reaches its exec call.
+ // Keep retrying until that happens.
+ } else {
+ t.Fatalf("exec: got %v, want nil", err)
+ }
}
// Remount with noexec flag.
diff --git a/src/os/os_test.go b/src/os/os_test.go
index f4103907fa..277b2455e6 100644
--- a/src/os/os_test.go
+++ b/src/os/os_test.go
@@ -274,9 +274,11 @@ func TestLstat(t *testing.T) {
if !equal(sfname, dir.Name()) {
t.Error("name should be ", sfname, "; is", dir.Name())
}
- filesize := size(path, t)
- if dir.Size() != filesize {
- t.Error("size should be", filesize, "; is", dir.Size())
+ if dir.Mode()&ModeSymlink == 0 {
+ filesize := size(path, t)
+ if dir.Size() != filesize {
+ t.Error("size should be", filesize, "; is", dir.Size())
+ }
}
}
@@ -2606,9 +2608,6 @@ func TestReaddirSmallSeek(t *testing.T) {
// See issue 37161. Read only one entry from a directory,
// seek to the beginning, and read again. We should not see
// duplicate entries.
- if runtime.GOOS == "windows" {
- testenv.SkipFlaky(t, 36019)
- }
wd, err := Getwd()
if err != nil {
t.Fatal(err)
diff --git a/src/os/user/cgo_lookup_cgo.go b/src/os/user/cgo_lookup_cgo.go
index 17995738d2..4f78dcad23 100644
--- a/src/os/user/cgo_lookup_cgo.go
+++ b/src/os/user/cgo_lookup_cgo.go
@@ -12,42 +12,48 @@ import (
/*
#cgo solaris CFLAGS: -D_POSIX_PTHREAD_SEMANTICS
+#cgo CFLAGS: -fno-stack-protector
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>
+#include <string.h>
static struct passwd mygetpwuid_r(int uid, char *buf, size_t buflen, int *found, int *perr) {
struct passwd pwd;
- struct passwd *result;
- *perr = getpwuid_r(uid, &pwd, buf, buflen, &result);
- *found = result != NULL;
- return pwd;
+ struct passwd *result;
+ memset (&pwd, 0, sizeof(pwd));
+ *perr = getpwuid_r(uid, &pwd, buf, buflen, &result);
+ *found = result != NULL;
+ return pwd;
}
static struct passwd mygetpwnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) {
struct passwd pwd;
- struct passwd *result;
- *perr = getpwnam_r(name, &pwd, buf, buflen, &result);
- *found = result != NULL;
- return pwd;
+ struct passwd *result;
+ memset(&pwd, 0, sizeof(pwd));
+ *perr = getpwnam_r(name, &pwd, buf, buflen, &result);
+ *found = result != NULL;
+ return pwd;
}
static struct group mygetgrgid_r(int gid, char *buf, size_t buflen, int *found, int *perr) {
struct group grp;
- struct group *result;
- *perr = getgrgid_r(gid, &grp, buf, buflen, &result);
- *found = result != NULL;
- return grp;
+ struct group *result;
+ memset(&grp, 0, sizeof(grp));
+ *perr = getgrgid_r(gid, &grp, buf, buflen, &result);
+ *found = result != NULL;
+ return grp;
}
static struct group mygetgrnam_r(const char *name, char *buf, size_t buflen, int *found, int *perr) {
struct group grp;
- struct group *result;
- *perr = getgrnam_r(name, &grp, buf, buflen, &result);
- *found = result != NULL;
- return grp;
+ struct group *result;
+ memset(&grp, 0, sizeof(grp));
+ *perr = getgrnam_r(name, &grp, buf, buflen, &result);
+ *found = result != NULL;
+ return grp;
}
*/
import "C"
diff --git a/src/os/user/cgo_lookup_unix.go b/src/os/user/cgo_lookup_unix.go
index b745ffd9cf..3735971eb4 100644
--- a/src/os/user/cgo_lookup_unix.go
+++ b/src/os/user/cgo_lookup_unix.go
@@ -8,6 +8,7 @@ package user
import (
"fmt"
+ "runtime"
"strconv"
"strings"
"syscall"
@@ -170,6 +171,9 @@ func retryWithBuffer(startSize bufferKind, f func([]byte) syscall.Errno) error {
errno := f(buf)
if errno == 0 {
return nil
+ } else if runtime.GOOS == "aix" && errno+1 == 0 {
+ // On AIX getpwuid_r appears to return -1,
+ // not ERANGE, on buffer overflow.
} else if errno != syscall.ERANGE {
return errno
}
diff --git a/src/runtime/coverage/emitdata_test.go b/src/runtime/coverage/emitdata_test.go
index 0ccb2d27b0..3839e4437f 100644
--- a/src/runtime/coverage/emitdata_test.go
+++ b/src/runtime/coverage/emitdata_test.go
@@ -157,7 +157,7 @@ func runHarness(t *testing.T, harnessPath string, tp string, setGoCoverDir bool,
func testForSpecificFunctions(t *testing.T, dir string, want []string, avoid []string) string {
args := []string{"tool", "covdata", "debugdump",
- "-live", "-pkg=main", "-i=" + dir}
+ "-live", "-pkg=command-line-arguments", "-i=" + dir}
t.Logf("running: go %v\n", args)
cmd := exec.Command(testenv.GoToolPath(t), args...)
b, err := cmd.CombinedOutput()
@@ -167,18 +167,21 @@ func testForSpecificFunctions(t *testing.T, dir string, want []string, avoid []s
output := string(b)
rval := ""
for _, f := range want {
- wf := "Func: " + f
+ wf := "Func: " + f + "\n"
if strings.Contains(output, wf) {
continue
}
rval += fmt.Sprintf("error: output should contain %q but does not\n", wf)
}
for _, f := range avoid {
- wf := "Func: " + f
+ wf := "Func: " + f + "\n"
if strings.Contains(output, wf) {
rval += fmt.Sprintf("error: output should not contain %q but does\n", wf)
}
}
+ if rval != "" {
+ t.Logf("=-= begin output:\n" + output + "\n=-= end output\n")
+ }
return rval
}
diff --git a/src/runtime/coverage/testsupport.go b/src/runtime/coverage/testsupport.go
index 462d06c878..1d90ebd7a2 100644
--- a/src/runtime/coverage/testsupport.go
+++ b/src/runtime/coverage/testsupport.go
@@ -136,13 +136,16 @@ func (ts *tstate) processPod(p pods.Pod) error {
return err
}
- // Read counter data files.
+ // A map to store counter data, indexed by pkgid/fnid tuple.
pmm := make(map[pkfunc][]uint32)
- for _, cdf := range p.CounterDataFiles {
+
+ // Helper to read a single counter data file.
+ readcdf := func(cdf string) error {
cf, err := os.Open(cdf)
if err != nil {
return fmt.Errorf("opening counter data file %s: %s", cdf, err)
}
+ defer cf.Close()
var cdr *decodecounter.CounterDataReader
cdr, err = decodecounter.NewCounterDataReader(cdf, cf)
if err != nil {
@@ -170,6 +173,14 @@ func (ts *tstate) processPod(p pods.Pod) error {
copy(c, data.Counters)
pmm[key] = c
}
+ return nil
+ }
+
+ // Read counter data files.
+ for _, cdf := range p.CounterDataFiles {
+ if err := readcdf(cdf); err != nil {
+ return err
+ }
}
// Visit meta-data file.
diff --git a/src/runtime/internal/atomic/atomic_test.go b/src/runtime/internal/atomic/atomic_test.go
index 2ae60b8507..2427bfd211 100644
--- a/src/runtime/internal/atomic/atomic_test.go
+++ b/src/runtime/internal/atomic/atomic_test.go
@@ -345,6 +345,36 @@ func TestBitwiseContended(t *testing.T) {
}
}
+func TestCasRel(t *testing.T) {
+ const _magic = 0x5a5aa5a5
+ var x struct {
+ before uint32
+ i uint32
+ after uint32
+ o uint32
+ n uint32
+ }
+
+ x.before = _magic
+ x.after = _magic
+ for j := 0; j < 32; j += 1 {
+ x.i = (1 << j) + 0
+ x.o = (1 << j) + 0
+ x.n = (1 << j) + 1
+ if !atomic.CasRel(&x.i, x.o, x.n) {
+ t.Fatalf("should have swapped %#x %#x", x.o, x.n)
+ }
+
+ if x.i != x.n {
+ t.Fatalf("wrong x.i after swap: x.i=%#x x.n=%#x", x.i, x.n)
+ }
+
+ if x.before != _magic || x.after != _magic {
+ t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, _magic, _magic)
+ }
+ }
+}
+
func TestStorepNoWB(t *testing.T) {
var p [2]*int
for i := range p {
diff --git a/src/runtime/internal/startlinetest/func_amd64.go b/src/runtime/internal/startlinetest/func_amd64.go
index 6cd9a3f417..ab7063d615 100644
--- a/src/runtime/internal/startlinetest/func_amd64.go
+++ b/src/runtime/internal/startlinetest/func_amd64.go
@@ -8,3 +8,6 @@ package startlinetest
// Defined in func_amd64.s, this is a trivial assembly function that calls
// runtime_test.callerStartLine.
func AsmFunc() int
+
+// Provided by runtime_test.
+var CallerStartLine func(bool) int
diff --git a/src/runtime/internal/startlinetest/func_amd64.s b/src/runtime/internal/startlinetest/func_amd64.s
index ace5b34e70..96982bedab 100644
--- a/src/runtime/internal/startlinetest/func_amd64.s
+++ b/src/runtime/internal/startlinetest/func_amd64.s
@@ -23,5 +23,6 @@
TEXT ·AsmFunc<ABIInternal>(SB),NOSPLIT,$8-0
NO_LOCAL_POINTERS
MOVQ $0, AX // wantInlined
- CALL runtime_test·callerStartLine<ABIInternal>(SB)
+ MOVQ ·CallerStartLine(SB), DX
+ CALL (DX)
RET
diff --git a/src/runtime/libfuzzer.go b/src/runtime/libfuzzer.go
index 013e7165b2..0ece035405 100644
--- a/src/runtime/libfuzzer.go
+++ b/src/runtime/libfuzzer.go
@@ -148,13 +148,8 @@ var __sanitizer_cov_trace_const_cmp8 byte
//go:cgo_import_static __sanitizer_cov_8bit_counters_init
var __sanitizer_cov_8bit_counters_init byte
-//go:linkname __start___sancov_cntrs __start___sancov_cntrs
-//go:cgo_import_static __start___sancov_cntrs
-var __start___sancov_cntrs byte
-
-//go:linkname __stop___sancov_cntrs __stop___sancov_cntrs
-//go:cgo_import_static __stop___sancov_cntrs
-var __stop___sancov_cntrs byte
+// start, stop markers of counters, set by the linker
+var __start___sancov_cntrs, __stop___sancov_cntrs byte
//go:linkname __sanitizer_cov_pcs_init __sanitizer_cov_pcs_init
//go:cgo_import_static __sanitizer_cov_pcs_init
diff --git a/src/runtime/mfinal_test.go b/src/runtime/mfinal_test.go
index 902ccc57f8..61d625ac27 100644
--- a/src/runtime/mfinal_test.go
+++ b/src/runtime/mfinal_test.go
@@ -53,7 +53,7 @@ func TestFinalizerType(t *testing.T) {
}},
}
- for i, tt := range finalizerTests {
+ for _, tt := range finalizerTests {
done := make(chan bool, 1)
go func() {
// allocate struct with pointer to avoid hitting tinyalloc.
@@ -71,11 +71,7 @@ func TestFinalizerType(t *testing.T) {
}()
<-done
runtime.GC()
- select {
- case <-ch:
- case <-time.After(time.Second * 4):
- t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
- }
+ <-ch
}
}
@@ -109,11 +105,7 @@ func TestFinalizerInterfaceBig(t *testing.T) {
}()
<-done
runtime.GC()
- select {
- case <-ch:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer for type *bigValue didn't run")
- }
+ <-ch
}
func fin(v *int) {
@@ -188,11 +180,7 @@ func TestEmptySlice(t *testing.T) {
fin := make(chan bool, 1)
runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
runtime.GC()
- select {
- case <-fin:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer of next object in memory didn't run")
- }
+ <-fin
xsglobal = xs // keep empty slice alive until here
}
@@ -220,11 +208,7 @@ func TestEmptyString(t *testing.T) {
// set finalizer on string contents of y
runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
runtime.GC()
- select {
- case <-fin:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer of next string in memory didn't run")
- }
+ <-fin
ssglobal = ss // keep 0-length string live until here
}
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index c4f3bb6a81..af5c18c301 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -136,8 +136,6 @@ func osinit() {
ncpu = getncpu()
physPageSize = getPageSize()
-
- osinit_hack()
}
func sysctlbynameInt32(name []byte) (int32, int32) {
diff --git a/src/runtime/race/internal/amd64v1/doc.go b/src/runtime/race/internal/amd64v1/doc.go
index 130b290bdb..ccb088cc46 100644
--- a/src/runtime/race/internal/amd64v1/doc.go
+++ b/src/runtime/race/internal/amd64v1/doc.go
@@ -5,4 +5,6 @@
// This package holds the race detector .syso for
// amd64 architectures with GOAMD64<v3.
+//go:build amd64 && ((linux && !amd64.v3) || darwin || freebsd || netbsd || openbsd || windows)
+
package amd64v1
diff --git a/src/runtime/race/internal/amd64v3/doc.go b/src/runtime/race/internal/amd64v3/doc.go
index 6983335281..215998a90c 100644
--- a/src/runtime/race/internal/amd64v3/doc.go
+++ b/src/runtime/race/internal/amd64v3/doc.go
@@ -5,4 +5,6 @@
// This package holds the race detector .syso for
// amd64 architectures with GOAMD64>=v3.
+//go:build amd64 && linux && amd64.v3
+
package amd64v3
diff --git a/src/runtime/race/race_v1_amd64.go b/src/runtime/race/race_v1_amd64.go
index b8a20315fd..7c40db1dcf 100644
--- a/src/runtime/race/race_v1_amd64.go
+++ b/src/runtime/race/race_v1_amd64.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build (linux && !amd64.v3) || darwin || freebsd || netbsd || openbsd || windows
-// +build linux,!amd64.v3 darwin freebsd netbsd openbsd windows
package race
diff --git a/src/runtime/race/race_v3_amd64.go b/src/runtime/race/race_v3_amd64.go
index 913bb77f48..80728d834a 100644
--- a/src/runtime/race/race_v3_amd64.go
+++ b/src/runtime/race/race_v3_amd64.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build linux && amd64.v3
-// +build linux,amd64.v3
package race
diff --git a/src/runtime/start_line_amd64_test.go b/src/runtime/start_line_amd64_test.go
index 57001e71de..305ed0b126 100644
--- a/src/runtime/start_line_amd64_test.go
+++ b/src/runtime/start_line_amd64_test.go
@@ -13,6 +13,8 @@ import (
// is only tested on amd64 to avoid the need for a proliferation of per-arch
// copies of this function.
func TestStartLineAsm(t *testing.T) {
+ startlinetest.CallerStartLine = callerStartLine
+
const wantLine = 23
got := startlinetest.AsmFunc()
if got != wantLine {
diff --git a/src/runtime/string_test.go b/src/runtime/string_test.go
index 1ea7f5e481..cfc0ad7cde 100644
--- a/src/runtime/string_test.go
+++ b/src/runtime/string_test.go
@@ -223,6 +223,19 @@ func TestLargeStringConcat(t *testing.T) {
}
}
+func TestConcatTempString(t *testing.T) {
+ s := "bytes"
+ b := []byte(s)
+ n := testing.AllocsPerRun(1000, func() {
+ if "prefix "+string(b)+" suffix" != "prefix bytes suffix" {
+ t.Fatalf("strings are not equal: '%v' and '%v'", "prefix "+string(b)+" suffix", "prefix bytes suffix")
+ }
+ })
+ if n != 0 {
+ t.Fatalf("want 0 allocs, got %v", n)
+ }
+}
+
func TestCompareTempString(t *testing.T) {
s := strings.Repeat("x", sizeNoStack)
b := []byte(s)
@@ -230,10 +243,24 @@ func TestCompareTempString(t *testing.T) {
if string(b) != s {
t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
}
+ if string(b) < s {
+ t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
+ }
+ if string(b) > s {
+ t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
+ }
if string(b) == s {
} else {
t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
}
+ if string(b) <= s {
+ } else {
+ t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
+ }
+ if string(b) >= s {
+ } else {
+ t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
+ }
})
if n != 0 {
t.Fatalf("want 0 allocs, got %v", n)
diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go
index 28dc291596..61b7f8c728 100644
--- a/src/runtime/sys_darwin.go
+++ b/src/runtime/sys_darwin.go
@@ -179,51 +179,6 @@ func pthread_kill(t pthread, sig uint32) {
}
func pthread_kill_trampoline()
-// osinit_hack is a clumsy hack to work around Apple libc bugs
-// causing fork+exec to hang in the child process intermittently.
-// See go.dev/issue/33565 and go.dev/issue/56784 for a few reports.
-//
-// The stacks obtained from the hung child processes are in
-// libSystem_atfork_child, which is supposed to reinitialize various
-// parts of the C library in the new process.
-//
-// One common stack dies in _notify_fork_child calling _notify_globals
-// (inlined) calling _os_alloc_once, because _os_alloc_once detects that
-// the once lock is held by the parent process and then calls
-// _os_once_gate_corruption_abort. The allocation is setting up the
-// globals for the notification subsystem. See the source code at [1].
-// To work around this, we can allocate the globals earlier in the Go
-// program's lifetime, before any execs are involved, by calling any
-// notify routine that is exported, calls _notify_globals, and doesn't do
-// anything too expensive otherwise. notify_is_valid_token(0) fits the bill.
-//
-// The other common stack dies in xpc_atfork_child calling
-// _objc_msgSend_uncached which ends up in
-// WAITING_FOR_ANOTHER_THREAD_TO_FINISH_CALLING_+initialize. Of course,
-// whatever thread the child is waiting for is in the parent process and
-// is not going to finish anything in the child process. There is no
-// public source code for these routines, so it is unclear exactly what
-// the problem is. However, xpc_atfork_child turns out to be exported
-// (for use by libSystem_atfork_child, which is in a different library,
-// so xpc_atfork_child is unlikely to be unexported any time soon).
-// It also stands to reason that since xpc_atfork_child is called at the
-// start of any forked child process, it can't be too harmful to call at
-// the start of an ordinary Go process. And whatever caches it needs for
-// a non-deadlocking fast path during exec empirically do get initialized
-// by calling it at startup.
-//
-// So osinit_hack_trampoline (in sys_darwin_$GOARCH.s) calls
-// notify_is_valid_token(0) and xpc_atfork_child(), which makes the
-// fork+exec hangs stop happening. If Apple fixes the libc bug in
-// some future version of macOS, then we can remove this awful code.
-//
-//go:nosplit
-func osinit_hack() {
- libcCall(unsafe.Pointer(abi.FuncPCABI0(osinit_hack_trampoline)), nil)
- return
-}
-func osinit_hack_trampoline()
-
// mmap is used to do low-level memory allocation via mmap. Don't allow stack
// splits, since this function (used by sysAlloc) is called in a lot of low-level
// parts of the runtime and callers often assume it won't acquire any locks.
@@ -593,6 +548,3 @@ func setNonblock(fd int32) {
//go:cgo_import_dynamic libc_pthread_cond_wait pthread_cond_wait "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic libc_pthread_cond_timedwait_relative_np pthread_cond_timedwait_relative_np "/usr/lib/libSystem.B.dylib"
//go:cgo_import_dynamic libc_pthread_cond_signal pthread_cond_signal "/usr/lib/libSystem.B.dylib"
-
-//go:cgo_import_dynamic libc_notify_is_valid_token notify_is_valid_token "/usr/lib/libSystem.B.dylib"
-//go:cgo_import_dynamic libc_xpc_atfork_child xpc_atfork_child "/usr/lib/libSystem.B.dylib"
diff --git a/src/runtime/sys_darwin_amd64.s b/src/runtime/sys_darwin_amd64.s
index 16783a4819..369b12e8f9 100644
--- a/src/runtime/sys_darwin_amd64.s
+++ b/src/runtime/sys_darwin_amd64.s
@@ -597,15 +597,6 @@ TEXT runtime·pthread_kill_trampoline(SB),NOSPLIT,$0
POPQ BP
RET
-TEXT runtime·osinit_hack_trampoline(SB),NOSPLIT,$0
- PUSHQ BP
- MOVQ SP, BP
- MOVQ $0, DI // arg 1 val
- CALL libc_notify_is_valid_token(SB)
- CALL libc_xpc_atfork_child(SB)
- POPQ BP
- RET
-
// syscall calls a function in libc on behalf of the syscall package.
// syscall takes a pointer to a struct like:
// struct {
diff --git a/src/runtime/sys_darwin_arm64.s b/src/runtime/sys_darwin_arm64.s
index 3cbac77394..4fa99cc0f9 100644
--- a/src/runtime/sys_darwin_arm64.s
+++ b/src/runtime/sys_darwin_arm64.s
@@ -458,12 +458,6 @@ TEXT runtime·pthread_setspecific_trampoline(SB),NOSPLIT,$0
BL libc_pthread_setspecific(SB)
RET
-TEXT runtime·osinit_hack_trampoline(SB),NOSPLIT,$0
- MOVD $0, R0 // arg 1 val
- BL libc_notify_is_valid_token(SB)
- BL libc_xpc_atfork_child(SB)
- RET
-
// syscall calls a function in libc on behalf of the syscall package.
// syscall takes a pointer to a struct like:
// struct {
diff --git a/src/sync/map_bench_test.go b/src/sync/map_bench_test.go
index 4815f57349..eebec3bacf 100644
--- a/src/sync/map_bench_test.go
+++ b/src/sync/map_bench_test.go
@@ -389,7 +389,6 @@ func BenchmarkCompareAndSwapNoExistingKey(b *testing.B) {
}
func BenchmarkCompareAndSwapValueNotEqual(b *testing.B) {
- const n = 1 << 10
benchMap(b, bench{
setup: func(_ *testing.B, m mapInterface) {
m.Store(0, 0)
diff --git a/src/syscall/exec_freebsd.go b/src/syscall/exec_freebsd.go
index b85bcd93a8..af5a4158f0 100644
--- a/src/syscall/exec_freebsd.go
+++ b/src/syscall/exec_freebsd.go
@@ -205,7 +205,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
if r1 != ppid {
pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
- _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
+ _, _, err1 = RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
if err1 != 0 {
goto childerror
}
diff --git a/src/syscall/exec_libc2.go b/src/syscall/exec_libc2.go
index 41bc79a721..6e3c2bf9d7 100644
--- a/src/syscall/exec_libc2.go
+++ b/src/syscall/exec_libc2.go
@@ -78,7 +78,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// About to call fork.
// No more allocation or calls of non-assembly functions.
runtime_BeforeFork()
- r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fork_trampoline), 0, 0, 0)
+ r1, _, err1 = rawSyscall(forkTrampoline, 0, 0, 0)
if err1 != 0 {
runtime_AfterFork()
return 0, err1
@@ -276,6 +276,6 @@ childerror:
// send error code on pipe
rawSyscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
- rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
+ rawSyscall(exitTrampoline, 253, 0, 0)
}
}
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index b61b51dff1..7e0c3d250b 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -470,7 +470,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
// so it is safe to always use _LINUX_CAPABILITY_VERSION_3.
caps.hdr.version = _LINUX_CAPABILITY_VERSION_3
- if _, _, err1 := RawSyscall(SYS_CAPGET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
+ if _, _, err1 = RawSyscall(SYS_CAPGET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
goto childerror
}
@@ -481,7 +481,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
caps.data[capToIndex(c)].inheritable |= capToMask(c)
}
- if _, _, err1 := RawSyscall(SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
+ if _, _, err1 = RawSyscall(SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
goto childerror
}
@@ -514,7 +514,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att
r1, _ = rawSyscallNoError(SYS_GETPPID, 0, 0, 0)
if r1 != ppid {
pid, _ := rawSyscallNoError(SYS_GETPID, 0, 0, 0)
- _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
+ _, _, err1 = RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
if err1 != 0 {
goto childerror
}
diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go
index a035d415ed..1e21fffaef 100644
--- a/src/syscall/exec_linux_test.go
+++ b/src/syscall/exec_linux_test.go
@@ -8,6 +8,7 @@ package syscall_test
import (
"bytes"
+ "errors"
"flag"
"fmt"
"internal/testenv"
@@ -504,7 +505,8 @@ func prepareCgroupFD(t *testing.T) (int, string) {
// Need an ability to create a sub-cgroup.
subCgroup, err := os.MkdirTemp(prefix+string(bytes.TrimSpace(cg)), "subcg-")
if err != nil {
- if os.IsPermission(err) {
+ // Running in an unprivileged container, this may also return EROFS #57262.
+ if os.IsPermission(err) || errors.Is(err, syscall.EROFS) {
t.Skip(err)
}
t.Fatal(err)
diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go
index d6b7890f55..8f28b5aa22 100644
--- a/src/syscall/exec_plan9.go
+++ b/src/syscall/exec_plan9.go
@@ -276,7 +276,7 @@ dirloop:
// Pass 3: close fd[i] if it was moved in the previous pass.
for i = 0; i < len(fd); i++ {
- if fd[i] >= 0 && fd[i] != int(i) {
+ if fd[i] >= len(fd) {
RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
}
}
diff --git a/src/syscall/syscall_darwin.go b/src/syscall/syscall_darwin.go
index a39e99dc63..5ec311962a 100644
--- a/src/syscall/syscall_darwin.go
+++ b/src/syscall/syscall_darwin.go
@@ -22,7 +22,34 @@ func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
-var dupTrampoline = abi.FuncPCABI0(libc_dup2_trampoline)
+// These are called from exec_libc2.go in the child of fork.
+// The names differ between macOS and OpenBSD, so we need
+// to declare the specific ones used here to keep the exec_libc2.go
+// code portable.
+//
+// We use __fork and __exit, not fork and exit, to avoid the libc atfork
+// and atexit handlers. The atfork handlers have caused fork child
+// hangs in the past (see #33565, #56784). The atexit handlers have
+// not, but the non-libc ports all invoke the system call, so doing
+// the same here makes sense. In general we wouldn't expect
+// atexit handlers to work terribly well in a fork child anyway.
+// (Also, perhaps the atfork handlers clear the atexit handlers,
+// in which case we definitely need to avoid calling the libc exit
+// if we bypass the libc fork.)
+//
+// Other calls that are made in the child after the fork are
+// ptrace, setsid, setpgid, getpid, ioctl, chroot, setgroups,
+// setgid, setuid, chdir, dup2, fcntl, close, execve, and write.
+// Those are all simple kernel wrappers that should be safe
+// to be called directly. The fcntl and ioctl functions do run
+// some code around the kernel call, but they don't call any
+// other functions, so for now we keep using them instead of
+// calling the lower-level __fcntl and __ioctl functions.
+var (
+ dupTrampoline = abi.FuncPCABI0(libc_dup2_trampoline)
+ exitTrampoline = abi.FuncPCABI0(libc___exit_trampoline)
+ forkTrampoline = abi.FuncPCABI0(libc___fork_trampoline)
+)
type SockaddrDatalink struct {
Len uint8
@@ -210,11 +237,12 @@ func Kill(pid int, signum Signal) (err error) { return kill(pid, int(signum), 1)
//sys writev(fd int, iovecs []Iovec) (cnt uintptr, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
-//sysnb fork() (pid int, err error)
+//sysnb __fork() (pid int, err error)
//sysnb ioctl(fd int, req int, arg int) (err error)
//sysnb ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) = SYS_ioctl
//sysnb execve(path *byte, argv **byte, envp **byte) (err error)
//sysnb exit(res int) (err error)
+//sysnb __exit(res int) (err error)
//sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error)
//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (val int, err error) = SYS_fcntl
//sys unlinkat(fd int, path string, flags int) (err error)
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index bdee570dda..d4cc34bdee 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -13,6 +13,7 @@ package syscall
import (
"internal/itoa"
+ "runtime"
"unsafe"
)
@@ -145,8 +146,17 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
return faccessat(dirfd, path, mode)
}
- if err := faccessat2(dirfd, path, mode, flags); err != ENOSYS && err != EPERM {
- return err
+ // Attempt to use the newer faccessat2, which supports flags directly,
+ // falling back if it doesn't exist.
+ //
+ // Don't attempt on Android, which does not allow faccessat2 through
+ // its seccomp policy [1] on any version of Android as of 2022-12-20.
+ //
+ // [1] https://cs.android.com/android/platform/superproject/+/master:bionic/libc/SECCOMP_BLOCKLIST_APP.TXT;l=4;drc=dbb8670dfdcc677f7e3b9262e93800fa14c4e417
+ if runtime.GOOS != "android" {
+ if err := faccessat2(dirfd, path, mode, flags); err != ENOSYS && err != EPERM {
+ return err
+ }
}
// The Linux kernel faccessat system call does not take any flags.
@@ -643,21 +653,6 @@ func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
return nil, EAFNOSUPPORT
}
-func Accept(fd int) (nfd int, sa Sockaddr, err error) {
- var rsa RawSockaddrAny
- var len _Socklen = SizeofSockaddrAny
- nfd, err = accept4(fd, &rsa, &len, 0)
- if err != nil {
- return
- }
- sa, err = anyToSockaddr(&rsa)
- if err != nil {
- Close(nfd)
- nfd = 0
- }
- return
-}
-
func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len _Socklen = SizeofSockaddrAny
diff --git a/src/syscall/syscall_linux_accept.go b/src/syscall/syscall_linux_accept.go
new file mode 100644
index 0000000000..66c0f84cb8
--- /dev/null
+++ b/src/syscall/syscall_linux_accept.go
@@ -0,0 +1,34 @@
+// Copyright 2009 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.
+
+// We require Linux kernel version 2.6.32. The accept4 system call was
+// added in version 2.6.28, so in general we can use accept4.
+// Unfortunately, for ARM only, accept4 was added in version 2.6.36.
+// Handle that case here, by using a copy of the Accept function that
+// we used in Go 1.17.
+
+//go:build linux && arm
+
+package syscall
+
+//sys accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error)
+
+func Accept(fd int) (nfd int, sa Sockaddr, err error) {
+ var rsa RawSockaddrAny
+ var len _Socklen = SizeofSockaddrAny
+ // Try accept4 first for Android and newer kernels.
+ nfd, err = accept4(fd, &rsa, &len, 0)
+ if err == ENOSYS {
+ nfd, err = accept(fd, &rsa, &len)
+ }
+ if err != nil {
+ return
+ }
+ sa, err = anyToSockaddr(&rsa)
+ if err != nil {
+ Close(nfd)
+ nfd = 0
+ }
+ return
+}
diff --git a/src/syscall/syscall_linux_accept4.go b/src/syscall/syscall_linux_accept4.go
new file mode 100644
index 0000000000..74898672c0
--- /dev/null
+++ b/src/syscall/syscall_linux_accept4.go
@@ -0,0 +1,25 @@
+// Copyright 2009 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.
+
+// This file provides the Accept function used on all systems
+// other than arm. See syscall_linux_accept.go for why.
+
+//go:build linux && !arm
+
+package syscall
+
+func Accept(fd int) (nfd int, sa Sockaddr, err error) {
+ var rsa RawSockaddrAny
+ var len _Socklen = SizeofSockaddrAny
+ nfd, err = accept4(fd, &rsa, &len, 0)
+ if err != nil {
+ return
+ }
+ sa, err = anyToSockaddr(&rsa)
+ if err != nil {
+ Close(nfd)
+ nfd = 0
+ }
+ return
+}
diff --git a/src/syscall/syscall_openbsd_libc.go b/src/syscall/syscall_openbsd_libc.go
index 516d02975c..6358a9a390 100644
--- a/src/syscall/syscall_openbsd_libc.go
+++ b/src/syscall/syscall_openbsd_libc.go
@@ -10,7 +10,11 @@ import (
"internal/abi"
)
-var dupTrampoline = abi.FuncPCABI0(libc_dup3_trampoline)
+var (
+ dupTrampoline = abi.FuncPCABI0(libc_dup3_trampoline)
+ exitTrampoline = abi.FuncPCABI0(libc_exit_trampoline)
+ forkTrampoline = abi.FuncPCABI0(libc_fork_trampoline)
+)
func init() {
execveOpenBSD = execve
diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go
index 6b3fff3f37..5e3f6cccf0 100644
--- a/src/syscall/zsyscall_darwin_amd64.go
+++ b/src/syscall/zsyscall_darwin_amd64.go
@@ -1734,8 +1734,8 @@ func libc_munmap_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func fork() (pid int, err error) {
- r0, _, e1 := rawSyscall(abi.FuncPCABI0(libc_fork_trampoline), 0, 0, 0)
+func __fork() (pid int, err error) {
+ r0, _, e1 := rawSyscall(abi.FuncPCABI0(libc___fork_trampoline), 0, 0, 0)
pid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
@@ -1743,9 +1743,9 @@ func fork() (pid int, err error) {
return
}
-func libc_fork_trampoline()
+func libc___fork_trampoline()
-//go:cgo_import_dynamic libc_fork fork "/usr/lib/libSystem.B.dylib"
+//go:cgo_import_dynamic libc___fork __fork "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1801,6 +1801,20 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func __exit(res int) (err error) {
+ _, _, e1 := rawSyscall(abi.FuncPCABI0(libc___exit_trampoline), uintptr(res), 0, 0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc___exit_trampoline()
+
+//go:cgo_import_dynamic libc___exit __exit "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
var _p0 unsafe.Pointer
if len(mib) > 0 {
diff --git a/src/syscall/zsyscall_darwin_amd64.s b/src/syscall/zsyscall_darwin_amd64.s
index 90e51fb9a4..cbb4496a50 100644
--- a/src/syscall/zsyscall_darwin_amd64.s
+++ b/src/syscall/zsyscall_darwin_amd64.s
@@ -221,14 +221,16 @@ TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
JMP libc_mmap(SB)
TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
JMP libc_munmap(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
- JMP libc_fork(SB)
+TEXT ·libc___fork_trampoline(SB),NOSPLIT,$0-0
+ JMP libc___fork(SB)
TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
JMP libc_ioctl(SB)
TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc___exit_trampoline(SB),NOSPLIT,$0-0
+ JMP libc___exit(SB)
TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go
index 61601449a0..d4c56be818 100644
--- a/src/syscall/zsyscall_darwin_arm64.go
+++ b/src/syscall/zsyscall_darwin_arm64.go
@@ -1734,8 +1734,8 @@ func libc_munmap_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func fork() (pid int, err error) {
- r0, _, e1 := rawSyscall(abi.FuncPCABI0(libc_fork_trampoline), 0, 0, 0)
+func __fork() (pid int, err error) {
+ r0, _, e1 := rawSyscall(abi.FuncPCABI0(libc___fork_trampoline), 0, 0, 0)
pid = int(r0)
if e1 != 0 {
err = errnoErr(e1)
@@ -1743,9 +1743,9 @@ func fork() (pid int, err error) {
return
}
-func libc_fork_trampoline()
+func libc___fork_trampoline()
-//go:cgo_import_dynamic libc_fork fork "/usr/lib/libSystem.B.dylib"
+//go:cgo_import_dynamic libc___fork __fork "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
@@ -1801,6 +1801,20 @@ func libc_exit_trampoline()
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func __exit(res int) (err error) {
+ _, _, e1 := rawSyscall(abi.FuncPCABI0(libc___exit_trampoline), uintptr(res), 0, 0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
+
+func libc___exit_trampoline()
+
+//go:cgo_import_dynamic libc___exit __exit "/usr/lib/libSystem.B.dylib"
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
var _p0 unsafe.Pointer
if len(mib) > 0 {
diff --git a/src/syscall/zsyscall_darwin_arm64.s b/src/syscall/zsyscall_darwin_arm64.s
index f00747939e..ce1850e0ae 100644
--- a/src/syscall/zsyscall_darwin_arm64.s
+++ b/src/syscall/zsyscall_darwin_arm64.s
@@ -221,14 +221,16 @@ TEXT ·libc_mmap_trampoline(SB),NOSPLIT,$0-0
JMP libc_mmap(SB)
TEXT ·libc_munmap_trampoline(SB),NOSPLIT,$0-0
JMP libc_munmap(SB)
-TEXT ·libc_fork_trampoline(SB),NOSPLIT,$0-0
- JMP libc_fork(SB)
+TEXT ·libc___fork_trampoline(SB),NOSPLIT,$0-0
+ JMP libc___fork(SB)
TEXT ·libc_ioctl_trampoline(SB),NOSPLIT,$0-0
JMP libc_ioctl(SB)
TEXT ·libc_execve_trampoline(SB),NOSPLIT,$0-0
JMP libc_execve(SB)
TEXT ·libc_exit_trampoline(SB),NOSPLIT,$0-0
JMP libc_exit(SB)
+TEXT ·libc___exit_trampoline(SB),NOSPLIT,$0-0
+ JMP libc___exit(SB)
TEXT ·libc_sysctl_trampoline(SB),NOSPLIT,$0-0
JMP libc_sysctl(SB)
TEXT ·libc_unlinkat_trampoline(SB),NOSPLIT,$0-0
diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go
index db5ec60448..69f811a0ec 100644
--- a/src/syscall/zsyscall_linux_arm.go
+++ b/src/syscall/zsyscall_linux_arm.go
@@ -1,4 +1,4 @@
-// mksyscall.pl -l32 -arm -tags linux,arm syscall_linux.go syscall_linux_arm.go
+// mksyscall.pl -l32 -arm -tags linux,arm syscall_linux.go syscall_linux_arm.go syscall_linux_accept.go
// Code generated by the command above; DO NOT EDIT.
//go:build linux && arm
@@ -1601,3 +1601,14 @@ func setrlimit(resource int, rlim *rlimit32) (err error) {
}
return
}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
+func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) {
+ r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
+ fd = int(r0)
+ if e1 != 0 {
+ err = errnoErr(e1)
+ }
+ return
+}
diff --git a/src/testing/testing.go b/src/testing/testing.go
index acd28667c2..fc34cbf28b 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -13,8 +13,9 @@
//
// Within these functions, use the Error, Fail or related methods to signal failure.
//
-// To write a new test suite, create a file whose name ends _test.go that
-// contains the TestXxx functions as described here.
+// To write a new test suite, create a file that
+// contains the TestXxx functions as described here,
+// and give that file a name ending in "_test.go".
// The file will be excluded from regular
// package builds but will be included when the "go test" command is run.
//
diff --git a/test/fixedbugs/issue57184.go b/test/fixedbugs/issue57184.go
new file mode 100644
index 0000000000..1384b50be8
--- /dev/null
+++ b/test/fixedbugs/issue57184.go
@@ -0,0 +1,40 @@
+// run
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "log"
+ "reflect"
+ "sort"
+)
+
+func main() {
+ const length = 257
+ x := make([]int64, length)
+ for i := 0; i < length; i++ {
+ x[i] = int64(i) * 27644437 % int64(length)
+ }
+
+ isLessStatic := func(i, j int) bool {
+ return x[i] < x[j]
+ }
+
+ isLessReflect := reflect.MakeFunc(reflect.TypeOf(isLessStatic), func(args []reflect.Value) []reflect.Value {
+ i := args[0].Int()
+ j := args[1].Int()
+ b := x[i] < x[j]
+ return []reflect.Value{reflect.ValueOf(b)}
+ }).Interface().(func(i, j int) bool)
+
+ sort.SliceStable(x, isLessReflect)
+
+ for i := 0; i < length-1; i++ {
+ if x[i] >= x[i+1] {
+ log.Fatalf("not sorted! (length=%v, idx=%v)\n%v\n", length, i, x)
+ }
+ }
+}
diff --git a/test/fixedbugs/issue57309.go b/test/fixedbugs/issue57309.go
new file mode 100644
index 0000000000..ec6a397574
--- /dev/null
+++ b/test/fixedbugs/issue57309.go
@@ -0,0 +1,23 @@
+// run
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+type I interface {
+ M()
+}
+
+type S struct {
+}
+
+func (*S) M() {
+}
+
+func main() {
+ func() {
+ I(&S{}).M()
+ }()
+}