diff --git a/AUTHORS b/AUTHORS
index 8d8d83605e..8d7e196732 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -817,6 +817,7 @@ Lehner Florian
Leigh McCulloch
Leo Antunes
Leon Klingele
+Leonard Wang
Leonel Quinteros
Lev Shamardin
Lewin Bormann
@@ -1015,6 +1016,7 @@ Nathan Youngman
Nathaniel Cook
Naveen Kumar Sangi
Neelesh Chandola
+Neil Alexander
Neil Lyons
Netflix, Inc.
Neuman Vong
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 74d4687373..a548cb0e2f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1569,7 +1569,7 @@ Leigh McCulloch
Leo Antunes
Leo Rudberg
Leon Klingele
-Leonard Wang
+Leonard Wang
Leonardo Comelli
Leonel Quinteros
Lev Shamardin
@@ -1901,6 +1901,7 @@ Naveen Kumar Sangi
Neeilan Selvalingam
Neelesh Chandola
Nehal J Wani
+Neil Alexander
Neil Lyons
Neuman Vong
Neven Sajko
diff --git a/api/next.txt b/api/next.txt
index 1192fc9069..cc4120b7ab 100644
--- a/api/next.txt
+++ b/api/next.txt
@@ -1,111 +1,13 @@
-pkg syscall (darwin-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (darwin-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (darwin-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (darwin-amd64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (darwin-amd64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (darwin-amd64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (darwin-amd64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (darwin-amd64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-386), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-386), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-386), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-386), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-386-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-386-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-386-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-386-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-amd64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-amd64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-amd64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-amd64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-amd64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-arm), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-arm), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-arm), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-arm), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (freebsd-arm-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (freebsd-arm-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (freebsd-arm-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (freebsd-arm-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-386), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-386), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-386), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-386), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-386-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-386-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-386-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-386-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-amd64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-amd64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-amd64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-amd64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-amd64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-arm), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-arm), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-arm), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-arm), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (linux-arm-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (linux-arm-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (linux-arm-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (linux-arm-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-386), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-386), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-386), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-386), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-386-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-386-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-386-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-386-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-amd64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-amd64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-amd64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-amd64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-amd64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-arm), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-arm), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-arm), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-arm), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-arm-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-arm-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-arm-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-arm-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-arm64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-arm64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-arm64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-arm64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (netbsd-arm64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (netbsd-arm64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (netbsd-arm64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (netbsd-arm64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (openbsd-386), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (openbsd-386), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (openbsd-386), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (openbsd-386), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (openbsd-386-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (openbsd-386-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (openbsd-386-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (openbsd-386-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (openbsd-amd64), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (openbsd-amd64), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (openbsd-amd64), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (openbsd-amd64), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (openbsd-amd64-cgo), func RecvfromInet4(int, []uint8, int, *SockaddrInet4) (int, error)
-pkg syscall (openbsd-amd64-cgo), func RecvfromInet6(int, []uint8, int, *SockaddrInet6) (int, error)
-pkg syscall (openbsd-amd64-cgo), func SendtoInet4(int, []uint8, int, SockaddrInet4) error
-pkg syscall (openbsd-amd64-cgo), func SendtoInet6(int, []uint8, int, SockaddrInet6) error
-pkg syscall (windows-386), func WSASendtoInet4(Handle, *WSABuf, uint32, *uint32, uint32, SockaddrInet4, *Overlapped, *uint8) error
-pkg syscall (windows-386), func WSASendtoInet6(Handle, *WSABuf, uint32, *uint32, uint32, SockaddrInet6, *Overlapped, *uint8) error
-pkg syscall (windows-amd64), func WSASendtoInet4(Handle, *WSABuf, uint32, *uint32, uint32, SockaddrInet4, *Overlapped, *uint8) error
-pkg syscall (windows-amd64), func WSASendtoInet6(Handle, *WSABuf, uint32, *uint32, uint32, SockaddrInet6, *Overlapped, *uint8) error
+pkg debug/buildinfo, func Read(io.ReaderAt) (*debug.BuildInfo, error)
+pkg debug/buildinfo, func ReadFile(string) (*debug.BuildInfo, error)
+pkg debug/buildinfo, type BuildInfo = debug.BuildInfo
+pkg runtime/debug, method (*BuildInfo) MarshalText() ([]byte, error)
+pkg runtime/debug, method (*BuildInfo) UnmarshalText() ([]byte, error)
+pkg runtime/debug, type BuildInfo struct, GoVersion string
+pkg runtime/debug, type BuildInfo struct, Settings []BuildSetting
+pkg runtime/debug, type BuildSetting struct
+pkg runtime/debug, type BuildSetting struct, Key string
+pkg runtime/debug, type BuildSetting struct, Value string
pkg testing, func Fuzz(func(*F)) FuzzResult
pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
pkg testing, func RunFuzzTargets(func(string, string) (bool, error), []InternalFuzzTarget) bool
diff --git a/doc/go1.18.html b/doc/go1.18.html
index a5b47fa261..385a1ae804 100644
--- a/doc/go1.18.html
+++ b/doc/go1.18.html
@@ -65,6 +65,44 @@ Do not send CLs removing the interior tags from such phrases.
and installs packages, as before.
+
+ The go command now embeds version control information in
+ binaries including the currently checked-out revision, commit time, and a
+ flag indicating whether edited or untracked files are present. Version
+ control information is embedded if the go command is invoked in
+ a directory within a Git, Mercurial, Fossil, or Bazaar repository, and the
+ main package and its containing main module are in the same
+ repository. This information may be omitted using the flag
+ -buildvcs=false.
+
+
+
+ Additionally, the go command embeds information about the build
+ including build and tool tags (set with -tags), compiler,
+ assembler, and linker flags (like -gcflags), whether cgo was
+ enabled, and if it was, the values of the cgo environment variables
+ (like CGO_CFLAGS). This information may be omitted using the
+ flag -buildinfo=false. Both VCS and build information may be
+ read together with module information using go
+ version -m file or
+ runtime/debug.ReadBuildInfo (for the currently running binary)
+ or the new debug/buildinfo
+ package.
+
+
+
+ If the main module's go.mod file
+ specifies go 1.17
+ or higher, go mod download without
+ arguments now downloads source code for only the modules
+ explicitly required in the main
+ module's go.mod file. (In a go 1.17 or
+ higher module, that set already includes all dependencies needed to build the
+ packages and tests in the main module.)
+ To also download source code for transitive dependencies, use
+ go mod download all.
+
+
TODO: complete this section, or delete if not needed
@@ -74,7 +112,7 @@ Do not send CLs removing the interior tags from such phrases.
gofmt now reads and formats input files concurrently, with a
memory limit proportional to GOMAXPROCS. On a machine with
- multiple CPUs, gofmt should now be significantly faster.
+ multiple CPUs, gofmt should now be significantly faster.
@@ -98,10 +136,33 @@ Do not send CLs removing the interior tags from such phrases.
Core library
+TODO
TODO: complete this section
+New net/netip package
+
+ The new net/netip
+ package defines a new IP address type, Addr.
+ Compared to the existing
+ net.IP type, the netip.Addr type takes less
+ memory, is immutable, and is comparable so it supports ==
+ and can be used as a map key.
+
+
+ In addition to Addr, the package defines
+ AddrPort, representing
+ an IP and port, and
+ Prefix, representing
+ a network CIDR prefix.
+
+
+ The net package now has methods to send and receive UDP packets
+ using netip.Addr values instead of the relatively heavy
+ *net.UDPAddr values.
+
+
Minor changes to the library
@@ -114,6 +175,19 @@ Do not send CLs removing the interior tags from such phrases.
TODO: complete this section
+- debug/buildinfo
+ -
+
+ This new package provides access to module versions, version control
+ information, and build flags embedded in executable files built by
+ the go command. The same information is also available via
+ runtime/debug.ReadBuildInfo
+ for the currently running binary and via go
+ version -m on the command line.
+
+
+
+
- image/draw
-
@@ -127,12 +201,35 @@ Do not send CLs removing the interior tags from such phrases.
+- reflect
+ -
+
+ The new
+ Value.SetIterKey
+ and Value.SetIterValue
+ methods set a Value using a map iterator as the source. They are equivalent to
+ Value.Set(iter.Key()) and Value.Set(iter.Value()) but
+ do fewer allocations.
+
+
+ -
+
+ The new
+ Value.UnsafePointer
+ method returns the Value's value as an unsafe.Pointer.
+ This allows callers to migrate from Value.UnsafeAddr
+ and Value.Pointer
+ to eliminate the need to perform uintptr to unsafe.Pointer conversions at the callsite (as unsafe.Pointer rules require).
+
+
+
+
- syscall
-
The new function SyscallN
has been introduced for Windows, allowing for calls with arbitrary number
- of arguments. As results,
+ of arguments. As a result,
Syscall,
Syscall6,
Syscall9,
diff --git a/doc/go_spec.html b/doc/go_spec.html
index c8051f58af..46eebb5713 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
@@ -4598,7 +4598,8 @@ a block. The following statements are terminating:
A "for" statement in which:
- there are no "break" statements referring to the "for" statement, and
- - the loop condition is absent.
+ - the loop condition is absent, and
+ - the "for" statement does not use a range clause.
diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go
index 55be3c5f70..a2b43bb72d 100644
--- a/misc/cgo/testcarchive/carchive_test.go
+++ b/misc/cgo/testcarchive/carchive_test.go
@@ -931,3 +931,55 @@ func TestManyCalls(t *testing.T) {
t.Error(err)
}
}
+
+// Issue 49288.
+func TestPreemption(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skipping asynchronous preemption test with gccgo")
+ }
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp8" + exeSuffix)
+ os.Remove("libgo8.a")
+ os.Remove("libgo8.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo8.h")
+
+ ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ argv := cmdToRun("./testp8")
+ cmd = exec.Command(argv[0], argv[1:]...)
+ var sb strings.Builder
+ cmd.Stdout = &sb
+ cmd.Stderr = &sb
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ timer := time.AfterFunc(time.Minute,
+ func() {
+ t.Error("test program timed out")
+ cmd.Process.Kill()
+ },
+ )
+ defer timer.Stop()
+
+ if err := cmd.Wait(); err != nil {
+ t.Log(sb.String())
+ t.Error(err)
+ }
+}
diff --git a/misc/cgo/testcarchive/testdata/libgo8/a.go b/misc/cgo/testcarchive/testdata/libgo8/a.go
new file mode 100644
index 0000000000..718418ecb8
--- /dev/null
+++ b/misc/cgo/testcarchive/testdata/libgo8/a.go
@@ -0,0 +1,36 @@
+// Copyright 2021 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 "C"
+
+import (
+ "os"
+ "runtime"
+ "sync/atomic"
+)
+
+var started int32
+
+// Start a goroutine that loops forever.
+func init() {
+ runtime.GOMAXPROCS(1)
+ go func() {
+ for {
+ atomic.StoreInt32(&started, 1)
+ }
+ }()
+}
+
+//export GoFunction8
+func GoFunction8() {
+ for atomic.LoadInt32(&started) == 0 {
+ runtime.Gosched()
+ }
+ os.Exit(0)
+}
+
+func main() {
+}
diff --git a/misc/cgo/testcarchive/testdata/main8.c b/misc/cgo/testcarchive/testdata/main8.c
new file mode 100644
index 0000000000..95fb7a349e
--- /dev/null
+++ b/misc/cgo/testcarchive/testdata/main8.c
@@ -0,0 +1,16 @@
+// Copyright 2021 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.
+
+// Test preemption.
+
+#include
+
+#include "libgo8.h"
+
+int main() {
+ GoFunction8();
+
+ // That should have exited the program.
+ abort();
+}
diff --git a/misc/cgo/testcshared/testdata/libgo2/dup2.go b/misc/cgo/testcshared/testdata/libgo2/dup2.go
index d18f0b130d..d343aa54d9 100644
--- a/misc/cgo/testcshared/testdata/libgo2/dup2.go
+++ b/misc/cgo/testcshared/testdata/libgo2/dup2.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin dragonfly freebsd linux,!arm64 netbsd openbsd
+// +build darwin dragonfly freebsd linux,!arm64,!riscv64 netbsd openbsd
package main
diff --git a/misc/cgo/testcshared/testdata/libgo2/dup3.go b/misc/cgo/testcshared/testdata/libgo2/dup3.go
index c9c65a6e3c..459f0dc196 100644
--- a/misc/cgo/testcshared/testdata/libgo2/dup3.go
+++ b/misc/cgo/testcshared/testdata/libgo2/dup3.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build linux,arm64
+// +build linux,arm64 linux,riscv64
package main
diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go
new file mode 100644
index 0000000000..dbcce2fe28
--- /dev/null
+++ b/misc/cgo/testsanitizers/asan_test.go
@@ -0,0 +1,66 @@
+// Copyright 2021 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 TestASAN(t *testing.T) {
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The asan tests require support for the -asan option.
+ if !aSanSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+ }
+
+ t.Parallel()
+ requireOvercommit(t)
+ config := configure("address")
+ config.skipIfCSanitizerBroken(t)
+
+ mustRun(t, config.goCmd("build", "std"))
+
+ cases := []struct {
+ src string
+ memoryAccessError string
+ }{
+ {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free"},
+ {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow"},
+ {src: "asan3_fail.go", memoryAccessError: "use-after-poison"},
+ {src: "asan4_fail.go", memoryAccessError: "use-after-poison"},
+ {src: "asan_useAfterReturn.go"},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.src, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ outPath := dir.Join(name)
+ mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+
+ cmd := hangProneCmd(outPath)
+ if tc.memoryAccessError != "" {
+ out, err := cmd.CombinedOutput()
+ if err != nil && strings.Contains(string(out), tc.memoryAccessError) {
+ return
+ }
+ t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
+ }
+ mustRun(t, cmd)
+ })
+ }
+}
diff --git a/misc/cgo/testsanitizers/cc_test.go b/misc/cgo/testsanitizers/cc_test.go
index 7af30ab557..b776afa3e6 100644
--- a/misc/cgo/testsanitizers/cc_test.go
+++ b/misc/cgo/testsanitizers/cc_test.go
@@ -267,6 +267,9 @@ func configure(sanitizer string) *config {
c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
}
+ case "address":
+ c.goFlags = append(c.goFlags, "-asan")
+
default:
panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
}
@@ -450,3 +453,14 @@ func mSanSupported(goos, goarch string) bool {
return false
}
}
+
+// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported,
+// because the internal pacakage can't be used here.
+func aSanSupported(goos, goarch string) bool {
+ switch goos {
+ case "linux":
+ return goarch == "amd64" || goarch == "arm64"
+ default:
+ return false
+ }
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan1_fail.go b/misc/cgo/testsanitizers/testdata/asan1_fail.go
new file mode 100644
index 0000000000..e60db76981
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/asan1_fail.go
@@ -0,0 +1,28 @@
+// Copyright 2021 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
+
+/*
+#include
+#include
+
+int *p;
+int* test() {
+ p = (int *)malloc(2 * sizeof(int));
+ free(p);
+ return p;
+}
+*/
+import "C"
+import "fmt"
+
+func main() {
+ // C passes Go an invalid pointer.
+ a := C.test()
+ // Use after free
+ *a = 2
+ // We shouldn't get here; asan should stop us first.
+ fmt.Println(*a)
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan2_fail.go b/misc/cgo/testsanitizers/testdata/asan2_fail.go
new file mode 100644
index 0000000000..e35670c440
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/asan2_fail.go
@@ -0,0 +1,34 @@
+// Copyright 2021 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
+
+/*
+#include
+#include
+
+int *p;
+int* f() {
+ int i;
+ p = (int *)malloc(5*sizeof(int));
+ for (i = 0; i < 5; i++) {
+ p[i] = i+10;
+ }
+ return p;
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+func main() {
+ a := C.f()
+ q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
+ // Access to C pointer out of bounds.
+ *q5 = 100
+ // We shouldn't get here; asan should stop us first.
+ fmt.Printf("q5: %d, %x\n", *q5, q5)
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan3_fail.go b/misc/cgo/testsanitizers/testdata/asan3_fail.go
new file mode 100644
index 0000000000..9f6d26dd89
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/asan3_fail.go
@@ -0,0 +1,23 @@
+// Copyright 2021 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
+
+/*
+#include
+#include
+
+void test(int *a) {
+ // Access Go pointer out of bounds.
+ int c = a[5]; // BOOM
+ // We shouldn't get here; asan should stop us first.
+ printf("a[5]=%d\n", c);
+}
+*/
+import "C"
+
+func main() {
+ cIntSlice := []C.int{200, 201, 203, 203, 204}
+ C.test(&cIntSlice[0])
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan4_fail.go b/misc/cgo/testsanitizers/testdata/asan4_fail.go
new file mode 100644
index 0000000000..12098458ae
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/asan4_fail.go
@@ -0,0 +1,22 @@
+// Copyright 2021 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
+
+/*
+#include
+#include
+
+void test(int* a) {
+ // Access Go pointer out of bounds.
+ a[3] = 300; // BOOM
+ // We shouldn't get here; asan should stop us first.
+ printf("a[3]=%d\n", a[3]);
+}*/
+import "C"
+
+func main() {
+ var cIntArray [2]C.int
+ C.test(&cIntArray[0]) // cIntArray is moved to heap.
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go
new file mode 100644
index 0000000000..3d3d5a6ab1
--- /dev/null
+++ b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go
@@ -0,0 +1,26 @@
+// Copyright 2021 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
+
+// The -fsanitize=address option of C compier can detect stack-use-after-return bugs.
+// In the following program, the local variable 'local' was moved to heap by the Go
+// compiler because foo() is returning the reference to 'local', and return stack of
+// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local'
+// must be available even after foo() has finished. Therefore, Go has no such issue.
+
+import "fmt"
+
+var ptr *int
+
+func main() {
+ foo()
+ fmt.Printf("ptr=%x, %v", *ptr, ptr)
+}
+
+func foo() {
+ var local int
+ local = 1
+ ptr = &local // local is moved to heap.
+}
diff --git a/misc/wasm/go_js_wasm_exec b/misc/wasm/go_js_wasm_exec
index b700722dfe..fcbd0e4fc8 100755
--- a/misc/wasm/go_js_wasm_exec
+++ b/misc/wasm/go_js_wasm_exec
@@ -11,4 +11,4 @@ while [ -h "$SOURCE" ]; do
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
-exec node "$DIR/wasm_exec.js" "$@"
+exec node "$DIR/wasm_exec_node.js" "$@"
diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js
index e2f3cda995..9ce6a20c3f 100644
--- a/misc/wasm/wasm_exec.js
+++ b/misc/wasm/wasm_exec.js
@@ -1,49 +1,19 @@
// Copyright 2018 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.
+
"use strict";
(() => {
- // Map multiple JavaScript environments to a single common API,
- // preferring web standards over Node.js API.
- //
- // Environments considered:
- // - Browsers
- // - Node.js
- // - Electron
- // - Parcel
- // - Webpack
-
- if (typeof global !== "undefined") {
- // global already exists
- } else if (typeof window !== "undefined") {
- window.global = window;
- } else if (typeof self !== "undefined") {
- self.global = self;
- } else {
- throw new Error("cannot export Go (neither global, window nor self is defined)");
- }
-
- if (!global.require && typeof require !== "undefined") {
- global.require = require;
- }
-
- if (!global.fs && global.require) {
- const fs = require("fs");
- if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) {
- global.fs = fs;
- }
- }
-
const enosys = () => {
const err = new Error("not implemented");
err.code = "ENOSYS";
return err;
};
- if (!global.fs) {
+ if (!globalThis.fs) {
let outputBuf = "";
- global.fs = {
+ globalThis.fs = {
constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
@@ -88,8 +58,8 @@
};
}
- if (!global.process) {
- global.process = {
+ if (!globalThis.process) {
+ globalThis.process = {
getuid() { return -1; },
getgid() { return -1; },
geteuid() { return -1; },
@@ -103,47 +73,26 @@
}
}
- if (!global.crypto && global.require) {
- const nodeCrypto = require("crypto");
- global.crypto = {
- getRandomValues(b) {
- nodeCrypto.randomFillSync(b);
- },
- };
- }
- if (!global.crypto) {
- throw new Error("global.crypto is not available, polyfill required (getRandomValues only)");
+ if (!globalThis.crypto) {
+ throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
}
- if (!global.performance) {
- global.performance = {
- now() {
- const [sec, nsec] = process.hrtime();
- return sec * 1000 + nsec / 1000000;
- },
- };
+ if (!globalThis.performance) {
+ throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
}
- if (!global.TextEncoder && global.require) {
- global.TextEncoder = require("util").TextEncoder;
- }
- if (!global.TextEncoder) {
- throw new Error("global.TextEncoder is not available, polyfill required");
+ if (!globalThis.TextEncoder) {
+ throw new Error("globalThis.TextEncoder is not available, polyfill required");
}
- if (!global.TextDecoder && global.require) {
- global.TextDecoder = require("util").TextDecoder;
+ if (!globalThis.TextDecoder) {
+ throw new Error("globalThis.TextDecoder is not available, polyfill required");
}
- if (!global.TextDecoder) {
- throw new Error("global.TextDecoder is not available, polyfill required");
- }
-
- // End of polyfills for common API.
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
- global.Go = class {
+ globalThis.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
@@ -518,7 +467,7 @@
null,
true,
false,
- global,
+ globalThis,
this,
];
this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
@@ -527,7 +476,7 @@
[null, 2],
[true, 3],
[false, 4],
- [global, 5],
+ [globalThis, 5],
[this, 6],
]);
this._idPool = []; // unused ids that have been garbage collected
@@ -570,9 +519,9 @@
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
- const wasmMinDataAddr = 4096 + 4096;
+ const wasmMinDataAddr = 4096 + 8192;
if (offset >= wasmMinDataAddr) {
- throw new Error("command line too long");
+ throw new Error("total length of command line and environment variables exceeds limit");
}
this._inst.exports.run(argc, argv);
@@ -602,36 +551,4 @@
};
}
}
-
- if (
- typeof module !== "undefined" &&
- global.require &&
- global.require.main === module &&
- global.process &&
- global.process.versions &&
- !global.process.versions.electron
- ) {
- if (process.argv.length < 3) {
- console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
- process.exit(1);
- }
-
- const go = new Go();
- go.argv = process.argv.slice(2);
- go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
- go.exit = process.exit;
- WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
- process.on("exit", (code) => { // Node.js exits if no event handler is pending
- if (code === 0 && !go.exited) {
- // deadlock, make Go print error and stack traces
- go._pendingEvent = { id: 0 };
- go._resume();
- }
- });
- return go.run(result.instance);
- }).catch((err) => {
- console.error(err);
- process.exit(1);
- });
- }
})();
diff --git a/misc/wasm/wasm_exec_node.js b/misc/wasm/wasm_exec_node.js
new file mode 100644
index 0000000000..f9200ca950
--- /dev/null
+++ b/misc/wasm/wasm_exec_node.js
@@ -0,0 +1,49 @@
+// Copyright 2021 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.
+
+"use strict";
+
+if (process.argv.length < 3) {
+ console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
+ process.exit(1);
+}
+
+globalThis.require = require;
+globalThis.fs = require("fs");
+globalThis.TextEncoder = require("util").TextEncoder;
+globalThis.TextDecoder = require("util").TextDecoder;
+
+globalThis.performance = {
+ now() {
+ const [sec, nsec] = process.hrtime();
+ return sec * 1000 + nsec / 1000000;
+ },
+};
+
+const crypto = require("crypto");
+globalThis.crypto = {
+ getRandomValues(b) {
+ crypto.randomFillSync(b);
+ },
+};
+
+require("./wasm_exec");
+
+const go = new Go();
+go.argv = process.argv.slice(2);
+go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env);
+go.exit = process.exit;
+WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
+ process.on("exit", (code) => { // Node.js exits if no event handler is pending
+ if (code === 0 && !go.exited) {
+ // deadlock, make Go print error and stack traces
+ go._pendingEvent = { id: 0 };
+ go._resume();
+ }
+ });
+ return go.run(result.instance);
+}).catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/src/archive/tar/stat_actime1.go b/src/archive/tar/stat_actime1.go
index 4fdf2a04b3..c4c2480fee 100644
--- a/src/archive/tar/stat_actime1.go
+++ b/src/archive/tar/stat_actime1.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || linux || dragonfly || openbsd || solaris
-// +build aix linux dragonfly openbsd solaris
package tar
diff --git a/src/archive/tar/stat_actime2.go b/src/archive/tar/stat_actime2.go
index 5a9a35cbb4..f76d6be220 100644
--- a/src/archive/tar/stat_actime2.go
+++ b/src/archive/tar/stat_actime2.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build darwin || freebsd || netbsd
-// +build darwin freebsd netbsd
package tar
diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go
index 3957349d6e..717a0b3abc 100644
--- a/src/archive/tar/stat_unix.go
+++ b/src/archive/tar/stat_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris
-// +build aix linux darwin dragonfly freebsd openbsd netbsd solaris
package tar
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index c91a8d00e6..e40a2c656b 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -741,6 +741,9 @@ func (r *Reader) initFileList() {
for _, file := range r.File {
isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/'
name := toValidName(file.Name)
+ if name == "" {
+ continue
+ }
for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) {
dirs[dir] = true
}
@@ -782,8 +785,11 @@ func fileEntryLess(x, y string) bool {
func (r *Reader) Open(name string) (fs.File, error) {
r.initFileList()
+ if !fs.ValidPath(name) {
+ return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
+ }
e := r.openLookup(name)
- if e == nil || !fs.ValidPath(name) {
+ if e == nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
if e.isDir {
@@ -797,7 +803,7 @@ func (r *Reader) Open(name string) (fs.File, error) {
}
func split(name string) (dir, elem string, isDir bool) {
- if name[len(name)-1] == '/' {
+ if len(name) > 0 && name[len(name)-1] == '/' {
isDir = true
name = name[:len(name)-1]
}
diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go
index afb03ace24..a54915316c 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -13,6 +13,7 @@ import (
"io/fs"
"os"
"path/filepath"
+ "reflect"
"regexp"
"strings"
"testing"
@@ -1202,6 +1203,15 @@ func TestCVE202127919(t *testing.T) {
if err != nil {
t.Errorf("Error reading file: %v", err)
}
+ if len(r.File) != 1 {
+ t.Fatalf("No entries in the file list")
+ }
+ if r.File[0].Name != "../test.txt" {
+ t.Errorf("Unexpected entry name: %s", r.File[0].Name)
+ }
+ if _, err := r.File[0].Open(); err != nil {
+ t.Errorf("Error opening file: %v", err)
+ }
}
func TestReadDataDescriptor(t *testing.T) {
@@ -1402,3 +1412,121 @@ func TestCVE202139293(t *testing.T) {
t.Fatalf("unexpected error, got: %v, want: %v", err, ErrFormat)
}
}
+
+func TestCVE202141772(t *testing.T) {
+ // Archive contains a file whose name is exclusively made up of '/', '\'
+ // characters, or "../", "..\" paths, which would previously cause a panic.
+ //
+ // Length Method Size Cmpr Date Time CRC-32 Name
+ // -------- ------ ------- ---- ---------- ----- -------- ----
+ // 0 Stored 0 0% 08-05-2021 18:32 00000000 /
+ // 0 Stored 0 0% 09-14-2021 12:59 00000000 //
+ // 0 Stored 0 0% 09-14-2021 12:59 00000000 \
+ // 11 Stored 11 0% 09-14-2021 13:04 0d4a1185 /test.txt
+ // -------- ------- --- -------
+ // 11 11 0% 4 files
+ data := []byte{
+ 0x50, 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x06, 0x94, 0x05, 0x53, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2f, 0x50,
+ 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x50,
+ 0x4b, 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x5c, 0x50, 0x4b,
+ 0x03, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11, 0x4a, 0x0d,
+ 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
+ 0x74, 0x2e, 0x74, 0x78, 0x74, 0x68, 0x65, 0x6c,
+ 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
+ 0x50, 0x4b, 0x01, 0x02, 0x14, 0x03, 0x0a, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x06, 0x94, 0x05, 0x53,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0xed, 0x41, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x50,
+ 0x4b, 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x78, 0x67, 0x2e, 0x53, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x00, 0x1f, 0x00, 0x00, 0x00, 0x2f, 0x2f, 0x0a,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x18, 0x00, 0x93, 0x98, 0x25, 0x57, 0x25,
+ 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
+ 0xa9, 0xd7, 0x01, 0x93, 0x98, 0x25, 0x57, 0x25,
+ 0xa9, 0xd7, 0x01, 0x50, 0x4b, 0x01, 0x02, 0x3f,
+ 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
+ 0x67, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00,
+ 0x00, 0x5c, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x93, 0x98,
+ 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
+ 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x93, 0x98,
+ 0x25, 0x57, 0x25, 0xa9, 0xd7, 0x01, 0x50, 0x4b,
+ 0x01, 0x02, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x91, 0x68, 0x2e, 0x53, 0x85, 0x11,
+ 0x4a, 0x0d, 0x0b, 0x00, 0x00, 0x00, 0x0b, 0x00,
+ 0x00, 0x00, 0x09, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x5e, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x65, 0x73,
+ 0x74, 0x2e, 0x74, 0x78, 0x74, 0x0a, 0x00, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18,
+ 0x00, 0xa9, 0x80, 0x51, 0x01, 0x26, 0xa9, 0xd7,
+ 0x01, 0x31, 0xd1, 0x57, 0x01, 0x26, 0xa9, 0xd7,
+ 0x01, 0xdf, 0x48, 0x85, 0xf9, 0x25, 0xa9, 0xd7,
+ 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x04, 0x00, 0x31, 0x01, 0x00,
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+ }
+ r, err := NewReader(bytes.NewReader([]byte(data)), int64(len(data)))
+ if err != nil {
+ t.Fatalf("Error reading the archive: %v", err)
+ }
+ entryNames := []string{`/`, `//`, `\`, `/test.txt`}
+ var names []string
+ for _, f := range r.File {
+ names = append(names, f.Name)
+ if _, err := f.Open(); err != nil {
+ t.Errorf("Error opening %q: %v", f.Name, err)
+ }
+ if _, err := r.Open(f.Name); err == nil {
+ t.Errorf("Opening %q with fs.FS API succeeded", f.Name)
+ }
+ }
+ if !reflect.DeepEqual(names, entryNames) {
+ t.Errorf("Unexpected file entries: %q", names)
+ }
+ if _, err := r.Open(""); err == nil {
+ t.Errorf("Opening %q with fs.FS API succeeded", "")
+ }
+ if _, err := r.Open("test.txt"); err != nil {
+ t.Errorf("Error opening %q with fs.FS API: %v", "test.txt", err)
+ }
+ dirEntries, err := fs.ReadDir(r, ".")
+ if err != nil {
+ t.Fatalf("Error reading the root directory: %v", err)
+ }
+ if len(dirEntries) != 1 || dirEntries[0].Name() != "test.txt" {
+ t.Errorf("Unexpected directory entries")
+ for _, dirEntry := range dirEntries {
+ _, err := r.Open(dirEntry.Name())
+ t.Logf("%q (Open error: %v)", dirEntry.Name(), err)
+ }
+ t.FailNow()
+ }
+ info, err := dirEntries[0].Info()
+ if err != nil {
+ t.Fatalf("Error reading info entry: %v", err)
+ }
+ if name := info.Name(); name != "test.txt" {
+ t.Errorf("Inconsistent name in info entry: %v", name)
+ }
+}
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index a58df25494..063a7785f3 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -745,19 +745,14 @@ func (b *Writer) WriteString(s string) (int, error) {
}
// ReadFrom implements io.ReaderFrom. If the underlying writer
-// supports the ReadFrom method, and b has no buffered data yet,
-// this calls the underlying ReadFrom without buffering.
+// supports the ReadFrom method, this calls the underlying ReadFrom.
+// If there is buffered data and an underlying ReadFrom, this fills
+// the buffer and writes it before calling ReadFrom.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.err != nil {
return 0, b.err
}
- if b.Buffered() == 0 {
- if w, ok := b.wr.(io.ReaderFrom); ok {
- n, err = w.ReadFrom(r)
- b.err = err
- return n, err
- }
- }
+ readerFrom, readerFromOK := b.wr.(io.ReaderFrom)
var m int
for {
if b.Available() == 0 {
@@ -765,6 +760,12 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return n, err1
}
}
+ if readerFromOK && b.Buffered() == 0 {
+ nn, err := readerFrom.ReadFrom(r)
+ b.err = err
+ n += nn
+ return n, err
+ }
nr := 0
for nr < maxConsecutiveEmptyReads {
m, err = r.Read(b.buf[b.n:])
diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go
index 8e8a8a1778..4dddfa9085 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -1351,6 +1351,54 @@ func TestWriterReadFromErrNoProgress(t *testing.T) {
}
}
+type readFromWriter struct {
+ buf []byte
+ writeBytes int
+ readFromBytes int
+}
+
+func (w *readFromWriter) Write(p []byte) (int, error) {
+ w.buf = append(w.buf, p...)
+ w.writeBytes += len(p)
+ return len(p), nil
+}
+
+func (w *readFromWriter) ReadFrom(r io.Reader) (int64, error) {
+ b, err := io.ReadAll(r)
+ w.buf = append(w.buf, b...)
+ w.readFromBytes += len(b)
+ return int64(len(b)), err
+}
+
+// Test that calling (*Writer).ReadFrom with a partially-filled buffer
+// fills the buffer before switching over to ReadFrom.
+func TestWriterReadFromWithBufferedData(t *testing.T) {
+ const bufsize = 16
+
+ input := createTestInput(64)
+ rfw := &readFromWriter{}
+ w := NewWriterSize(rfw, bufsize)
+
+ const writeSize = 8
+ if n, err := w.Write(input[:writeSize]); n != writeSize || err != nil {
+ t.Errorf("w.Write(%v bytes) = %v, %v; want %v, nil", writeSize, n, err, writeSize)
+ }
+ n, err := w.ReadFrom(bytes.NewReader(input[writeSize:]))
+ if wantn := len(input[writeSize:]); int(n) != wantn || err != nil {
+ t.Errorf("io.Copy(w, %v bytes) = %v, %v; want %v, nil", wantn, n, err, wantn)
+ }
+ if err := w.Flush(); err != nil {
+ t.Errorf("w.Flush() = %v, want nil", err)
+ }
+
+ if got, want := rfw.writeBytes, bufsize; got != want {
+ t.Errorf("wrote %v bytes with Write, want %v", got, want)
+ }
+ if got, want := rfw.readFromBytes, len(input)-bufsize; got != want {
+ t.Errorf("wrote %v bytes with ReadFrom, want %v", got, want)
+ }
+}
+
func TestReadZero(t *testing.T) {
for _, size := range []int{100, 2} {
t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) {
@@ -1472,7 +1520,7 @@ func TestReaderDiscard(t *testing.T) {
wantBuffered: 0,
},
// Any error from filling shouldn't show up until we
- // get past the valid bytes. Here we return we return 5 valid bytes at the same time
+ // get past the valid bytes. Here we return 5 valid bytes at the same time
// as an error, but test that we don't see the error from Discard.
{
name: "fill error, discard less",
diff --git a/src/bytes/boundary_test.go b/src/bytes/boundary_test.go
index 5a47526593..f9855fcb05 100644
--- a/src/bytes/boundary_test.go
+++ b/src/bytes/boundary_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//
//go:build linux
-// +build linux
package bytes_test
@@ -66,7 +65,11 @@ func TestIndexByteNearPageBoundary(t *testing.T) {
func TestIndexNearPageBoundary(t *testing.T) {
t.Parallel()
- var q [64]byte
+ q := dangerousSlice(t)
+ if len(q) > 64 {
+ // Only worry about when we're near the end of a page.
+ q = q[len(q)-64:]
+ }
b := dangerousSlice(t)
if len(b) > 256 {
// Only worry about when we're near the end of a page.
@@ -82,4 +85,16 @@ func TestIndexNearPageBoundary(t *testing.T) {
}
q[j-1] = 0
}
+
+ // Test differing alignments and sizes of q which always end on a page boundary.
+ q[len(q)-1] = 1 // difference is only found on the last byte
+ for j := 0; j < len(q); j++ {
+ for i := range b {
+ idx := Index(b[i:], q[j:])
+ if idx != -1 {
+ t.Fatalf("Index(b[%d:], q[%d:])=%d, want -1\n", i, j, idx)
+ }
+ }
+ }
+ q[len(q)-1] = 0
}
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 926f65cc8f..9e6b68eaf4 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -21,7 +21,7 @@ func Equal(a, b []byte) bool {
}
// Compare returns an integer comparing two byte slices lexicographically.
-// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
+// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
return bytealg.Compare(a, b)
@@ -699,7 +699,7 @@ func ToValidUTF8(s, replacement []byte) []byte {
if c < utf8.RuneSelf {
i++
invalid = false
- b = append(b, byte(c))
+ b = append(b, c)
continue
}
_, wid := utf8.DecodeRune(s[i:])
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
index 81979de191..1b94a1b883 100644
--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
// The run program is invoked via the dist tool.
// To invoke manually: go tool dist test -run api --no-rebuild
diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index cffa9a26a4..4d374cb828 100644
--- a/src/cmd/asm/internal/arch/arch.go
+++ b/src/cmd/asm/internal/arch/arch.go
@@ -50,7 +50,7 @@ func nilRegisterNumber(name string, n int16) (int16, bool) {
// Set configures the architecture specified by GOARCH and returns its representation.
// It returns nil if GOARCH is not recognized.
-func Set(GOARCH string) *Arch {
+func Set(GOARCH string, shared bool) *Arch {
switch GOARCH {
case "386":
return archX86(&x86.Link386)
@@ -73,7 +73,7 @@ func Set(GOARCH string) *Arch {
case "ppc64le":
return archPPC64(&ppc64.Linkppc64le)
case "riscv64":
- return archRISCV64()
+ return archRISCV64(shared)
case "s390x":
return archS390x()
case "wasm":
@@ -378,6 +378,9 @@ func archPPC64(linkArch *obj.LinkArch) *Arch {
for i := ppc64.REG_MSR; i <= ppc64.REG_CR; i++ {
register[obj.Rconv(i)] = int16(i)
}
+ for i := ppc64.REG_CR0LT; i <= ppc64.REG_CR7SO; i++ {
+ register[obj.Rconv(i)] = int16(i)
+ }
register["CR"] = ppc64.REG_CR
register["XER"] = ppc64.REG_XER
register["LR"] = ppc64.REG_LR
@@ -538,12 +541,18 @@ func archMips64(linkArch *obj.LinkArch) *Arch {
}
}
-func archRISCV64() *Arch {
+func archRISCV64(shared bool) *Arch {
register := make(map[string]int16)
// Standard register names.
for i := riscv.REG_X0; i <= riscv.REG_X31; i++ {
- if i == riscv.REG_G {
+ // Disallow X3 in shared mode, as this will likely be used as the
+ // GP register, which could result in problems in non-Go code,
+ // including signal handlers.
+ if shared && i == riscv.REG_GP {
+ continue
+ }
+ if i == riscv.REG_TP || i == riscv.REG_G {
continue
}
name := fmt.Sprintf("X%d", i-riscv.REG_X0)
diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go
index 8ef02b1a0e..c1295a0c42 100644
--- a/src/cmd/asm/internal/asm/operand_test.go
+++ b/src/cmd/asm/internal/asm/operand_test.go
@@ -19,7 +19,7 @@ import (
func setArch(goarch string) (*arch.Arch, *obj.Link) {
buildcfg.GOOS = "linux" // obj can handle this OS for all architectures.
buildcfg.GOARCH = goarch
- architecture := arch.Set(goarch)
+ architecture := arch.Set(goarch, false)
if architecture == nil {
panic("asm: unrecognized architecture " + goarch)
}
diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s
index b9da48acdd..c140fd025a 100644
--- a/src/cmd/asm/internal/asm/testdata/ppc64.s
+++ b/src/cmd/asm/internal/asm/testdata/ppc64.s
@@ -342,14 +342,14 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
NOP F2
NOP $4
- CRAND CR1, CR2, CR3 // 4c620a02
- CRANDN CR1, CR2, CR3 // 4c620902
- CREQV CR1, CR2, CR3 // 4c620a42
- CRNAND CR1, CR2, CR3 // 4c6209c2
- CRNOR CR1, CR2, CR3 // 4c620842
- CROR CR1, CR2, CR3 // 4c620b82
- CRORN CR1, CR2, CR3 // 4c620b42
- CRXOR CR1, CR2, CR3 // 4c620982
+ CRAND CR0GT, CR0EQ, CR0SO // 4c620a02
+ CRANDN CR0GT, CR0EQ, CR0SO // 4c620902
+ CREQV CR0GT, CR0EQ, CR0SO // 4c620a42
+ CRNAND CR0GT, CR0EQ, CR0SO // 4c6209c2
+ CRNOR CR0GT, CR0EQ, CR0SO // 4c620842
+ CROR CR0GT, CR0EQ, CR0SO // 4c620b82
+ CRORN CR0GT, CR0EQ, CR0SO // 4c620b42
+ CRXOR CR0GT, CR0EQ, CR0SO // 4c620982
ISEL $1, R3, R4, R5 // 7ca3205e
ISEL $0, R3, R4, R5 // 7ca3201e
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index dd947c7b5b..607166e664 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -28,6 +28,10 @@ var (
CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime")
)
+var DebugFlags struct {
+ MayMoreStack string `help:"call named function before all stack growth checks"`
+}
+
var (
D MultiFlag
I MultiFlag
@@ -39,6 +43,7 @@ func init() {
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
flag.Var(&I, "I", "include directory; can be set multiple times")
flag.BoolVar(&DebugV, "v", false, "print debug output")
+ flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help")
objabi.AddVersionFlag() // -V
objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
}
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 043bc696e5..3683527f5b 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -29,19 +29,20 @@ func main() {
buildcfg.Check()
GOARCH := buildcfg.GOARCH
- architecture := arch.Set(GOARCH)
+ flags.Parse()
+
+ architecture := arch.Set(GOARCH, *flags.Shared || *flags.Dynlink)
if architecture == nil {
log.Fatalf("unrecognized architecture %s", GOARCH)
}
- flags.Parse()
-
ctxt := obj.Linknew(architecture.LinkArch)
ctxt.Debugasm = flags.PrintOut
ctxt.Debugvlog = flags.DebugV
ctxt.Flag_dynlink = *flags.Dynlink
ctxt.Flag_linkshared = *flags.Linkshared
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
+ ctxt.Flag_maymorestack = flags.DebugFlags.MayMoreStack
ctxt.IsAsm = true
ctxt.Pkgpath = *flags.Importpath
switch *flags.Spectre {
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index c78197896c..997a830994 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -29,7 +29,7 @@ import (
"unicode"
"unicode/utf8"
- "cmd/internal/str"
+ "cmd/internal/quoted"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -1568,7 +1568,7 @@ func checkGCCBaseCmd() ([]string, error) {
if value == "" {
value = defaultCC(goos, goarch)
}
- args, err := str.SplitQuotedFields(value)
+ args, err := quoted.Split(value)
if err != nil {
return nil, err
}
diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go
index b68ef274f3..ef7fa86749 100644
--- a/src/cmd/compile/doc.go
+++ b/src/cmd/compile/doc.go
@@ -44,6 +44,8 @@ Flags:
Print compiler version and exit.
-asmhdr file
Write assembly header to file.
+ -asan
+ Insert calls to C/C++ address sanitizer.
-buildid id
Record id as the build id in the export metadata.
-blockprofile file
diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go
index 4da4e9ca3a..74c8707b29 100644
--- a/src/cmd/compile/internal/abi/abiutils.go
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -780,11 +780,11 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is
}
// ComputePadding returns a list of "post element" padding values in
-// the case where we have a structure being passed in registers. Give
-// a param assignment corresponding to a struct, it returns a list of
-// contaning padding values for each field, e.g. the Kth element in
+// the case where we have a structure being passed in registers. Given
+// a param assignment corresponding to a struct, it returns a list
+// containing padding values for each field, e.g. the Kth element in
// the list is the amount of padding between field K and the following
-// field. For things that are not struct (or structs without padding)
+// field. For things that are not structs (or structs without padding)
// it returns a list of zeros. Example:
//
// type small struct {
@@ -796,8 +796,8 @@ func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, is
//
// For this struct we would return a list [0, 1, 0, 0], meaning that
// we have one byte of padding after the second field, and no bytes of
-// padding after any of the other fields. Input parameter "storage"
-// is with enough capacity to accommodate padding elements for
+// padding after any of the other fields. Input parameter "storage" is
+// a slice with enough capacity to accommodate padding elements for
// the architected register set in question.
func (pa *ABIParamAssignment) ComputePadding(storage []uint64) []uint64 {
nr := len(pa.Registers)
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 0e74574422..b0e5c34030 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -772,7 +772,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Val = math.Float64frombits(uint64(v.AuxInt))
p.To.Type = obj.TYPE_REG
p.To.Reg = x
- case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload, ssa.OpAMD64MOVOload:
+ case ssa.OpAMD64MOVQload, ssa.OpAMD64MOVLload, ssa.OpAMD64MOVWload, ssa.OpAMD64MOVBload, ssa.OpAMD64MOVOload,
+ ssa.OpAMD64MOVSSload, ssa.OpAMD64MOVSDload, ssa.OpAMD64MOVBQSXload, ssa.OpAMD64MOVWQSXload, ssa.OpAMD64MOVLQSXload,
+ ssa.OpAMD64MOVBEQload, ssa.OpAMD64MOVBELload:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
@@ -788,7 +790,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVQstore, ssa.OpAMD64MOVSSstore, ssa.OpAMD64MOVSDstore, ssa.OpAMD64MOVLstore, ssa.OpAMD64MOVWstore, ssa.OpAMD64MOVBstore, ssa.OpAMD64MOVOstore,
ssa.OpAMD64ADDQmodify, ssa.OpAMD64SUBQmodify, ssa.OpAMD64ANDQmodify, ssa.OpAMD64ORQmodify, ssa.OpAMD64XORQmodify,
- ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify:
+ ssa.OpAMD64ADDLmodify, ssa.OpAMD64SUBLmodify, ssa.OpAMD64ANDLmodify, ssa.OpAMD64ORLmodify, ssa.OpAMD64XORLmodify,
+ ssa.OpAMD64MOVBEQstore, ssa.OpAMD64MOVBELstore:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go
index de677f3a69..ee1a8ca3aa 100644
--- a/src/cmd/compile/internal/amd64/versions_test.go
+++ b/src/cmd/compile/internal/amd64/versions_test.go
@@ -53,7 +53,9 @@ func TestGoAMD64v1(t *testing.T) {
opcodes := map[string]bool{}
var features []string
for feature, opcodeList := range featureToOpcodes {
- features = append(features, fmt.Sprintf("cpu.%s=off", feature))
+ if runtimeFeatures[feature] {
+ features = append(features, fmt.Sprintf("cpu.%s=off", feature))
+ }
for _, op := range opcodeList {
opcodes[op] = true
}
@@ -204,14 +206,28 @@ func clobber(t *testing.T, src string, dst *os.File, opcodes map[string]bool) {
f.Close()
}
+func setOf(keys ...string) map[string]bool {
+ m := make(map[string]bool, len(keys))
+ for _, key := range keys {
+ m[key] = true
+ }
+ return m
+}
+
+var runtimeFeatures = setOf(
+ "adx", "aes", "avx", "avx2", "bmi1", "bmi2", "erms", "fma",
+ "pclmulqdq", "popcnt", "rdtscp", "sse3", "sse41", "sse42", "ssse3",
+)
+
var featureToOpcodes = map[string][]string{
// Note: we include *q, *l, and plain opcodes here.
// go tool objdump doesn't include a [QL] on popcnt instructions, until CL 351889
// native objdump doesn't include [QL] on linux.
- "popcnt": []string{"popcntq", "popcntl", "popcnt"},
- "bmi1": []string{"andnq", "andnl", "andn", "blsiq", "blsil", "blsi", "blsmskq", "blsmskl", "blsmsk", "blsrq", "blsrl", "blsr", "tzcntq", "tzcntl", "tzcnt"},
- "sse41": []string{"roundsd"},
- "fma": []string{"vfmadd231sd"},
+ "popcnt": {"popcntq", "popcntl", "popcnt"},
+ "bmi1": {"andnq", "andnl", "andn", "blsiq", "blsil", "blsi", "blsmskq", "blsmskl", "blsmsk", "blsrq", "blsrl", "blsr", "tzcntq", "tzcntl", "tzcnt"},
+ "sse41": {"roundsd"},
+ "fma": {"vfmadd231sd"},
+ "movbe": {"movbeqq", "movbeq", "movbell", "movbel", "movbe"},
}
// Test to use POPCNT instruction, if available
@@ -364,5 +380,4 @@ func TestFMA(t *testing.T) {
t.Errorf("FMA(%f,%f,%f) = %f, want %f", tt.x, tt.y, tt.z, got, tt.want)
}
}
-
}
diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go
index 4c2516f60e..be6d49fac7 100644
--- a/src/cmd/compile/internal/base/base.go
+++ b/src/cmd/compile/internal/base/base.go
@@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{
"runtime",
"runtime/race",
"runtime/msan",
+ "runtime/asan",
"internal/cpu",
}
diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go
index e2245e1c26..b105e46e35 100644
--- a/src/cmd/compile/internal/base/debug.go
+++ b/src/cmd/compile/internal/base/debug.go
@@ -6,15 +6,6 @@
package base
-import (
- "fmt"
- "log"
- "os"
- "reflect"
- "strconv"
- "strings"
-)
-
// Debug holds the parsed debugging configuration values.
var Debug DebugFlags
@@ -26,7 +17,7 @@ var Debug DebugFlags
// Each setting is name=value; for ints, name is short for name=1.
type DebugFlags struct {
Append int `help:"print information about append compilation"`
- Checkptr int `help:"instrument unsafe pointer conversions"`
+ Checkptr int `help:"instrument unsafe pointer conversions\n0: instrumentation disabled\n1: conversions involving unsafe.Pointer are instrumented\n2: conversions to unsafe.Pointer force heap allocation"`
Closure int `help:"print information about closure compilation"`
DclStack int `help:"run internal dclstack check"`
Defer int `help:"print information about defer compilation"`
@@ -40,7 +31,7 @@ type DebugFlags struct {
LocationLists int `help:"print information about DWARF location list creation"`
Nil int `help:"print information about nil checks"`
NoOpenDefer int `help:"disable open-coded defers"`
- PCTab string `help:"print named pc-value table"`
+ PCTab string `help:"print named pc-value table\nOne of: pctospadj, pctofile, pctoline, pctoinline, pctopcdata"`
Panic int `help:"show all compiler panics"`
Slice int `help:"print information about slice compilation"`
SoftFloat int `help:"force compiler to emit soft-float code"`
@@ -51,142 +42,12 @@ type DebugFlags struct {
UnifiedQuirks int `help:"enable unified IR construction's quirks mode"`
WB int `help:"print information about write barriers"`
ABIWrap int `help:"print information about ABI wrapper generation"`
+ MayMoreStack string `help:"call named function before all stack growth checks"`
- any bool // set when any of the values have been set
-}
-
-// Any reports whether any of the debug flags have been set.
-func (d *DebugFlags) Any() bool { return d.any }
-
-type debugField struct {
- name string
- help string
- val interface{} // *int or *string
-}
-
-var debugTab []debugField
-
-func init() {
- v := reflect.ValueOf(&Debug).Elem()
- t := v.Type()
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- if f.Name == "any" {
- continue
- }
- name := strings.ToLower(f.Name)
- help := f.Tag.Get("help")
- if help == "" {
- panic(fmt.Sprintf("base.Debug.%s is missing help text", f.Name))
- }
- ptr := v.Field(i).Addr().Interface()
- switch ptr.(type) {
- default:
- panic(fmt.Sprintf("base.Debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
- case *int, *string:
- // ok
- }
- debugTab = append(debugTab, debugField{name, help, ptr})
- }
+ Any bool // set when any of the debug flags have been set
}
// DebugSSA is called to set a -d ssa/... option.
// If nil, those options are reported as invalid options.
// If DebugSSA returns a non-empty string, that text is reported as a compiler error.
var DebugSSA func(phase, flag string, val int, valString string) string
-
-// parseDebug parses the -d debug string argument.
-func parseDebug(debugstr string) {
- // parse -d argument
- if debugstr == "" {
- return
- }
- Debug.any = true
-Split:
- for _, name := range strings.Split(debugstr, ",") {
- if name == "" {
- continue
- }
- // display help about the -d option itself and quit
- if name == "help" {
- fmt.Print(debugHelpHeader)
- maxLen := len("ssa/help")
- for _, t := range debugTab {
- if len(t.name) > maxLen {
- maxLen = len(t.name)
- }
- }
- for _, t := range debugTab {
- fmt.Printf("\t%-*s\t%s\n", maxLen, t.name, t.help)
- }
- // ssa options have their own help
- fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
- fmt.Print(debugHelpFooter)
- os.Exit(0)
- }
- val, valstring, haveInt := 1, "", true
- if i := strings.IndexAny(name, "=:"); i >= 0 {
- var err error
- name, valstring = name[:i], name[i+1:]
- val, err = strconv.Atoi(valstring)
- if err != nil {
- val, haveInt = 1, false
- }
- }
- for _, t := range debugTab {
- if t.name != name {
- continue
- }
- switch vp := t.val.(type) {
- case nil:
- // Ignore
- case *string:
- *vp = valstring
- case *int:
- if !haveInt {
- log.Fatalf("invalid debug value %v", name)
- }
- *vp = val
- default:
- panic("bad debugtab type")
- }
- continue Split
- }
- // special case for ssa for now
- if DebugSSA != nil && strings.HasPrefix(name, "ssa/") {
- // expect form ssa/phase/flag
- // e.g. -d=ssa/generic_cse/time
- // _ in phase name also matches space
- phase := name[4:]
- flag := "debug" // default flag is debug
- if i := strings.Index(phase, "/"); i >= 0 {
- flag = phase[i+1:]
- phase = phase[:i]
- }
- err := DebugSSA(phase, flag, val, valstring)
- if err != "" {
- log.Fatalf(err)
- }
- continue Split
- }
- log.Fatalf("unknown debug key -d %s\n", name)
- }
-}
-
-const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
-
- is one of:
-
-`
-
-const debugHelpFooter = `
- is key-specific.
-
-Key "checkptr" supports values:
- "0": instrumentation disabled
- "1": conversions involving unsafe.Pointer are instrumented
- "2": conversions to unsafe.Pointer force heap allocation
-
-Key "pctab" supports values:
- "pctospadj", "pctofile", "pctoline", "pctoinline", "pctopcdata"
-`
diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go
index 241f5da185..d78f93b343 100644
--- a/src/cmd/compile/internal/base/flag.go
+++ b/src/cmd/compile/internal/base/flag.go
@@ -64,19 +64,19 @@ type CmdFlags struct {
// V is added by objabi.AddVersionFlag
W CountFlag "help:\"debug parse tree after type checking\""
- LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
- LowerD func(string) "help:\"enable debugging settings; try -d help\""
- LowerE CountFlag "help:\"no limit on number of errors reported\""
- LowerH CountFlag "help:\"halt on error\""
- LowerJ CountFlag "help:\"debug runtime-initialized variables\""
- LowerL CountFlag "help:\"disable inlining\""
- LowerM CountFlag "help:\"print optimization decisions\""
- LowerO string "help:\"write output to `file`\""
- LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
- LowerR CountFlag "help:\"debug generated wrappers\""
- LowerT bool "help:\"enable tracing for debugging the compiler\""
- LowerW CountFlag "help:\"debug type checking\""
- LowerV *bool "help:\"increase debug verbosity\""
+ LowerC int "help:\"concurrency during compilation (1 means no concurrency)\""
+ LowerD flag.Value "help:\"enable debugging settings; try -d help\""
+ LowerE CountFlag "help:\"no limit on number of errors reported\""
+ LowerH CountFlag "help:\"halt on error\""
+ LowerJ CountFlag "help:\"debug runtime-initialized variables\""
+ LowerL CountFlag "help:\"disable inlining\""
+ LowerM CountFlag "help:\"print optimization decisions\""
+ LowerO string "help:\"write output to `file`\""
+ LowerP *string "help:\"set expected package import `path`\"" // &Ctxt.Pkgpath, set below
+ LowerR CountFlag "help:\"debug generated wrappers\""
+ LowerT bool "help:\"enable tracing for debugging the compiler\""
+ LowerW CountFlag "help:\"debug type checking\""
+ LowerV *bool "help:\"increase debug verbosity\""
// Special characters
Percent int "flag:\"%\" help:\"debug non-static initializers\""
@@ -84,6 +84,7 @@ type CmdFlags struct {
// Longer names
AsmHdr string "help:\"write assembly header to `file`\""
+ ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
Bench string "help:\"append benchmark times to `file`\""
BlockProfile string "help:\"write block profile to `file`\""
BuildID string "help:\"record `id` as the build id in the export metadata\""
@@ -108,7 +109,7 @@ type CmdFlags struct {
Live CountFlag "help:\"debug liveness analysis\""
MSan bool "help:\"build code compatible with C/C++ memory sanitizer\""
MemProfile string "help:\"write memory profile to `file`\""
- MemProfileRate int64 "help:\"set runtime.MemProfileRate to `rate`\""
+ MemProfileRate int "help:\"set runtime.MemProfileRate to `rate`\""
MutexProfile string "help:\"write mutex profile to `file`\""
NoLocalImports bool "help:\"reject local (relative) imports\""
Pack bool "help:\"write to file.a instead of file.o\""
@@ -144,7 +145,7 @@ func ParseFlags() {
Flag.I = addImportDir
Flag.LowerC = 1
- Flag.LowerD = parseDebug
+ Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
Flag.LowerP = &Ctxt.Pkgpath
Flag.LowerV = &Ctxt.Debugvlog
@@ -177,6 +178,9 @@ func ParseFlags() {
if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
}
+ if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
+ log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
+ }
if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
}
@@ -188,6 +192,7 @@ func ParseFlags() {
Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
Ctxt.Flag_optimize = Flag.N == 0
Ctxt.Debugasm = int(Flag.S)
+ Ctxt.Flag_maymorestack = Debug.MayMoreStack
if flag.NArg() < 1 {
usage()
@@ -217,12 +222,16 @@ func ParseFlags() {
}
Flag.LowerO = p + suffix
}
-
- if Flag.Race && Flag.MSan {
+ switch {
+ case Flag.Race && Flag.MSan:
log.Fatal("cannot use both -race and -msan")
+ case Flag.Race && Flag.ASan:
+ log.Fatal("cannot use both -race and -asan")
+ case Flag.MSan && Flag.ASan:
+ log.Fatal("cannot use both -msan and -asan")
}
- if Flag.Race || Flag.MSan {
- // -race and -msan imply -d=checkptr for now.
+ if Flag.Race || Flag.MSan || Flag.ASan {
+ // -race, -msan and -asan imply -d=checkptr for now.
if Debug.Checkptr == -1 { // if not set explicitly
Debug.Checkptr = 1
}
@@ -322,6 +331,12 @@ func registerFlags() {
case funcType:
f := v.Field(i).Interface().(func(string))
objabi.Flagfn1(name, help, f)
+ default:
+ if val, ok := v.Field(i).Interface().(flag.Value); ok {
+ flag.Var(val, name, help)
+ } else {
+ panic(fmt.Sprintf("base.Flag.%s has unexpected type %s", f.Name, f.Type))
+ }
}
}
}
@@ -349,7 +364,7 @@ func concurrentBackendAllowed() bool {
// while writing the object file, and that is non-concurrent.
// Adding Debug_vlog, however, causes Debug.S to also print
// while flushing the plist, which happens concurrently.
- if Ctxt.Debugvlog || Debug.Any() || Flag.Live > 0 {
+ if Ctxt.Debugvlog || Debug.Any || Flag.Live > 0 {
return false
}
// TODO: Test and delete this condition.
diff --git a/src/cmd/compile/internal/bitvec/bv.go b/src/cmd/compile/internal/bitvec/bv.go
index bcac1fe351..ad7ed0a196 100644
--- a/src/cmd/compile/internal/bitvec/bv.go
+++ b/src/cmd/compile/internal/bitvec/bv.go
@@ -128,10 +128,21 @@ func (bv BitVec) IsEmpty() bool {
return true
}
+func (bv BitVec) Count() int {
+ n := 0
+ for _, x := range bv.B {
+ n += bits.OnesCount32(x)
+ }
+ return n
+}
+
func (bv BitVec) Not() {
for i, x := range bv.B {
bv.B[i] = ^x
}
+ if bv.N%wordBits != 0 {
+ bv.B[len(bv.B)-1] &= 1< 0 {
+ noder.BuildInstantiations(false)
+ }
}
noder.MakeWrappers(typecheck.Target) // must happen after inlining
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 432c003b9a..dcb54047f1 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -249,8 +249,7 @@ func addGCLocals() {
}
}
if x := fn.StackObjects; x != nil {
- attr := int16(obj.RODATA)
- objw.Global(x, int32(len(x.P)), attr)
+ objw.Global(x, int32(len(x.P)), obj.RODATA)
x.Set(obj.AttrStatic, true)
}
if x := fn.OpenCodedDeferInfo; x != nil {
@@ -260,6 +259,10 @@ func addGCLocals() {
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
x.Set(obj.AttrStatic, true)
}
+ if x := fn.ArgLiveInfo; x != nil {
+ objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
+ x.Set(obj.AttrStatic, true)
+ }
}
}
diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
index 4baddbc029..56fd137de2 100644
--- a/src/cmd/compile/internal/gc/util.go
+++ b/src/cmd/compile/internal/gc/util.go
@@ -12,10 +12,7 @@ import (
"cmd/compile/internal/base"
)
-var (
- memprofilerate int64
- traceHandler func(string)
-)
+var traceHandler func(string)
func startProfile() {
if base.Flag.CPUProfile != "" {
@@ -29,8 +26,8 @@ func startProfile() {
base.AtExit(pprof.StopCPUProfile)
}
if base.Flag.MemProfile != "" {
- if memprofilerate != 0 {
- runtime.MemProfileRate = int(memprofilerate)
+ if base.Flag.MemProfileRate != 0 {
+ runtime.MemProfileRate = base.Flag.MemProfileRate
}
f, err := os.Create(base.Flag.MemProfile)
if err != nil {
diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go
index cbc78539fd..d04ef5c34d 100644
--- a/src/cmd/compile/internal/importer/iimport.go
+++ b/src/cmd/compile/internal/importer/iimport.go
@@ -43,12 +43,12 @@ func (r *intReader) uint64() uint64 {
// Keep this in sync with constants in iexport.go.
const (
- iexportVersionGo1_11 = 0
- iexportVersionPosCol = 1
- // TODO: before release, change this back to 2.
- iexportVersionGenerics = iexportVersionPosCol
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGenerics = 1 // probably change to 2 before release
+ iexportVersionGo1_18 = 2
- iexportVersionCurrent = iexportVersionGenerics
+ iexportVersionCurrent = 2
)
type ident struct {
@@ -99,13 +99,9 @@ func ImportData(imports map[string]*types2.Package, data, path string) (pkg *typ
version = int64(r.uint64())
switch version {
- case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
- if version > iexportVersionGenerics {
- errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
- } else {
- errorf("unknown iexport format version %d", version)
- }
+ errorf("unknown iexport format version %d", version)
}
sLen := int64(r.uint64())
@@ -374,7 +370,19 @@ func (r *importReader) obj(name string) {
id := ident{r.currPkg.Name(), name}
r.p.tparamIndex[id] = t
- t.SetConstraint(r.typ())
+ var implicit bool
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ implicit = r.bool()
+ }
+ constraint := r.typ()
+ if implicit {
+ iface, _ := constraint.(*types2.Interface)
+ if iface == nil {
+ errorf("non-interface constraint marked implicit")
+ }
+ iface.MarkImplicit()
+ }
+ t.SetConstraint(constraint)
case 'V':
typ := r.typ()
@@ -392,6 +400,10 @@ func (r *importReader) declare(obj types2.Object) {
func (r *importReader) value() (typ types2.Type, val constant.Value) {
typ = r.typ()
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ // TODO: add support for using the kind
+ _ = constant.Kind(r.int64())
+ }
switch b := typ.Underlying().(*types2.Basic); b.Info() & types2.IsConstType {
case types2.IsBoolean:
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index 51270a3315..47b895f7e3 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -309,7 +309,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
break
}
- if fn := inlCallee(n.X); fn != nil && fn.Inl != nil {
+ if fn := inlCallee(n.X); fn != nil && typecheck.HaveInlineBody(fn) {
v.budget -= fn.Inl.Cost
break
}
@@ -358,8 +358,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
return true
}
- case ir.ORANGE,
- ir.OSELECT,
+ case ir.OSELECT,
ir.OGO,
ir.ODEFER,
ir.ODCLTYPE, // can't print yet
@@ -390,27 +389,6 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
// These nodes don't produce code; omit from inlining budget.
return false
- case ir.OFOR, ir.OFORUNTIL:
- n := n.(*ir.ForStmt)
- if n.Label != nil {
- v.reason = "labeled control"
- return true
- }
- case ir.OSWITCH:
- n := n.(*ir.SwitchStmt)
- if n.Label != nil {
- v.reason = "labeled control"
- return true
- }
- // case ir.ORANGE, ir.OSELECT in "unhandled" above
-
- case ir.OBREAK, ir.OCONTINUE:
- n := n.(*ir.BranchStmt)
- if n.Label != nil {
- // Should have short-circuited due to labeled control error above.
- base.Fatalf("unexpected labeled break/continue: %v", n)
- }
-
case ir.OIF:
n := n.(*ir.IfStmt)
if ir.IsConst(n.Cond, constant.Bool) {
@@ -607,7 +585,7 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
if ir.IsIntrinsicCall(call) {
break
}
- if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
+ if fn := inlCallee(call.X); fn != nil && typecheck.HaveInlineBody(fn) {
n = mkinlcall(call, fn, maxCost, inlMap, edit)
}
}
@@ -707,6 +685,27 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
return n
}
+ // Don't inline a function fn that has no shape parameters, but is passed at
+ // least one shape arg. This means we must be inlining a non-generic function
+ // fn that was passed into a generic function, and can be called with a shape
+ // arg because it matches an appropriate type parameters. But fn may include
+ // an interface conversion (that may be applied to a shape arg) that was not
+ // apparent when we first created the instantiation of the generic function.
+ // We can't handle this if we actually do the inlining, since we want to know
+ // all interface conversions immediately after stenciling. So, we avoid
+ // inlining in this case. See #49309.
+ if !fn.Type().HasShape() {
+ for _, arg := range n.Args {
+ if arg.Type().HasShape() {
+ if logopt.Enabled() {
+ logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
+ fmt.Sprintf("inlining non-shape function %v with shape args", ir.FuncName(fn)))
+ }
+ return n
+ }
+ }
+ }
+
if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
// Runtime package must not be instrumented.
// Instrument skips runtime package. However, some runtime code can be
@@ -1244,7 +1243,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
// Don't do special substitutions if inside a closure
break
}
- // Since we don't handle bodies with closures,
+ // Because of the above test for subst.newclofn,
// this return is guaranteed to belong to the current inlined function.
n := n.(*ir.ReturnStmt)
init := subst.list(n.Init())
@@ -1272,7 +1271,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
typecheck.Stmts(init)
return ir.NewBlockStmt(base.Pos, init)
- case ir.OGOTO:
+ case ir.OGOTO, ir.OBREAK, ir.OCONTINUE:
if subst.newclofn != nil {
// Don't do special substitutions if inside a closure
break
@@ -1280,9 +1279,8 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
n := n.(*ir.BranchStmt)
m := ir.Copy(n).(*ir.BranchStmt)
m.SetPos(subst.updatedPos(m.Pos()))
- *m.PtrInit() = nil
- p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
- m.Label = typecheck.Lookup(p)
+ m.SetInit(nil)
+ m.Label = translateLabel(n.Label)
return m
case ir.OLABEL:
@@ -1293,9 +1291,8 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
n := n.(*ir.LabelStmt)
m := ir.Copy(n).(*ir.LabelStmt)
m.SetPos(subst.updatedPos(m.Pos()))
- *m.PtrInit() = nil
- p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
- m.Label = typecheck.Lookup(p)
+ m.SetInit(nil)
+ m.Label = translateLabel(n.Label)
return m
case ir.OCLOSURE:
@@ -1307,6 +1304,27 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
m.SetPos(subst.updatedPos(m.Pos()))
ir.EditChildren(m, subst.edit)
+ if subst.newclofn == nil {
+ // Translate any label on FOR, RANGE loops or SWITCH
+ switch m.Op() {
+ case ir.OFOR:
+ m := m.(*ir.ForStmt)
+ m.Label = translateLabel(m.Label)
+ return m
+
+ case ir.ORANGE:
+ m := m.(*ir.RangeStmt)
+ m.Label = translateLabel(m.Label)
+ return m
+
+ case ir.OSWITCH:
+ m := m.(*ir.SwitchStmt)
+ m.Label = translateLabel(m.Label)
+ return m
+ }
+
+ }
+
switch m := m.(type) {
case *ir.AssignStmt:
if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
@@ -1323,6 +1341,16 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
return m
}
+// translateLabel makes a label from an inlined function (if non-nil) be unique by
+// adding "·inlgen".
+func translateLabel(l *types.Sym) *types.Sym {
+ if l == nil {
+ return nil
+ }
+ p := fmt.Sprintf("%s·%d", l.Name, inlgen)
+ return typecheck.Lookup(p)
+}
+
func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
if subst.noPosUpdate {
return xpos
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index 18d0b023ad..41c96079f7 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -201,7 +201,7 @@ const (
funcNilCheckDisabled // disable nil checks when compiling this function
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
funcExportInline // include inline body in export data
- funcInstrumentBody // add race/msan instrumentation during SSA construction
+ funcInstrumentBody // add race/msan/asan instrumentation during SSA construction
funcOpenCodedDeferDisallowed // can't do open-coded defers
funcClosureCalled // closure is only immediately called; used by escape analysis
)
diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go
index dcfff7deba..1d4110c73c 100644
--- a/src/cmd/compile/internal/ir/name.go
+++ b/src/cmd/compile/internal/ir/name.go
@@ -146,7 +146,10 @@ func (n *Name) editChildren(edit func(Node) Node) {}
// That is, given "type T Defn", it returns Defn.
// It is used by package types.
func (n *Name) TypeDefn() *types.Type {
- return n.Ntype.Type()
+ if n.Ntype != nil {
+ return n.Ntype.Type()
+ }
+ return n.Type()
}
// RecordFrameOffset records the frame offset for the name.
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 1435e4313e..b204a1d544 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -15,6 +15,8 @@ var Syms struct {
AssertE2I2 *obj.LSym
AssertI2I *obj.LSym
AssertI2I2 *obj.LSym
+ Asanread *obj.LSym
+ Asanwrite *obj.LSym
CheckPtrAlignment *obj.LSym
Deferproc *obj.LSym
DeferprocStack *obj.LSym
diff --git a/src/cmd/compile/internal/liveness/arg.go b/src/cmd/compile/internal/liveness/arg.go
new file mode 100644
index 0000000000..2ca5d095aa
--- /dev/null
+++ b/src/cmd/compile/internal/liveness/arg.go
@@ -0,0 +1,339 @@
+// Copyright 2021 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 liveness
+
+import (
+ "fmt"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/bitvec"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/ssa"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+)
+
+// Argument liveness tracking.
+//
+// For arguments passed in registers, this file tracks if their spill slots
+// are live for runtime traceback. An argument spill slot is live at a PC
+// if we know that an actual value has stored into it at or before this point.
+//
+// Stack args are always live and not tracked in this code. Stack args are
+// laid out before register spill slots, so we emit the smallest offset that
+// needs tracking. Slots before that offset are always live. That offset is
+// usually the offset of the first spill slot. But if the first spill slot is
+// always live (e.g. if it is address-taken), it will be the offset of a later
+// one.
+//
+// The liveness information is emitted as a FUNCDATA and a PCDATA.
+//
+// FUNCDATA format:
+// - start (smallest) offset that needs tracking (1 byte)
+// - a list of bitmaps.
+// In a bitmap bit i is set if the i-th spill slot is live.
+//
+// At a PC where the liveness info changes, a PCDATA indicates the
+// byte offset of the liveness map in the FUNCDATA. PCDATA -1 is a
+// special case indicating all slots are live (for binary size
+// saving).
+
+const allLiveIdx = -1
+
+// name and offset
+type nameOff struct {
+ n *ir.Name
+ off int64
+}
+
+func (a nameOff) FrameOffset() int64 { return a.n.FrameOffset() + a.off }
+func (a nameOff) String() string { return fmt.Sprintf("%v+%d", a.n, a.off) }
+
+type blockArgEffects struct {
+ livein bitvec.BitVec // variables live at block entry
+ liveout bitvec.BitVec // variables live at block exit
+}
+
+type argLiveness struct {
+ fn *ir.Func
+ f *ssa.Func
+ args []nameOff // name and offset of spill slots
+ idx map[nameOff]int32 // index in args
+
+ be []blockArgEffects // indexed by block ID
+
+ bvset bvecSet // Set of liveness bitmaps, used for uniquifying.
+
+ // Liveness map indices at each Value (where it changes) and Block entry.
+ // During the computation the indices are temporarily index to bvset.
+ // At the end they will be index (offset) to the output funcdata (changed
+ // in (*argLiveness).emit).
+ blockIdx map[ssa.ID]int
+ valueIdx map[ssa.ID]int
+}
+
+// ArgLiveness computes the liveness information of register argument spill slots.
+// An argument's spill slot is "live" if we know it contains a meaningful value,
+// that is, we have stored the register value to it.
+// Returns the liveness map indices at each Block entry and at each Value (where
+// it changes).
+func ArgLiveness(fn *ir.Func, f *ssa.Func, pp *objw.Progs) (blockIdx, valueIdx map[ssa.ID]int) {
+ if f.OwnAux.ABIInfo().InRegistersUsed() == 0 || base.Flag.N != 0 {
+ // No register args. Nothing to emit.
+ // Or if -N is used we spill everything upfront so it is always live.
+ return nil, nil
+ }
+
+ lv := &argLiveness{
+ fn: fn,
+ f: f,
+ idx: make(map[nameOff]int32),
+ be: make([]blockArgEffects, f.NumBlocks()),
+ blockIdx: make(map[ssa.ID]int),
+ valueIdx: make(map[ssa.ID]int),
+ }
+ // Gather all register arg spill slots.
+ for _, a := range f.OwnAux.ABIInfo().InParams() {
+ n, ok := a.Name.(*ir.Name)
+ if !ok || len(a.Registers) == 0 {
+ continue
+ }
+ _, offs := a.RegisterTypesAndOffsets()
+ for _, off := range offs {
+ if n.FrameOffset()+off > 0xff {
+ // We only print a limited number of args, with stack
+ // offsets no larger than 255.
+ continue
+ }
+ lv.args = append(lv.args, nameOff{n, off})
+ }
+ }
+ if len(lv.args) > 10 {
+ lv.args = lv.args[:10] // We print no more than 10 args.
+ }
+
+ // We spill address-taken or non-SSA-able value upfront, so they are always live.
+ alwaysLive := func(n *ir.Name) bool { return n.Addrtaken() || !f.Frontend().CanSSA(n.Type()) }
+
+ // We'll emit the smallest offset for the slots that need liveness info.
+ // No need to include a slot with a lower offset if it is always live.
+ for len(lv.args) > 0 && alwaysLive(lv.args[0].n) {
+ lv.args = lv.args[1:]
+ }
+ if len(lv.args) == 0 {
+ return // everything is always live
+ }
+
+ for i, a := range lv.args {
+ lv.idx[a] = int32(i)
+ }
+
+ nargs := int32(len(lv.args))
+ bulk := bitvec.NewBulk(nargs, int32(len(f.Blocks)*2))
+ for _, b := range f.Blocks {
+ be := &lv.be[b.ID]
+ be.livein = bulk.Next()
+ be.liveout = bulk.Next()
+
+ // initialize to all 1s, so we can AND them
+ be.livein.Not()
+ be.liveout.Not()
+ }
+
+ entrybe := &lv.be[f.Entry.ID]
+ entrybe.livein.Clear()
+ for i, a := range lv.args {
+ if alwaysLive(a.n) {
+ entrybe.livein.Set(int32(i))
+ }
+ }
+
+ // Visit blocks in reverse-postorder, compute block effects.
+ po := f.Postorder()
+ for i := len(po) - 1; i >= 0; i-- {
+ b := po[i]
+ be := &lv.be[b.ID]
+
+ // A slot is live at block entry if it is live in all predecessors.
+ for _, pred := range b.Preds {
+ pb := pred.Block()
+ be.livein.And(be.livein, lv.be[pb.ID].liveout)
+ }
+
+ be.liveout.Copy(be.livein)
+ for _, v := range b.Values {
+ lv.valueEffect(v, be.liveout)
+ }
+ }
+
+ // Coalesce identical live vectors. Compute liveness indices at each PC
+ // where it changes.
+ live := bitvec.New(nargs)
+ addToSet := func(bv bitvec.BitVec) (int, bool) {
+ if bv.Count() == int(nargs) { // special case for all live
+ return allLiveIdx, false
+ }
+ return lv.bvset.add(bv)
+ }
+ for _, b := range lv.f.Blocks {
+ be := &lv.be[b.ID]
+ lv.blockIdx[b.ID], _ = addToSet(be.livein)
+
+ live.Copy(be.livein)
+ var lastv *ssa.Value
+ for i, v := range b.Values {
+ if lv.valueEffect(v, live) {
+ // Record that liveness changes but not emit a map now.
+ // For a sequence of StoreRegs we only need to emit one
+ // at last.
+ lastv = v
+ }
+ if lastv != nil && (mayFault(v) || i == len(b.Values)-1) {
+ // Emit the liveness map if it may fault or at the end of
+ // the block. We may need a traceback if the instruction
+ // may cause a panic.
+ var added bool
+ lv.valueIdx[lastv.ID], added = addToSet(live)
+ if added {
+ // live is added to bvset and we cannot modify it now.
+ // Make a copy.
+ t := live
+ live = bitvec.New(nargs)
+ live.Copy(t)
+ }
+ lastv = nil
+ }
+ }
+
+ // Sanity check.
+ if !live.Eq(be.liveout) {
+ panic("wrong arg liveness map at block end")
+ }
+ }
+
+ // Emit funcdata symbol, update indices to offsets in the symbol data.
+ lsym := lv.emit()
+ fn.LSym.Func().ArgLiveInfo = lsym
+
+ //lv.print()
+
+ p := pp.Prog(obj.AFUNCDATA)
+ p.From.SetConst(objabi.FUNCDATA_ArgLiveInfo)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = lsym
+
+ return lv.blockIdx, lv.valueIdx
+}
+
+// valueEffect applies the effect of v to live, return whether it is changed.
+func (lv *argLiveness) valueEffect(v *ssa.Value, live bitvec.BitVec) bool {
+ if v.Op != ssa.OpStoreReg { // TODO: include other store instructions?
+ return false
+ }
+ n, off := ssa.AutoVar(v)
+ if n.Class != ir.PPARAM {
+ return false
+ }
+ i, ok := lv.idx[nameOff{n, off}]
+ if !ok || live.Get(i) {
+ return false
+ }
+ live.Set(i)
+ return true
+}
+
+func mayFault(v *ssa.Value) bool {
+ switch v.Op {
+ case ssa.OpLoadReg, ssa.OpStoreReg, ssa.OpCopy, ssa.OpPhi,
+ ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive,
+ ssa.OpSelect0, ssa.OpSelect1, ssa.OpSelectN, ssa.OpMakeResult,
+ ssa.OpConvert, ssa.OpInlMark, ssa.OpGetG:
+ return false
+ }
+ if len(v.Args) == 0 {
+ return false // assume constant op cannot fault
+ }
+ return true // conservatively assume all other ops could fault
+}
+
+func (lv *argLiveness) print() {
+ fmt.Println("argument liveness:", lv.f.Name)
+ live := bitvec.New(int32(len(lv.args)))
+ for _, b := range lv.f.Blocks {
+ be := &lv.be[b.ID]
+
+ fmt.Printf("%v: live in: ", b)
+ lv.printLivenessVec(be.livein)
+ if idx, ok := lv.blockIdx[b.ID]; ok {
+ fmt.Printf(" #%d", idx)
+ }
+ fmt.Println()
+
+ for _, v := range b.Values {
+ if lv.valueEffect(v, live) {
+ fmt.Printf(" %v: ", v)
+ lv.printLivenessVec(live)
+ if idx, ok := lv.valueIdx[v.ID]; ok {
+ fmt.Printf(" #%d", idx)
+ }
+ fmt.Println()
+ }
+ }
+
+ fmt.Printf("%v: live out: ", b)
+ lv.printLivenessVec(be.liveout)
+ fmt.Println()
+ }
+ fmt.Println("liveness maps data:", lv.fn.LSym.Func().ArgLiveInfo.P)
+}
+
+func (lv *argLiveness) printLivenessVec(bv bitvec.BitVec) {
+ for i, a := range lv.args {
+ if bv.Get(int32(i)) {
+ fmt.Printf("%v ", a)
+ }
+ }
+}
+
+func (lv *argLiveness) emit() *obj.LSym {
+ livenessMaps := lv.bvset.extractUnique()
+
+ // stack offsets of register arg spill slots
+ argOffsets := make([]uint8, len(lv.args))
+ for i, a := range lv.args {
+ off := a.FrameOffset()
+ if off > 0xff {
+ panic("offset too large")
+ }
+ argOffsets[i] = uint8(off)
+ }
+
+ idx2off := make([]int, len(livenessMaps))
+
+ lsym := base.Ctxt.Lookup(lv.fn.LSym.Name + ".argliveinfo")
+ lsym.Set(obj.AttrContentAddressable, true)
+
+ off := objw.Uint8(lsym, 0, argOffsets[0]) // smallest offset that needs liveness info.
+ for idx, live := range livenessMaps {
+ idx2off[idx] = off
+ off = objw.BitVec(lsym, off, live)
+ }
+
+ // Update liveness indices to offsets.
+ for i, x := range lv.blockIdx {
+ if x != allLiveIdx {
+ lv.blockIdx[i] = idx2off[x]
+ }
+ }
+ for i, x := range lv.valueIdx {
+ if x != allLiveIdx {
+ lv.valueIdx[i] = idx2off[x]
+ }
+ }
+
+ return lsym
+}
diff --git a/src/cmd/compile/internal/liveness/bvset.go b/src/cmd/compile/internal/liveness/bvset.go
index 3431f54ede..60b2593867 100644
--- a/src/cmd/compile/internal/liveness/bvset.go
+++ b/src/cmd/compile/internal/liveness/bvset.go
@@ -47,9 +47,10 @@ func (m *bvecSet) grow() {
m.index = newIndex
}
-// add adds bv to the set and returns its index in m.extractUnique.
-// The caller must not modify bv after this.
-func (m *bvecSet) add(bv bitvec.BitVec) int {
+// add adds bv to the set and returns its index in m.extractUnique,
+// and whether it is newly added.
+// If it is newly added, the caller must not modify bv after this.
+func (m *bvecSet) add(bv bitvec.BitVec) (int, bool) {
if len(m.uniq)*4 >= len(m.index) {
m.grow()
}
@@ -62,12 +63,12 @@ func (m *bvecSet) add(bv bitvec.BitVec) int {
// New bvec.
index[h] = len(m.uniq)
m.uniq = append(m.uniq, bv)
- return len(m.uniq) - 1
+ return len(m.uniq) - 1, true
}
jlive := m.uniq[j]
if bv.Eq(jlive) {
// Existing bvec.
- return j
+ return j, false
}
h++
diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go
index 56580d11b5..3202e506c8 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -854,8 +854,9 @@ func (lv *liveness) epilogue() {
if lv.fn.OpenCodedDeferDisallowed() {
lv.livenessMap.DeferReturn = objw.LivenessDontCare
} else {
+ idx, _ := lv.stackMapSet.add(livedefer)
lv.livenessMap.DeferReturn = objw.LivenessIndex{
- StackMapIndex: lv.stackMapSet.add(livedefer),
+ StackMapIndex: idx,
IsUnsafePoint: false,
}
}
@@ -902,7 +903,7 @@ func (lv *liveness) compact(b *ssa.Block) {
isUnsafePoint := lv.allUnsafe || v.Op != ssa.OpClobber && lv.unsafePoints.Get(int32(v.ID))
idx := objw.LivenessIndex{StackMapIndex: objw.StackMapDontCare, IsUnsafePoint: isUnsafePoint}
if hasStackMap {
- idx.StackMapIndex = lv.stackMapSet.add(lv.livevars[pos])
+ idx.StackMapIndex, _ = lv.stackMapSet.add(lv.livevars[pos])
pos++
}
if hasStackMap || isUnsafePoint {
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
index f2dad9c302..82455f7d4a 100644
--- a/src/cmd/compile/internal/noder/decl.go
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -132,7 +132,11 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
g.target.Inits = append(g.target.Inits, fn)
}
+ haveEmbed := g.haveEmbed
g.later(func() {
+ defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
+
+ g.haveEmbed = haveEmbed
if fn.Type().HasTParam() {
g.topFuncIsGeneric = true
}
@@ -241,12 +245,15 @@ func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
if decl.Pragma != nil {
pragma := decl.Pragma.(*pragmas)
- // TODO(mdempsky): Plumb noder.importedEmbed through to here.
- varEmbed(g.makeXPos, names[0], decl, pragma, true)
+ varEmbed(g.makeXPos, names[0], decl, pragma, g.haveEmbed)
g.reportUnused(pragma)
}
+ haveEmbed := g.haveEmbed
do := func() {
+ defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
+
+ g.haveEmbed = haveEmbed
values := g.exprList(decl.Values)
var as2 *ir.AssignListStmt
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index 65568f2307..24e6dbefe7 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -235,12 +235,6 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
return DotField(pos, x, last)
}
- // TODO(danscales,mdempsky): Interface method sets are not sorted the
- // same between types and types2. In particular, using "last" here
- // without conversion will likely fail if an interface contains
- // unexported methods from two different packages (due to cross-package
- // interface embedding).
-
var n ir.Node
method2 := selinfo.Obj().(*types2.Func)
@@ -344,7 +338,7 @@ func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
return typed(g.typ(typ), n)
}
- _, isStruct := typ.Underlying().(*types2.Struct)
+ _, isStruct := types2.Structure(typ).(*types2.Struct)
exprs := make([]ir.Node, len(lit.ElemList))
for i, elem := range lit.ElemList {
diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go
index adb5a0e89f..5524673e66 100644
--- a/src/cmd/compile/internal/noder/helpers.go
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -183,7 +183,7 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
// If no type params, do the normal call transformations. This
// will convert OCALL to OCALLFUNC.
typed(typ, n)
- transformCall(n, nil)
+ transformCall(n)
return n
}
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index 0aaf894641..58dffbad1e 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -127,6 +127,8 @@ func openPackage(path string) (*os.File, error) {
suffix = "_race"
} else if base.Flag.MSan {
suffix = "_msan"
+ } else if base.Flag.ASan {
+ suffix = "_asan"
}
if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index e912f797b2..e20939de66 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -44,7 +44,6 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
GoVersion: base.Flag.Lang,
IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode
CompilerErrorMessages: true, // use error strings matching existing compiler errors
- AllowTypeLists: true, // remove this line once all tests use type set syntax
Error: func(err error) {
terr := err.(types2.Error)
base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
@@ -148,6 +147,9 @@ type irgen struct {
// laterFuncs records tasks that need to run after all declarations
// are processed.
laterFuncs []func()
+ // haveEmbed indicates whether the current node belongs to file that
+ // imports "embed" package.
+ haveEmbed bool
// exprStmtOK indicates whether it's safe to generate expressions or
// statements yet.
@@ -156,16 +158,6 @@ type irgen struct {
// types which we need to finish, by doing g.fillinMethods.
typesToFinalize []*typeDelayInfo
- dnum int // for generating unique dictionary variables
-
- // Map from a name of function that been instantiated to information about
- // its instantiated function (including dictionary format).
- instInfoMap map[*types.Sym]*instInfo
-
- // dictionary syms which we need to finish, by writing out any itabconv
- // entries.
- dictSymsToFinalize []*delayInfo
-
// True when we are compiling a top-level generic function or method. Use to
// avoid adding closures of generic functions/methods to the target.Decls
// list.
@@ -178,6 +170,23 @@ type irgen struct {
curDecl string
}
+// genInst has the information for creating needed instantiations and modifying
+// functions to use instantiations.
+type genInst struct {
+ dnum int // for generating unique dictionary variables
+
+ // Map from the names of all instantiations to information about the
+ // instantiations.
+ instInfoMap map[*types.Sym]*instInfo
+
+ // Dictionary syms which we need to finish, by writing out any itabconv
+ // entries.
+ dictSymsToFinalize []*delayInfo
+
+ // New instantiations created during this round of buildInstantiations().
+ newInsts []ir.Node
+}
+
func (g *irgen) later(fn func()) {
g.laterFuncs = append(g.laterFuncs, fn)
}
@@ -255,8 +264,11 @@ Outer:
types.ResumeCheckSize()
// 3. Process all remaining declarations.
- for _, declList := range declLists {
+ for i, declList := range declLists {
+ old := g.haveEmbed
+ g.haveEmbed = noders[i].importedEmbed
g.decls((*ir.Nodes)(&g.target.Decls), declList)
+ g.haveEmbed = old
}
g.exprStmtOK = true
@@ -303,8 +315,9 @@ Outer:
typecheck.DeclareUniverse()
- // Create any needed stencils of generic functions
- g.stencil()
+ // Create any needed instantiations of generic functions and transform
+ // existing and new functions to use those instantiations.
+ BuildInstantiations(true)
// Remove all generic functions from g.target.Decl, since they have been
// used for stenciling, but don't compile. Generic functions will already
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 2e7b636681..b36db67a50 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -36,7 +36,7 @@ func LoadPackage(filenames []string) {
mode := syntax.CheckBranches
if supportsGenerics {
- mode |= syntax.AllowGenerics | syntax.AllowTypeSets
+ mode |= syntax.AllowGenerics
}
// Limit the number of simultaneously open files.
@@ -323,8 +323,7 @@ func (p *noder) processPragmas() {
}
n := ir.AsNode(typecheck.Lookup(l.local).Def)
if n == nil || n.Op() != ir.ONAME {
- // TODO(mdempsky): Change to p.errorAt before Go 1.17 release.
- // base.WarnfAt(p.makeXPos(l.pos), "//go:linkname must refer to declared function or variable (will be an error in Go 1.17)")
+ p.errorAt(l.pos, "//go:linkname must refer to declared function or variable")
continue
}
if n.Sym().Linkname != "" {
@@ -1238,7 +1237,7 @@ func (p *noder) ifStmt(stmt *syntax.IfStmt) ir.Node {
init := p.stmt(stmt.Init)
n := ir.NewIfStmt(p.pos(stmt), p.expr(stmt.Cond), p.blockStmt(stmt.Then), nil)
if init != nil {
- *n.PtrInit() = []ir.Node{init}
+ n.SetInit([]ir.Node{init})
}
if stmt.Else != nil {
e := p.stmt(stmt.Else)
@@ -1285,7 +1284,7 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
init := p.stmt(stmt.Init)
n := ir.NewSwitchStmt(p.pos(stmt), p.expr(stmt.Tag), nil)
if init != nil {
- *n.PtrInit() = []ir.Node{init}
+ n.SetInit([]ir.Node{init})
}
var tswitch *ir.TypeSwitchGuard
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
index 48f4368113..0bc9135999 100644
--- a/src/cmd/compile/internal/noder/reader.go
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -466,7 +466,7 @@ func (r *reader) interfaceType() *types.Type {
if len(fields) == 0 {
return types.Types[types.TINTER] // empty interface
}
- return types.NewInterface(tpkg, fields)
+ return types.NewInterface(tpkg, fields, false)
}
func (r *reader) structType() *types.Type {
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 447fe8a538..74281bc479 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -9,6 +9,7 @@ package noder
import (
"cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
@@ -27,8 +28,8 @@ func assert(p bool) {
base.Assert(p)
}
-// Temporary - for outputting information on derived types, dictionaries, sub-dictionaries.
-// Turn off when running tests.
+// For outputting debug information on dictionary format and instantiated dictionaries
+// (type arg, derived types, sub-dictionary, and itab entries).
var infoPrintMode = false
func infoPrint(format string, a ...interface{}) {
@@ -37,207 +38,54 @@ func infoPrint(format string, a ...interface{}) {
}
}
-// stencil scans functions for instantiated generic function calls and creates the
-// required instantiations for simple generic functions. It also creates
-// instantiated methods for all fully-instantiated generic types that have been
-// encountered already or new ones that are encountered during the stenciling
-// process.
-func (g *irgen) stencil() {
- g.instInfoMap = make(map[*types.Sym]*instInfo)
+var geninst genInst
+func BuildInstantiations(preinliningMainScan bool) {
+ if geninst.instInfoMap == nil {
+ geninst.instInfoMap = make(map[*types.Sym]*instInfo)
+ }
+ geninst.buildInstantiations(preinliningMainScan)
+}
+
+// buildInstantiations scans functions for generic function calls and methods, and
+// creates the required instantiations. It also creates instantiated methods for all
+// fully-instantiated generic types that have been encountered already or new ones
+// that are encountered during the instantiation process. If preinliningMainScan is
+// true, it scans all declarations in typecheck.Target.Decls first, before scanning
+// any new instantiations created. If preinliningMainScan is false, we do not scan
+// any existing decls - we only scan method instantiations for any new
+// fully-instantiated types that we saw during inlining.
+func (g *genInst) buildInstantiations(preinliningMainScan bool) {
// Instantiate the methods of instantiated generic types that we have seen so far.
g.instantiateMethods()
- // Don't use range(g.target.Decls) - we also want to process any new instantiated
- // functions that are created during this loop, in order to handle generic
- // functions calling other generic functions.
- for i := 0; i < len(g.target.Decls); i++ {
- decl := g.target.Decls[i]
-
- // Look for function instantiations in bodies of non-generic
- // functions or in global assignments (ignore global type and
- // constant declarations).
- switch decl.Op() {
- case ir.ODCLFUNC:
- if decl.Type().HasTParam() {
- // Skip any generic functions
- continue
- }
- // transformCall() below depends on CurFunc being set.
- ir.CurFunc = decl.(*ir.Func)
-
- case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
- // These are all the various kinds of global assignments,
- // whose right-hand-sides might contain a function
- // instantiation.
-
- default:
- // The other possible ops at the top level are ODCLCONST
- // and ODCLTYPE, which don't have any function
- // instantiations.
- continue
+ if preinliningMainScan {
+ n := len(typecheck.Target.Decls)
+ for i := 0; i < n; i++ {
+ g.scanForGenCalls(typecheck.Target.Decls[i])
}
+ }
- // For all non-generic code, search for any function calls using
- // generic function instantiations. Then create the needed
- // instantiated function if it hasn't been created yet, and change
- // to calling that function directly.
- modified := false
- closureRequired := false
- // declInfo will be non-nil exactly if we are scanning an instantiated function
- declInfo := g.instInfoMap[decl.Sym()]
-
- ir.Visit(decl, func(n ir.Node) {
- if n.Op() == ir.OFUNCINST {
- // generic F, not immediately called
- closureRequired = true
- }
- if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
- // T.M or x.M, where T or x is generic, but not immediately
- // called. Not necessary if the method selected is
- // actually for an embedded interface field.
- closureRequired = true
- }
- if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
- // We have found a function call using a generic function
- // instantiation.
- call := n.(*ir.CallExpr)
- inst := call.X.(*ir.InstExpr)
- nameNode, isMeth := g.getInstNameNode(inst)
- targs := typecheck.TypesOf(inst.Targs)
- st := g.getInstantiation(nameNode, targs, isMeth).fun
- dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
- if infoPrintMode {
- dictkind := "Main dictionary"
- if usingSubdict {
- dictkind = "Sub-dictionary"
- }
- if inst.X.Op() == ir.OMETHVALUE {
- fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
- } else {
- fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
- }
- }
-
- // Transform the Call now, which changes OCALL to
- // OCALLFUNC and does typecheckaste/assignconvfn. Do
- // it before installing the instantiation, so we are
- // checking against non-shape param types in
- // typecheckaste.
- transformCall(call, nil)
-
- // Replace the OFUNCINST with a direct reference to the
- // new stenciled function
- call.X = st.Nname
- if inst.X.Op() == ir.OMETHVALUE {
- // When we create an instantiation of a method
- // call, we make it a function. So, move the
- // receiver to be the first arg of the function
- // call.
- call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
- }
-
- // Add dictionary to argument list.
- call.Args.Prepend(dictValue)
- modified = true
- }
- if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
- // Method call on a generic type, which was instantiated by stenciling.
- // Method calls on explicitly instantiated types will have an OFUNCINST
- // and are handled above.
- call := n.(*ir.CallExpr)
- meth := call.X.(*ir.SelectorExpr)
- targs := deref(meth.Type().Recv().Type).RParams()
-
- t := meth.X.Type()
- baseSym := deref(t).OrigSym()
- baseType := baseSym.Def.(*ir.Name).Type()
- var gf *ir.Name
- for _, m := range baseType.Methods().Slice() {
- if meth.Sel == m.Sym {
- gf = m.Nname.(*ir.Name)
- break
- }
- }
-
- // Transform the Call now, which changes OCALL
- // to OCALLFUNC and does typecheckaste/assignconvfn.
- transformCall(call, nil)
-
- st := g.getInstantiation(gf, targs, true).fun
- dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
- // We have to be using a subdictionary, since this is
- // a generic method call.
- assert(usingSubdict)
-
- // Transform to a function call, by appending the
- // dictionary and the receiver to the args.
- call.SetOp(ir.OCALLFUNC)
- call.X = st.Nname
- call.Args.Prepend(dictValue, meth.X)
- modified = true
- }
- })
-
- // If we found a reference to a generic instantiation that wasn't an
- // immediate call, then traverse the nodes of decl again (with
- // EditChildren rather than Visit), where we actually change the
- // reference to the instantiation to a closure that captures the
- // dictionary, then does a direct call.
- // EditChildren is more expensive than Visit, so we only do this
- // in the infrequent case of an OFUNCINST without a corresponding
- // call.
- if closureRequired {
- modified = true
- var edit func(ir.Node) ir.Node
- var outer *ir.Func
- if f, ok := decl.(*ir.Func); ok {
- outer = f
- }
- edit = func(x ir.Node) ir.Node {
- if x.Op() == ir.OFUNCINST {
- child := x.(*ir.InstExpr).X
- if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
- // Call EditChildren on child (x.X),
- // not x, so that we don't do
- // buildClosure() on the
- // METHEXPR/METHVALUE nodes as well.
- ir.EditChildren(child, edit)
- return g.buildClosure(outer, x)
- }
- }
- ir.EditChildren(x, edit)
- switch {
- case x.Op() == ir.OFUNCINST:
- return g.buildClosure(outer, x)
- case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
- len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
- !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
- return g.buildClosure(outer, x)
- }
- return x
- }
- edit(decl)
- }
- if base.Flag.W > 1 && modified {
- ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
- }
- ir.CurFunc = nil
- // We may have seen new fully-instantiated generic types while
- // instantiating any needed functions/methods in the above
- // function. If so, instantiate all the methods of those types
- // (which will then lead to more function/methods to scan in the loop).
- g.instantiateMethods()
+ // Scan all new instantiations created due to g.instantiateMethods() and the
+ // scan of current decls (if done). This loop purposely runs until no new
+ // instantiations are created.
+ for i := 0; i < len(g.newInsts); i++ {
+ g.scanForGenCalls(g.newInsts[i])
}
g.finalizeSyms()
// All the instantiations and dictionaries have been created. Now go through
- // each instantiation and transform the various operations that need to make
+ // each new instantiation and transform the various operations that need to make
// use of their dictionary.
- l := len(g.instInfoMap)
- for _, info := range g.instInfoMap {
+ l := len(g.newInsts)
+ for _, fun := range g.newInsts {
+ info := g.instInfoMap[fun.Sym()]
g.dictPass(info)
+ if !preinliningMainScan {
+ // Prepare for the round of inlining below.
+ inline.CanInline(fun.(*ir.Func))
+ }
if doubleCheck {
ir.Visit(info.fun, func(n ir.Node) {
if n.Op() != ir.OCONVIFACE {
@@ -255,13 +103,198 @@ func (g *irgen) stencil() {
ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
}
}
- assert(l == len(g.instInfoMap))
+ if !preinliningMainScan {
+ // Extra round of inlining for the new instantiations (only if
+ // preinliningMainScan is false, which means we have already done the
+ // main round of inlining)
+ for _, fun := range g.newInsts {
+ inline.InlineCalls(fun.(*ir.Func))
+ }
+ }
+ assert(l == len(g.newInsts))
+ g.newInsts = nil
+}
+
+// scanForGenCalls scans a single function (or global assignment), looking for
+// references to generic functions/methods. At each such reference, it creates any
+// required instantiation and transforms the reference.
+func (g *genInst) scanForGenCalls(decl ir.Node) {
+ switch decl.Op() {
+ case ir.ODCLFUNC:
+ if decl.Type().HasTParam() {
+ // Skip any generic functions
+ return
+ }
+ // transformCall() below depends on CurFunc being set.
+ ir.CurFunc = decl.(*ir.Func)
+
+ case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
+ // These are all the various kinds of global assignments,
+ // whose right-hand-sides might contain a function
+ // instantiation.
+
+ default:
+ // The other possible ops at the top level are ODCLCONST
+ // and ODCLTYPE, which don't have any function
+ // instantiations.
+ return
+ }
+
+ // Search for any function references using generic function/methods. Then
+ // create the needed instantiated function if it hasn't been created yet, and
+ // change to calling that function directly.
+ modified := false
+ closureRequired := false
+ // declInfo will be non-nil exactly if we are scanning an instantiated function
+ declInfo := g.instInfoMap[decl.Sym()]
+
+ ir.Visit(decl, func(n ir.Node) {
+ if n.Op() == ir.OFUNCINST {
+ // generic F, not immediately called
+ closureRequired = true
+ }
+ if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
+ // T.M or x.M, where T or x is generic, but not immediately
+ // called. Not necessary if the method selected is
+ // actually for an embedded interface field.
+ closureRequired = true
+ }
+ if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
+ // We have found a function call using a generic function
+ // instantiation.
+ call := n.(*ir.CallExpr)
+ inst := call.X.(*ir.InstExpr)
+ nameNode, isMeth := g.getInstNameNode(inst)
+ targs := typecheck.TypesOf(inst.Targs)
+ st := g.getInstantiation(nameNode, targs, isMeth).fun
+ dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
+ if infoPrintMode {
+ dictkind := "Main dictionary"
+ if usingSubdict {
+ dictkind = "Sub-dictionary"
+ }
+ if inst.X.Op() == ir.OMETHVALUE {
+ fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
+ } else {
+ fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
+ }
+ }
+
+ // Transform the Call now, which changes OCALL to
+ // OCALLFUNC and does typecheckaste/assignconvfn. Do
+ // it before installing the instantiation, so we are
+ // checking against non-shape param types in
+ // typecheckaste.
+ transformCall(call)
+
+ // Replace the OFUNCINST with a direct reference to the
+ // new stenciled function
+ call.X = st.Nname
+ if inst.X.Op() == ir.OMETHVALUE {
+ // When we create an instantiation of a method
+ // call, we make it a function. So, move the
+ // receiver to be the first arg of the function
+ // call.
+ call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
+ }
+
+ // Add dictionary to argument list.
+ call.Args.Prepend(dictValue)
+ modified = true
+ }
+ if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
+ // Method call on a generic type, which was instantiated by stenciling.
+ // Method calls on explicitly instantiated types will have an OFUNCINST
+ // and are handled above.
+ call := n.(*ir.CallExpr)
+ meth := call.X.(*ir.SelectorExpr)
+ targs := deref(meth.Type().Recv().Type).RParams()
+
+ t := meth.X.Type()
+ baseSym := deref(t).OrigSym()
+ baseType := baseSym.Def.(*ir.Name).Type()
+ var gf *ir.Name
+ for _, m := range baseType.Methods().Slice() {
+ if meth.Sel == m.Sym {
+ gf = m.Nname.(*ir.Name)
+ break
+ }
+ }
+
+ // Transform the Call now, which changes OCALL
+ // to OCALLFUNC and does typecheckaste/assignconvfn.
+ transformCall(call)
+
+ st := g.getInstantiation(gf, targs, true).fun
+ dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
+ // We have to be using a subdictionary, since this is
+ // a generic method call.
+ assert(usingSubdict)
+
+ // Transform to a function call, by appending the
+ // dictionary and the receiver to the args.
+ call.SetOp(ir.OCALLFUNC)
+ call.X = st.Nname
+ call.Args.Prepend(dictValue, meth.X)
+ modified = true
+ }
+ })
+
+ // If we found a reference to a generic instantiation that wasn't an
+ // immediate call, then traverse the nodes of decl again (with
+ // EditChildren rather than Visit), where we actually change the
+ // reference to the instantiation to a closure that captures the
+ // dictionary, then does a direct call.
+ // EditChildren is more expensive than Visit, so we only do this
+ // in the infrequent case of an OFUNCINST without a corresponding
+ // call.
+ if closureRequired {
+ modified = true
+ var edit func(ir.Node) ir.Node
+ var outer *ir.Func
+ if f, ok := decl.(*ir.Func); ok {
+ outer = f
+ }
+ edit = func(x ir.Node) ir.Node {
+ if x.Op() == ir.OFUNCINST {
+ child := x.(*ir.InstExpr).X
+ if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
+ // Call EditChildren on child (x.X),
+ // not x, so that we don't do
+ // buildClosure() on the
+ // METHEXPR/METHVALUE nodes as well.
+ ir.EditChildren(child, edit)
+ return g.buildClosure(outer, x)
+ }
+ }
+ ir.EditChildren(x, edit)
+ switch {
+ case x.Op() == ir.OFUNCINST:
+ return g.buildClosure(outer, x)
+ case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
+ len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
+ !types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
+ return g.buildClosure(outer, x)
+ }
+ return x
+ }
+ edit(decl)
+ }
+ if base.Flag.W > 1 && modified {
+ ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
+ }
+ ir.CurFunc = nil
+ // We may have seen new fully-instantiated generic types while
+ // instantiating any needed functions/methods in the above
+ // function. If so, instantiate all the methods of those types
+ // (which will then lead to more function/methods to scan in the loop).
+ g.instantiateMethods()
}
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR/OMETHVALUE
// of generic type. outer is the containing function (or nil if closure is
// in a global assignment instead of a function).
-func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
+func (g *genInst) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
pos := x.Pos()
var target *ir.Func // target instantiated function/method
var dictValue ir.Node // dictionary to use
@@ -423,8 +456,8 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
rcvrVar.Defn = rcvrAssign
if outer == nil {
rcvrVar.Class = ir.PEXTERN
- g.target.Decls = append(g.target.Decls, rcvrAssign)
- g.target.Externs = append(g.target.Externs, rcvrVar)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
+ typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
} else {
rcvrVar.Class = ir.PAUTO
rcvrVar.Curfn = outer
@@ -496,7 +529,7 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
ir.FinishCaptureNames(pos, outer, fn)
// Make a closure referencing our new internal function.
- c := ir.UseClosure(fn.OClosure, g.target)
+ c := ir.UseClosure(fn.OClosure, typecheck.Target)
var init []ir.Node
if outer != nil {
init = append(init, dictAssign)
@@ -510,12 +543,13 @@ func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
// instantiateMethods instantiates all the methods (and associated dictionaries) of
// all fully-instantiated generic types that have been added to typecheck.instTypeList.
// It continues until no more types are added to typecheck.instTypeList.
-func (g *irgen) instantiateMethods() {
+func (g *genInst) instantiateMethods() {
for {
instTypeList := typecheck.GetInstTypeList()
if len(instTypeList) == 0 {
break
}
+ typecheck.ClearInstTypeList()
for _, typ := range instTypeList {
assert(!typ.HasShape())
// Mark runtime type as needed, since this ensures that the
@@ -548,7 +582,7 @@ func (g *irgen) instantiateMethods() {
}
// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated.
-func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
+func (g *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
return meth.Selection.Nname.(*ir.Name), true
} else {
@@ -561,7 +595,7 @@ func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
// or main/static dictionary, as needed, and also returns a boolean indicating if a
// sub-dictionary was accessed. nameNode is the particular function or method being
// called/referenced, and targs are the type arguments.
-func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
+func (g *genInst) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
var dict ir.Node
usingSubdict := false
if declInfo != nil {
@@ -590,7 +624,7 @@ func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Nam
// yet. If so, it imports the body.
func checkFetchBody(nameNode *ir.Name) {
if nameNode.Func.Body == nil && nameNode.Func.Inl != nil {
- // If there is no body yet but Func.Inl exists, then we can can
+ // If there is no body yet but Func.Inl exists, then we can
// import the whole generic body.
assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
typecheck.ImportBody(nameNode.Func)
@@ -603,8 +637,19 @@ func checkFetchBody(nameNode *ir.Name) {
// getInstantiation gets the instantiantion and dictionary of the function or method nameNode
// with the type arguments shapes. If the instantiated function is not already
// cached, then it calls genericSubst to create the new instantiation.
-func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
- checkFetchBody(nameNode)
+func (g *genInst) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth bool) *instInfo {
+ if nameNode.Func == nil {
+ // If nameNode.Func is nil, this must be a reference to a method of
+ // an imported instantiated type. We will have already called
+ // g.instantiateMethods() on the fully-instantiated type, so
+ // g.instInfoMap[sym] will be non-nil below.
+ rcvr := nameNode.Type().Recv()
+ if rcvr == nil || !deref(rcvr.Type).IsFullyInstantiated() {
+ base.FatalfAt(nameNode.Pos(), "Unexpected function instantiation %v with no body", nameNode)
+ }
+ } else {
+ checkFetchBody(nameNode)
+ }
// Convert any non-shape type arguments to their shape, so we can reduce the
// number of instantiations we have to generate. You can actually have a mix
@@ -645,7 +690,8 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
// This ensures that the linker drops duplicates of this instantiation.
// All just works!
st.SetDupok(true)
- g.target.Decls = append(g.target.Decls, st)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, st)
+ g.newInsts = append(g.newInsts, st)
}
return info
}
@@ -653,7 +699,7 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
// Struct containing info needed for doing the substitution as we create the
// instantiation of a generic function with specified type arguments.
type subster struct {
- g *irgen
+ g *genInst
isMethod bool // If a method is being instantiated
newf *ir.Func // Func node for the new stenciled function
ts typecheck.Tsubster
@@ -669,7 +715,7 @@ type subster struct {
// function type where the receiver becomes the first parameter. For either a generic
// method or function, a dictionary parameter is the added as the very first
// parameter. genericSubst fills in info.dictParam and info.tparamToBound.
-func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
+func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes []*types.Type, isMethod bool, info *instInfo) *ir.Func {
var tparams []*types.Type
if isMethod {
// Get the type params from the method receiver (after skipping
@@ -846,7 +892,7 @@ func (subst *subster) checkDictionary(name *ir.Name, targs []*types.Type) (code
cond := ir.NewBinaryExpr(pos, ir.ONE, want, got)
typed(types.Types[types.TBOOL], cond)
panicArg := ir.NewNilExpr(pos)
- typed(types.NewInterface(types.LocalPkg, nil), panicArg)
+ typed(types.NewInterface(types.LocalPkg, nil, false), panicArg)
then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg)
then.SetTypecheck(1)
x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil)
@@ -893,8 +939,9 @@ func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int)
}
// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and
-// also descends into closures. It substitutes type arguments for type parameters
-// in all the new nodes.
+// also descends into closures. It substitutes type arguments for type parameters in
+// all the new nodes and does the transformations that were delayed on the generic
+// function.
func (subst *subster) node(n ir.Node) ir.Node {
// Use closure to capture all state needed by the ir.EditChildren argument.
var edit func(ir.Node) ir.Node
@@ -938,15 +985,15 @@ func (subst *subster) node(n ir.Node) ir.Node {
if _, isExpr := m.(ir.Expr); isExpr {
t := x.Type()
if t == nil {
- // t can be nil only if this is a call that has no
- // return values, so allow that and otherwise give
- // an error.
+ // Check for known cases where t can be nil (call
+ // that has no return values, and key expressions)
+ // and otherwise cause a fatal error.
_, isCallExpr := m.(*ir.CallExpr)
_, isStructKeyExpr := m.(*ir.StructKeyExpr)
_, isKeyExpr := m.(*ir.KeyExpr)
if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
x.Op() != ir.OCLOSE {
- base.Fatalf(fmt.Sprintf("Nil type for %v", x))
+ base.FatalfAt(m.Pos(), "Nil type for %v", x)
}
} else if x.Op() != ir.OCLOSURE {
m.SetType(subst.ts.Typ(x.Type()))
@@ -956,56 +1003,55 @@ func (subst *subster) node(n ir.Node) ir.Node {
ir.EditChildren(m, edit)
m.SetTypecheck(1)
- if x.Op().IsCmp() {
- transformCompare(m.(*ir.BinaryExpr))
- } else {
- switch x.Op() {
- case ir.OSLICE, ir.OSLICE3:
- transformSlice(m.(*ir.SliceExpr))
-
- case ir.OADD:
- m = transformAdd(m.(*ir.BinaryExpr))
-
- case ir.OINDEX:
- transformIndex(m.(*ir.IndexExpr))
-
- case ir.OAS2:
- as2 := m.(*ir.AssignListStmt)
- transformAssign(as2, as2.Lhs, as2.Rhs)
-
- case ir.OAS:
- as := m.(*ir.AssignStmt)
- if as.Y != nil {
- // transformAssign doesn't handle the case
- // of zeroing assignment of a dcl (rhs[0] is nil).
- lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
- transformAssign(as, lhs, rhs)
- as.X, as.Y = lhs[0], rhs[0]
- }
-
- case ir.OASOP:
- as := m.(*ir.AssignOpStmt)
- transformCheckAssign(as, as.X)
-
- case ir.ORETURN:
- transformReturn(m.(*ir.ReturnStmt))
-
- case ir.OSEND:
- transformSend(m.(*ir.SendStmt))
-
- case ir.OSELECT:
- transformSelect(m.(*ir.SelectStmt))
-
- case ir.OCOMPLIT:
- transformCompLit(m.(*ir.CompLitExpr))
-
- case ir.OADDR:
- transformAddr(m.(*ir.AddrExpr))
-
- }
- }
+ // Do the transformations that we delayed on the generic function
+ // node, now that we have substituted in the type args.
switch x.Op() {
+ case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ transformCompare(m.(*ir.BinaryExpr))
+
+ case ir.OSLICE, ir.OSLICE3:
+ transformSlice(m.(*ir.SliceExpr))
+
+ case ir.OADD:
+ m = transformAdd(m.(*ir.BinaryExpr))
+
+ case ir.OINDEX:
+ transformIndex(m.(*ir.IndexExpr))
+
+ case ir.OAS2:
+ as2 := m.(*ir.AssignListStmt)
+ transformAssign(as2, as2.Lhs, as2.Rhs)
+
+ case ir.OAS:
+ as := m.(*ir.AssignStmt)
+ if as.Y != nil {
+ // transformAssign doesn't handle the case
+ // of zeroing assignment of a dcl (rhs[0] is nil).
+ lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
+ transformAssign(as, lhs, rhs)
+ as.X, as.Y = lhs[0], rhs[0]
+ }
+
+ case ir.OASOP:
+ as := m.(*ir.AssignOpStmt)
+ transformCheckAssign(as, as.X)
+
+ case ir.ORETURN:
+ transformReturn(m.(*ir.ReturnStmt))
+
+ case ir.OSEND:
+ transformSend(m.(*ir.SendStmt))
+
+ case ir.OSELECT:
+ transformSelect(m.(*ir.SelectStmt))
+
+ case ir.OCOMPLIT:
+ transformCompLit(m.(*ir.CompLitExpr))
+
+ case ir.OADDR:
+ transformAddr(m.(*ir.AddrExpr))
+
case ir.OLITERAL:
t := m.Type()
if t != x.Type() {
@@ -1023,16 +1069,13 @@ func (subst *subster) node(n ir.Node) ir.Node {
}
case ir.OXDOT:
- // A method value/call via a type param will have been
- // left as an OXDOT. When we see this during stenciling,
- // finish the transformation, now that we have the
- // instantiated receiver type. We need to do this now,
- // since the access/selection to the method for the real
- // type is very different from the selection for the type
- // param. m will be transformed to an OMETHVALUE node. It
- // will be transformed to an ODOTMETH or ODOTINTER node if
- // we find in the OCALL case below that the method value
- // is actually called.
+ // Finish the transformation of an OXDOT, unless this was a
+ // bound call (a direct call on a type param). A bound call
+ // will be transformed during the dictPass. Otherwise, m
+ // will be transformed to an OMETHVALUE node. It will be
+ // transformed to an ODOTMETH or ODOTINTER node if we find in
+ // the OCALL case below that the method value is actually
+ // called.
mse := m.(*ir.SelectorExpr)
if src := mse.X.Type(); !src.IsShape() {
transformDot(mse, false)
@@ -1045,7 +1088,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
// Transform the conversion, now that we know the
// type argument.
m = transformConvCall(call)
- // CONVIFACE transformation was already done in node2
+ // CONVIFACE transformation was already done in noder2
assert(m.Op() != ir.OCONVIFACE)
case ir.OMETHVALUE, ir.OMETHEXPR:
@@ -1054,14 +1097,14 @@ func (subst *subster) node(n ir.Node) ir.Node {
// transform the call.
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
transformDot(call.X.(*ir.SelectorExpr), true)
- transformCall(call, subst.info.dictParam)
+ transformCall(call)
case ir.ODOT, ir.ODOTPTR:
// An OXDOT for a generic receiver was resolved to
// an access to a field which has a function
// value. Transform the call to that function, now
// that the OXDOT was resolved.
- transformCall(call, subst.info.dictParam)
+ transformCall(call)
case ir.ONAME:
name := call.X.Name()
@@ -1078,37 +1121,32 @@ func (subst *subster) node(n ir.Node) ir.Node {
// This is the case of a function value that was a
// type parameter (implied to be a function via a
// structural constraint) which is now resolved.
- transformCall(call, subst.info.dictParam)
+ transformCall(call)
}
- case ir.OCLOSURE:
- transformCall(call, subst.info.dictParam)
-
- case ir.ODEREF, ir.OINDEX, ir.OINDEXMAP, ir.ORECV:
- // Transform a call that was delayed because of the
- // use of typeparam inside an expression that required
- // a pointer dereference, array indexing, map indexing,
- // or channel receive to compute function value.
- transformCall(call, subst.info.dictParam)
-
- case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
- transformCall(call, subst.info.dictParam)
-
- case ir.OCONVNOP:
- transformCall(call, subst.info.dictParam)
-
case ir.OFUNCINST:
// A call with an OFUNCINST will get transformed
// in stencil() once we have created & attached the
// instantiation to be called.
// We must transform the arguments of the call now, though,
// so that any needed CONVIFACE nodes are exposed,
- // so the dictionary format is correct
+ // so the dictionary format is correct.
transformEarlyCall(call)
- case ir.OXDOT, ir.ODOTTYPE, ir.ODOTTYPE2:
+ case ir.OXDOT:
+ // This is the case of a bound call on a typeparam,
+ // which will be handled in the dictPass.
+
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ // These are DOTTYPEs that could get transformed into
+ // ODYNAMIC DOTTYPEs by the dict pass.
+
default:
- base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
+ // Transform a call for all other values of
+ // call.X.Op() that don't require any special
+ // handling.
+ transformCall(call)
+
}
case ir.OCLOSURE:
@@ -1170,7 +1208,8 @@ func (subst *subster) node(n ir.Node) ir.Node {
subst.newf = saveNewf
ir.CurFunc = saveNewf
- m = ir.UseClosure(newfn.OClosure, subst.g.target)
+ m = ir.UseClosure(newfn.OClosure, typecheck.Target)
+ subst.g.newInsts = append(subst.g.newInsts, m.(*ir.ClosureExpr).Func)
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
}
@@ -1182,7 +1221,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
// dictPass takes a function instantiation and does the transformations on the
// operations that need to make use of the dictionary param.
-func (g *irgen) dictPass(info *instInfo) {
+func (g *genInst) dictPass(info *instInfo) {
savef := ir.CurFunc
ir.CurFunc = info.fun
@@ -1232,16 +1271,13 @@ func (g *irgen) dictPass(info *instInfo) {
}
case ir.OCALL:
op := m.(*ir.CallExpr).X.Op()
- if op != ir.OFUNCINST {
- assert(op == ir.OMETHVALUE || op == ir.OCLOSURE || op == ir.ODYNAMICDOTTYPE || op == ir.ODYNAMICDOTTYPE2)
- if op == ir.OMETHVALUE {
- // Redo the transformation of OXDOT, now that we
- // know the method value is being called.
- m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
- transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
- }
- transformCall(m.(*ir.CallExpr), info.dictParam)
+ if op == ir.OMETHVALUE {
+ // Redo the transformation of OXDOT, now that we
+ // know the method value is being called.
+ m.(*ir.CallExpr).X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
+ transformDot(m.(*ir.CallExpr).X.(*ir.SelectorExpr), true)
}
+ transformCall(m.(*ir.CallExpr))
case ir.OCONVIFACE:
if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
@@ -1487,23 +1523,17 @@ func deref(t *types.Type) *types.Type {
// needed methods.
func markTypeUsed(t *types.Type, lsym *obj.LSym) {
if t.IsInterface() {
- // Mark all the methods of the interface as used.
- // TODO: we should really only mark the interface methods
- // that are actually called in the application.
- for i, _ := range t.AllMethods().Slice() {
- reflectdata.MarkUsedIfaceMethodIndex(lsym, t, i)
- }
- } else {
- // TODO: This is somewhat overkill, we really only need it
- // for types that are put into interfaces.
- // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
- reflectdata.MarkTypeUsedInInterface(t, lsym)
+ return
}
+ // TODO: This is somewhat overkill, we really only need it
+ // for types that are put into interfaces.
+ // Note: this relocation is also used in cmd/link/internal/ld/dwarf.go
+ reflectdata.MarkTypeUsedInInterface(t, lsym)
}
// getDictionarySym returns the dictionary for the named generic function gf, which
// is instantiated with the type arguments targs.
-func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
+func (g *genInst) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
if len(targs) == 0 {
base.Fatalf("%s should have type arguments", gf.Sym().Name)
}
@@ -1678,7 +1708,7 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
// dictionaries and method instantiations to be complete, so, to avoid recursive
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
// instantiations have been created.
-func (g *irgen) finalizeSyms() {
+func (g *genInst) finalizeSyms() {
for _, d := range g.dictSymsToFinalize {
infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
@@ -1699,18 +1729,6 @@ func (g *irgen) finalizeSyms() {
se := n.(*ir.SelectorExpr)
srctype = subst.Typ(se.X.Type())
dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
- found := false
- for i, m := range dsttype.AllMethods().Slice() {
- if se.Sel == m.Sym {
- // Mark that this method se.Sel is
- // used for the dsttype interface, so
- // it won't get deadcoded.
- reflectdata.MarkUsedIfaceMethodIndex(lsym, dsttype, i)
- found = true
- break
- }
- }
- assert(found)
case ir.ODOTTYPE, ir.ODOTTYPE2:
srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type())
dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type())
@@ -1744,7 +1762,7 @@ func (g *irgen) finalizeSyms() {
g.dictSymsToFinalize = nil
}
-func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
+func (g *genInst) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
sym := g.getDictionarySym(gf, targs, isMeth)
// Make (or reuse) a node referencing the dictionary symbol.
@@ -1792,7 +1810,7 @@ func hasShapeTypes(targs []*types.Type) bool {
// getInstInfo get the dictionary format for a function instantiation- type params, derived
// types, and needed subdictionaries and itabs.
-func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
+func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
info := instInfo.dictInfo
info.shapeParams = shapes
@@ -1818,17 +1836,23 @@ func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInf
}
}
+ // Map to remember when we have seen an instantiated function value or method
+ // expression/value as part of a call, so we can determine when we encounter
+ // an uncalled function value or method expression/value.
+ callMap := make(map[ir.Node]bool)
+
var visitFunc func(ir.Node)
visitFunc = func(n ir.Node) {
- if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() {
- if hasShapeNodes(n.(*ir.InstExpr).Targs) {
+ switch n.Op() {
+ case ir.OFUNCINST:
+ if !callMap[n] && hasShapeNodes(n.(*ir.InstExpr).Targs) {
infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
info.subDictCalls = append(info.subDictCalls, n)
}
- } else if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && !n.(*ir.SelectorExpr).Implicit() &&
- !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
- len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
- if hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
+ case ir.OMETHEXPR, ir.OMETHVALUE:
+ if !callMap[n] && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) &&
+ len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
+ hasShapeTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n)
} else {
@@ -1836,44 +1860,48 @@ func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInf
}
info.subDictCalls = append(info.subDictCalls, n)
}
- }
- if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
- n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true)
- if hasShapeNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) {
- infoPrint(" Subdictionary at generic function/method call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n)
+ case ir.OCALL:
+ ce := n.(*ir.CallExpr)
+ if ce.X.Op() == ir.OFUNCINST {
+ callMap[ce.X] = true
+ if hasShapeNodes(ce.X.(*ir.InstExpr).Targs) {
+ infoPrint(" Subdictionary at generic function/method call: %v - %v\n", ce.X.(*ir.InstExpr).X, n)
+ info.subDictCalls = append(info.subDictCalls, n)
+ }
+ }
+ if ce.X.Op() == ir.OXDOT &&
+ isShapeDeref(ce.X.(*ir.SelectorExpr).X.Type()) {
+ callMap[ce.X] = true
+ infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
info.subDictCalls = append(info.subDictCalls, n)
}
- }
- if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH &&
- //n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
- len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
- n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
- if hasShapeTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) {
- infoPrint(" Subdictionary at generic method call: %v\n", n)
- info.subDictCalls = append(info.subDictCalls, n)
+ case ir.OCALLMETH:
+ ce := n.(*ir.CallExpr)
+ if ce.X.Op() == ir.ODOTMETH &&
+ len(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
+ callMap[ce.X] = true
+ if hasShapeTypes(deref(ce.X.(*ir.SelectorExpr).X.Type()).RParams()) {
+ infoPrint(" Subdictionary at generic method call: %v\n", n)
+ info.subDictCalls = append(info.subDictCalls, n)
+ }
}
- }
- if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
- isShapeDeref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()) {
- n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
- infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
- info.subDictCalls = append(info.subDictCalls, n)
- }
- if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() &&
- !n.Type().IsEmptyInterface() &&
- n.(*ir.ConvExpr).X.Type().HasShape() {
- infoPrint(" Itab for interface conv: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
- if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsShape() {
- infoPrint(" Itab for bound call: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
- if (n.Op() == ir.ODOTTYPE || n.Op() == ir.ODOTTYPE2) && !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() {
- infoPrint(" Itab for dot type: %v\n", n)
- info.itabConvs = append(info.itabConvs, n)
- }
- if n.Op() == ir.OCLOSURE {
+ case ir.OCONVIFACE:
+ if n.Type().IsInterface() && !n.Type().IsEmptyInterface() &&
+ n.(*ir.ConvExpr).X.Type().HasShape() {
+ infoPrint(" Itab for interface conv: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+ case ir.OXDOT:
+ if n.(*ir.SelectorExpr).X.Type().IsShape() {
+ infoPrint(" Itab for bound call: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ if !n.(*ir.TypeAssertExpr).Type().IsInterface() && !n.(*ir.TypeAssertExpr).X.Type().IsEmptyInterface() {
+ infoPrint(" Itab for dot type: %v\n", n)
+ info.itabConvs = append(info.itabConvs, n)
+ }
+ case ir.OCLOSURE:
// Visit the closure body and add all relevant entries to the
// dictionary of the outer function (closure will just use
// the dictionary of the outer function).
@@ -1884,18 +1912,21 @@ func (g *irgen) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInf
for _, n := range cfunc.Dcl {
n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
}
- }
- if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
- for _, cc := range n.(*ir.SwitchStmt).Cases {
- for _, c := range cc.List {
- if c.Op() == ir.OTYPE && c.Type().HasShape() {
- // Type switch from a non-empty interface - might need an itab.
- infoPrint(" Itab for type switch: %v\n", c)
- info.itabConvs = append(info.itabConvs, c)
- if info.type2switchType == nil {
- info.type2switchType = map[ir.Node]*types.Type{}
+ case ir.OSWITCH:
+ ss := n.(*ir.SwitchStmt)
+ if ss.Tag != nil && ss.Tag.Op() == ir.OTYPESW &&
+ !ss.Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
+ for _, cc := range ss.Cases {
+ for _, c := range cc.List {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
+ // Type switch from a non-empty interface - might need an itab.
+ infoPrint(" Itab for type switch: %v\n", c)
+ info.itabConvs = append(info.itabConvs, c)
+ if info.type2switchType == nil {
+ info.type2switchType = map[ir.Node]*types.Type{}
+ }
+ info.type2switchType[c] = ss.Tag.(*ir.TypeSwitchGuard).X.Type()
}
- info.type2switchType[c] = n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type()
}
}
}
@@ -2100,7 +2131,7 @@ func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node,
//
// The returned closure is fully substituted and has already had any needed
// transformations done.
-func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
+func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
outer := info.fun
pos := m.Pos()
typ := m.Type() // type of the closure
@@ -2155,5 +2186,5 @@ func (g *irgen) buildClosure2(info *instInfo, m ir.Node) ir.Node {
ir.FinishCaptureNames(pos, outer, fn)
// Do final checks on closure and return it.
- return ir.UseClosure(fn.OClosure, g.target)
+ return ir.UseClosure(fn.OClosure, typecheck.Target)
}
diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go
index 29ee601d82..47e6397206 100644
--- a/src/cmd/compile/internal/noder/transform.go
+++ b/src/cmd/compile/internal/noder/transform.go
@@ -132,9 +132,7 @@ func transformConvCall(n *ir.CallExpr) ir.Node {
// transformCall transforms a normal function/method call. Corresponds to last half
// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even
// in the case of OCALL/OFUNCINST.
-// The dict parameter is used for OCALLINTER nodes to ensure that the called method
-// is retained by the linker.
-func transformCall(n *ir.CallExpr, dict *ir.Name) {
+func transformCall(n *ir.CallExpr) {
// n.Type() can be nil for calls with no return value
assert(n.Typecheck() == 1)
transformArgs(n)
@@ -144,17 +142,6 @@ func transformCall(n *ir.CallExpr, dict *ir.Name) {
switch l.Op() {
case ir.ODOTINTER:
n.SetOp(ir.OCALLINTER)
- if n.X.(*ir.SelectorExpr).X.Type().HasShape() {
- if dict == nil {
- base.Fatalf("calls on shape interfaces need a dictionary reference")
- }
- dict.SetAddrtaken(true)
- // KeepAlive isn't exactly the right thing here, as we only
- // need to keep the dictionary live in the linker-deadcode
- // sense, not the at-runtime sense. But the at-runtime sense
- // is stronger, so it works. See issue 48047.
- n.KeepAlive = append(n.KeepAlive, dict)
- }
case ir.ODOTMETH:
l := l.(*ir.SelectorExpr)
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
index 1a7cef4aa3..f035e0da97 100644
--- a/src/cmd/compile/internal/noder/types.go
+++ b/src/cmd/compile/internal/noder/types.go
@@ -213,7 +213,7 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
}
- return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...))
+ return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...), typ.IsImplicit())
case *types2.TypeParam:
// Save the name of the type parameter in the sym of the type.
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 27e6188ab7..d396c249e4 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -18,6 +18,7 @@ import (
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
+ "cmd/compile/internal/staticdata"
"cmd/compile/internal/typebits"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
@@ -958,11 +959,6 @@ func writeType(t *types.Type) *obj.LSym {
base.Fatalf("unresolved defined type: %v", tbase)
}
- dupok := 0
- if tbase.Sym() == nil || tbase.HasShape() { // TODO(mdempsky): Probably need DUPOK for instantiated types too.
- dupok = obj.DUPOK
- }
-
if !NeedEmit(tbase) {
if i := typecheck.BaseTypeIndex(t); i >= 0 {
lsym.Pkg = tbase.Sym().Pkg.Prefix
@@ -1195,7 +1191,9 @@ func writeType(t *types.Type) *obj.LSym {
}
ot = dextratypeData(lsym, ot, t)
- objw.Global(lsym, int32(ot), int16(dupok|obj.RODATA))
+ objw.Global(lsym, int32(ot), int16(obj.DUPOK|obj.RODATA))
+ // Note: DUPOK is required to ensure that we don't end up with more
+ // than one type descriptor for a given type.
// The linker will leave a table of all the typelinks for
// types in the binary, so the runtime can find them.
@@ -1412,6 +1410,9 @@ func WriteBasicTypes() {
if base.Flag.MSan {
dimportpath(types.NewPkg("runtime/msan", ""))
}
+ if base.Flag.ASan {
+ dimportpath(types.NewPkg("runtime/asan", ""))
+ }
dimportpath(types.NewPkg("main", ""))
}
@@ -1995,8 +1996,33 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
dot := n.X.(*ir.SelectorExpr)
ityp := dot.X.Type()
if ityp.HasShape() {
- base.Fatalf("marking method of shape type used %+v %s", ityp, dot.Sel.Name)
+ // Here we're calling a method on a generic interface. Something like:
+ //
+ // type I[T any] interface { foo() T }
+ // func f[T any](x I[T]) {
+ // ... = x.foo()
+ // }
+ // f[int](...)
+ // f[string](...)
+ //
+ // In this case, in f we're calling foo on a generic interface.
+ // Which method could that be? Normally we could match the method
+ // both by name and by type. But in this case we don't really know
+ // the type of the method we're calling. It could be func()int
+ // or func()string. So we match on just the function name, instead
+ // of both the name and the type used for the non-generic case below.
+ // TODO: instantiations at least know the shape of the instantiated
+ // type, and the linker could do more complicated matching using
+ // some sort of fuzzy shape matching. For now, only use the name
+ // of the method for matching.
+ r := obj.Addrel(ir.CurFunc.LSym)
+ // We use a separate symbol just to tell the linker the method name.
+ // (The symbol itself is not needed in the final binary.)
+ r.Sym = staticdata.StringSym(src.NoXPos, dot.Sel.Name)
+ r.Type = objabi.R_USEGENERICIFACEMETHOD
+ return
}
+
tsym := TypeLinksym(ityp)
r := obj.Addrel(ir.CurFunc.LSym)
r.Sym = tsym
@@ -2007,16 +2033,6 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
r.Type = objabi.R_USEIFACEMETHOD
}
-// MarkUsedIfaceMethodIndex marks that that method number ix (in the AllMethods list)
-// of interface type ityp is used, and should be attached to lsym.
-func MarkUsedIfaceMethodIndex(lsym *obj.LSym, ityp *types.Type, ix int) {
- tsym := TypeLinksym(ityp)
- r := obj.Addrel(lsym)
- r.Sym = tsym
- r.Add = InterfaceMethodOffset(ityp, int64(ix))
- r.Type = objabi.R_USEIFACEMETHOD
-}
-
// getDictionary returns the dictionary for the given named generic function
// or method, with the given type arguments.
func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
index 71ca774431..6ff3188f9b 100644
--- a/src/cmd/compile/internal/ssa/block.go
+++ b/src/cmd/compile/internal/ssa/block.go
@@ -279,7 +279,8 @@ func (b *Block) AddEdgeTo(c *Block) {
// removePred removes the ith input edge from b.
// It is the responsibility of the caller to remove
-// the corresponding successor edge.
+// the corresponding successor edge, and adjust any
+// phi values by calling b.removePhiArg(v, i).
func (b *Block) removePred(i int) {
n := len(b.Preds) - 1
if i != n {
@@ -322,6 +323,28 @@ func (b *Block) swapSuccessors() {
b.Likely *= -1
}
+// removePhiArg removes the ith arg from phi.
+// It must be called after calling b.removePred(i) to
+// adjust the corresponding phi value of the block:
+//
+// b.removePred(i)
+// for _, v := range b.Values {
+// if v.Op != OpPhi {
+// continue
+// }
+// b.removeArg(v, i)
+// }
+func (b *Block) removePhiArg(phi *Value, i int) {
+ n := len(b.Preds)
+ if numPhiArgs := len(phi.Args); numPhiArgs-1 != n {
+ b.Fatalf("inconsistent state, num predecessors: %d, num phi args: %d", n, numPhiArgs)
+ }
+ phi.Args[i].Uses--
+ phi.Args[i] = phi.Args[n]
+ phi.Args[n] = nil
+ phi.Args = phi.Args[:n]
+}
+
// LackingPos indicates whether b is a block whose position should be inherited
// from its successors. This is true if all the values within it have unreliable positions
// and if it is "plain", meaning that there is no control flow that is also very likely
diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go
index 1d34f8160b..be5f9e0a8b 100644
--- a/src/cmd/compile/internal/ssa/branchelim.go
+++ b/src/cmd/compile/internal/ssa/branchelim.go
@@ -22,7 +22,7 @@ import "cmd/internal/src"
func branchelim(f *Func) {
// FIXME: add support for lowering CondSelects on more architectures
switch f.Config.arch {
- case "arm64", "amd64", "wasm":
+ case "arm64", "ppc64le", "ppc64", "amd64", "wasm":
// implemented
default:
return
diff --git a/src/cmd/compile/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go
index b85721eba4..500ce3ae61 100644
--- a/src/cmd/compile/internal/ssa/critical.go
+++ b/src/cmd/compile/internal/ssa/critical.go
@@ -91,14 +91,13 @@ func critical(f *Func) {
b.removePred(i)
// Update corresponding phi args
- n := len(b.Preds)
- phi.Args[i].Uses--
- phi.Args[i] = phi.Args[n]
- phi.Args[n] = nil
- phi.Args = phi.Args[:n]
+ b.removePhiArg(phi, i)
+
// splitting occasionally leads to a phi having
// a single argument (occurs with -N)
- if n == 1 {
+ // TODO(cuonglm,khr): replace this with phielimValue, and
+ // make removePhiArg incorporates that.
+ if len(b.Preds) == 1 {
phi.Op = OpCopy
}
// Don't increment i in this case because we moved
diff --git a/src/cmd/compile/internal/ssa/deadcode.go b/src/cmd/compile/internal/ssa/deadcode.go
index 5d10dfe025..b47b106975 100644
--- a/src/cmd/compile/internal/ssa/deadcode.go
+++ b/src/cmd/compile/internal/ssa/deadcode.go
@@ -348,15 +348,11 @@ func (b *Block) removeEdge(i int) {
c.removePred(j)
// Remove phi args from c's phis.
- n := len(c.Preds)
for _, v := range c.Values {
if v.Op != OpPhi {
continue
}
- v.Args[j].Uses--
- v.Args[j] = v.Args[n]
- v.Args[n] = nil
- v.Args = v.Args[:n]
+ c.removePhiArg(v, j)
phielimValue(v)
// Note: this is trickier than it looks. Replacing
// a Phi with a Copy can in general cause problems because
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 18c9ab696d..a3cea855f2 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -958,6 +958,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc)
}
eltRO := x.regWidth(elt)
+ source.Type = t
for i := int64(0); i < t.NumElem(); i++ {
sel := source.Block.NewValue1I(pos, OpArraySelect, elt, i, source)
mem = x.storeArgOrLoad(pos, b, sel, mem, elt, storeOffset+i*elt.Size(), loadRegOffset, storeRc.at(t, 0))
@@ -991,6 +992,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
return x.storeArgOrLoad(pos, b, source, mem, t, storeOffset, loadRegOffset, storeRc)
}
+ source.Type = t
for i := 0; i < t.NumFields(); i++ {
fld := t.Field(i)
sel := source.Block.NewValue1I(pos, OpStructSelect, fld.Type, int64(i), source)
diff --git a/src/cmd/compile/internal/ssa/fuse_branchredirect.go b/src/cmd/compile/internal/ssa/fuse_branchredirect.go
index 1b8b307bca..751dca7468 100644
--- a/src/cmd/compile/internal/ssa/fuse_branchredirect.go
+++ b/src/cmd/compile/internal/ssa/fuse_branchredirect.go
@@ -78,7 +78,7 @@ func fuseBranchRedirect(f *Func) bool {
if v.Op != OpPhi {
continue
}
- v.RemoveArg(k)
+ b.removePhiArg(v, k)
phielimValue(v)
}
// Fix up child to have one more predecessor.
diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go
index a6da7a5fce..f4c89b0bb3 100644
--- a/src/cmd/compile/internal/ssa/gen/386Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/386Ops.go
@@ -455,7 +455,7 @@ func init() {
},
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index 8b73ee14ea..47a6af003c 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -2219,3 +2219,29 @@
(AND(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSR(Q|L) x)
(BSWAP(Q|L) (BSWAP(Q|L) p)) => p
+
+// CPUID feature: MOVBE.
+(MOV(Q|L)store [i] {s} p x:(BSWAP(Q|L) w) mem) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBE(Q|L)store [i] {s} p w mem)
+(BSWAP(Q|L) x:(MOV(Q|L)load [i] {s} p mem)) && x.Uses == 1 && buildcfg.GOAMD64 >= 3 => (MOVBE(Q|L)load [i] {s} p mem)
+(BSWAP(Q|L) (MOVBE(Q|L)load [i] {s} p m)) => (MOV(Q|L)load [i] {s} p m)
+(MOVBE(Q|L)store [i] {s} p (BSWAP(Q|L) x) m) => (MOV(Q|L)store [i] {s} p x m)
+
+(ORQ x0:(MOVBELload [i0] {s} p mem)
+ sh:(SHLQconst [32] x1:(MOVBELload [i1] {s} p mem)))
+ && i0 == i1+4
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && sh.Uses == 1
+ && mergePoint(b,x0,x1) != nil
+ && clobber(x0, x1, sh)
+ => @mergePoint(b,x0,x1) (MOVBEQload [i1] {s} p mem)
+
+(ORQ x0:(MOVBELload [i] {s} p0 mem)
+ sh:(SHLQconst [32] x1:(MOVBELload [i] {s} p1 mem)))
+ && x0.Uses == 1
+ && x1.Uses == 1
+ && sh.Uses == 1
+ && sequentialAddresses(p1, p0, 4)
+ && mergePoint(b,x0,x1) != nil
+ && clobber(x0, x1, sh)
+ => @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem)
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index 731454c761..a6906bec7c 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -765,7 +765,7 @@ func init() {
// With a register ABI, the actual register info for these instructions (i.e., what is used in regalloc) is augmented with per-call-site bindings of additional arguments to specific in and out registers.
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("DX"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
@@ -922,6 +922,12 @@ func init() {
// and BSFQ(0) is undefined. Same for TZCNTL(0)==32
{name: "TZCNTQ", argLength: 1, reg: gp11, asm: "TZCNTQ", clobberFlags: true},
{name: "TZCNTL", argLength: 1, reg: gp11, asm: "TZCNTL", clobberFlags: true},
+
+ // CPUID feature: MOVBE
+ {name: "MOVBELload", argLength: 2, reg: gpload, asm: "MOVBEL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVBELstore", argLength: 3, reg: gpstore, asm: "MOVBEL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVBEQload", argLength: 2, reg: gpload, asm: "MOVBEQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 8 bytes from arg0+auxint+aux. arg1=mem
+ {name: "MOVBEQstore", argLength: 3, reg: gpstore, asm: "MOVBEQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
}
var AMD64blocks = []blockData{
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
index a4a5b9bdcd..e052ce09f4 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
@@ -492,7 +492,7 @@ func init() {
// function calls
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index 75ba769724..2f004205a5 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -432,7 +432,7 @@ func init() {
// function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R7"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
index 54c0741efd..7b18c42ffb 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
@@ -276,7 +276,7 @@ func init() {
// function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/MIPSOps.go b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
index 5f73e9f2dc..523847badc 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
@@ -258,7 +258,7 @@ func init() {
// function calls
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64.rules b/src/cmd/compile/internal/ssa/gen/PPC64.rules
index 8e42bae215..c3f07a4e22 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64.rules
+++ b/src/cmd/compile/internal/ssa/gen/PPC64.rules
@@ -561,8 +561,10 @@
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(OR x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (ORCC x y) yes no)
((EQ|NE|LT|LE|GT|GE) (CMPconst [0] z:(XOR x y)) yes no) && z.Uses == 1 => ((EQ|NE|LT|LE|GT|GE) (XORCC x y) yes no)
-(CondSelect x y bool) && flagArg(bool) != nil => (ISEL [2] x y bool)
-(CondSelect x y bool) && flagArg(bool) == nil => (ISEL [2] x y (CMPWconst [0] bool))
+// 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))
+// 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)
// Lowering loads
(Load ptr mem) && (is64BitInt(t) || isPtr(t)) => (MOVDload ptr mem)
@@ -848,6 +850,8 @@
(ADDconst [c] (SUBFCconst [d] x)) && is32Bit(c+d) => (SUBFCconst [c+d] x)
(NEG (ADDconst [c] x)) && is32Bit(-c) => (SUBFCconst [-c] x)
(NEG (SUBFCconst [c] x)) && is32Bit(-c) => (ADDconst [-c] x)
+(NEG (SUB x y)) => (SUB y x)
+(NEG (NEG x)) => x
// Use register moves instead of stores and loads to move int<=>float values
// Common with math Float64bits, Float64frombits
@@ -1087,7 +1091,7 @@
((CMP|CMPW|CMPU|CMPWU) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW|CMPU|CMPWU) y x))
// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
-// ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
+// ISEL auxInt values 4=GE 5=LE 6=NE !arg2 ? arg1 : arg0
// ISELB special case where arg0, arg1 values are 0, 1
(Equal cmp) => (ISELB [2] (MOVDconst [1]) cmp)
@@ -1138,6 +1142,9 @@
(ISEL [n] x y (InvertFlags bool)) && n%4 == 0 => (ISEL [n+1] x y bool)
(ISEL [n] x y (InvertFlags bool)) && n%4 == 1 => (ISEL [n-1] x y bool)
(ISEL [n] x y (InvertFlags bool)) && n%4 == 2 => (ISEL [n] x y bool)
+(XORconst [1] (ISELB [6] (MOVDconst [1]) cmp)) => (ISELB [2] (MOVDconst [1]) cmp)
+(XORconst [1] (ISELB [5] (MOVDconst [1]) cmp)) => (ISELB [1] (MOVDconst [1]) cmp)
+(XORconst [1] (ISELB [4] (MOVDconst [1]) cmp)) => (ISELB [0] (MOVDconst [1]) cmp)
// A particular pattern seen in cgo code:
(AND (MOVDconst [c]) x:(MOVBZload _ _)) => (ANDconst [c&0xFF] x)
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index ff9ce64e18..59d8af1a9d 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -396,7 +396,7 @@ func init() {
{name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"},
// ISEL auxInt values 0=LT 1=GT 2=EQ arg2 ? arg0 : arg1
- // ISEL auxInt values 4=GE 5=LE 6=NE arg2 ? arg1 : arg0
+ // ISEL auxInt values 4=GE 5=LE 6=NE !arg2 ? arg1 : arg0
// ISELB special case where arg0, arg1 values are 0, 1 for boolean result
{name: "ISEL", argLength: 3, reg: crgp21, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
{name: "ISELB", argLength: 2, reg: crgp11, asm: "ISEL", aux: "Int32", typ: "Int32"}, // see above
@@ -434,7 +434,7 @@ func init() {
{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
{name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{callptr, ctxt, 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{callptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
index 8a3fdf75f7..076919773b 100644
--- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
@@ -29,6 +29,7 @@ const (
riscv64REG_CTXT = 20
riscv64REG_LR = 1
riscv64REG_SP = 2
+ riscv64REG_GP = 3
riscv64REG_TP = 4
riscv64REG_TMP = 31
riscv64REG_ZERO = 0
@@ -80,8 +81,8 @@ func init() {
// Add general purpose registers to gpMask.
switch r {
- // ZERO, TP and TMP are not in any gp mask.
- case riscv64REG_ZERO, riscv64REG_TP, riscv64REG_TMP:
+ // ZERO, GP, TP and TMP are not in any gp mask.
+ case riscv64REG_ZERO, riscv64REG_GP, riscv64REG_TP, riscv64REG_TMP:
case riscv64REG_G:
gpgMask |= mask
gpspsbgMask |= mask
@@ -240,10 +241,10 @@ func init() {
{name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem
// Calls
- {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: call, aux: "CallOff", call: true}, // tail call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
- {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
+ {name: "CALLstatic", argLength: 1, reg: call, aux: "CallOff", call: true}, // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: call, aux: "CallOff", call: true, tailCall: true}, // tail call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLclosure", argLength: 3, reg: callClosure, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
+ {name: "CALLinter", argLength: 2, reg: callInter, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
// duffzero
// arg0 = address of memory to zero (in X10, changed as side effect)
diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go
index 9b6ac2bfb6..cd7bad7acb 100644
--- a/src/cmd/compile/internal/ssa/gen/S390XOps.go
+++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go
@@ -480,7 +480,7 @@ func init() {
{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go
index 0d7327109a..edfba4ee99 100644
--- a/src/cmd/compile/internal/ssa/gen/WasmOps.go
+++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go
@@ -124,7 +124,7 @@ func init() {
var WasmOps = []opData{
{name: "LoweredStaticCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
- {name: "LoweredTailCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
+ {name: "LoweredTailCall", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
{name: "LoweredClosureCall", argLength: 3, reg: regInfo{inputs: []regMask{gp, gp, 0}, clobbers: callerSave}, aux: "CallOff", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{name: "LoweredInterCall", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index 8e5997b25a..2cf0a919fa 100644
--- a/src/cmd/compile/internal/ssa/gen/main.go
+++ b/src/cmd/compile/internal/ssa/gen/main.go
@@ -63,6 +63,7 @@ type opData struct {
resultNotInArgs bool // outputs must not be allocated to the same registers as inputs
clobberFlags bool // this op clobbers flags register
call bool // is a function call
+ tailCall bool // is a tail call
nilCheck bool // this op is a nil check on arg0
faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset)
faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset)
@@ -307,6 +308,9 @@ func genOp() {
if v.call {
fmt.Fprintln(w, "call: true,")
}
+ if v.tailCall {
+ fmt.Fprintln(w, "tailCall: true,")
+ }
if v.nilCheck {
fmt.Fprintln(w, "nilCheck: true,")
}
@@ -405,6 +409,7 @@ func genOp() {
fmt.Fprintln(w, "func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }")
fmt.Fprintln(w, "func (o Op) IsCall() bool { return opcodeTable[o].call }")
+ fmt.Fprintln(w, "func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }")
fmt.Fprintln(w, "func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }")
fmt.Fprintln(w, "func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }")
fmt.Fprintln(w, "func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }")
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index 6f7b9fa8e9..a1835dcd30 100644
--- a/src/cmd/compile/internal/ssa/op.go
+++ b/src/cmd/compile/internal/ssa/op.go
@@ -34,6 +34,7 @@ type opInfo struct {
resultNotInArgs bool // outputs must not be allocated to the same registers as inputs
clobberFlags bool // this op clobbers flags register
call bool // is a function call
+ tailCall bool // is a tail call
nilCheck bool // this op is a nil check on arg0
faultOnNilArg0 bool // this op will fault if arg0 is nil (and aux encodes a small offset)
faultOnNilArg1 bool // this op will fault if arg1 is nil (and aux encodes a small offset)
@@ -102,6 +103,10 @@ func (a *AuxNameOffset) String() string {
return fmt.Sprintf("%s+%d", a.Name.Sym().Name, a.Offset)
}
+func (a *AuxNameOffset) FrameOffset() int64 {
+ return a.Name.FrameOffset() + a.Offset
+}
+
type AuxCall struct {
Fn *obj.LSym
reg *regInfo // regInfo for this call
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 640e517fe7..2038575b0c 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -1043,6 +1043,10 @@ const (
OpAMD64BLSRL
OpAMD64TZCNTQ
OpAMD64TZCNTL
+ OpAMD64MOVBELload
+ OpAMD64MOVBELstore
+ OpAMD64MOVBEQload
+ OpAMD64MOVBEQstore
OpARMADD
OpARMADDconst
@@ -5944,6 +5948,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
},
@@ -13149,6 +13154,7 @@ var opcodeTable = [...]opInfo{
argLen: -1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 2147483631, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 g R15 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14
},
@@ -13780,6 +13786,66 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "MOVBELload",
+ auxType: auxSymOff,
+ argLen: 2,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMOVBEL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBELstore",
+ auxType: auxSymOff,
+ argLen: 3,
+ faultOnNilArg0: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBEQload",
+ auxType: auxSymOff,
+ argLen: 2,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.AMOVBEQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBEQstore",
+ auxType: auxSymOff,
+ argLen: 3,
+ faultOnNilArg0: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
{
name: "ADD",
@@ -17137,6 +17203,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 4294924287, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 g R12 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
},
@@ -21030,6 +21097,7 @@ var opcodeTable = [...]opInfo{
argLen: -1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 9223372035512336383, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
@@ -22981,6 +23049,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 140737421246462, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 g R31 F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30 HI LO
},
@@ -24550,6 +24619,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 4611686018393833470, // R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 g R31 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 HI LO
},
@@ -27400,6 +27470,7 @@ var opcodeTable = [...]opInfo{
argLen: -1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 576460745860964344, // R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29 g F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26
},
@@ -27878,11 +27949,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AADD,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27893,10 +27964,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AADDI,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27907,10 +27978,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AADDIW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27920,10 +27991,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ANEG,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27933,10 +28004,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ANEGW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27946,11 +28017,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASUB,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27960,11 +28031,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASUBW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27975,11 +28046,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMUL,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -27990,11 +28061,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMULW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28005,11 +28076,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMULH,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28020,11 +28091,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMULHU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28034,12 +28105,12 @@ var opcodeTable = [...]opInfo{
resultNotInArgs: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28049,12 +28120,12 @@ var opcodeTable = [...]opInfo{
resultNotInArgs: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28064,11 +28135,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ADIV,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28078,11 +28149,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ADIVU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28092,11 +28163,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ADIVW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28106,11 +28177,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ADIVUW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28120,11 +28191,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AREM,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28134,11 +28205,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AREMU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28148,11 +28219,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AREMW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28162,11 +28233,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AREMUW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28179,10 +28250,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28194,7 +28265,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28207,10 +28278,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28223,10 +28294,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVH,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28239,10 +28310,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28255,10 +28326,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28271,10 +28342,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVBU,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28287,10 +28358,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVHU,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28303,10 +28374,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVWU,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28319,8 +28390,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28333,8 +28404,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVH,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28347,8 +28418,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28361,8 +28432,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28375,7 +28446,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28388,7 +28459,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVH,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28401,7 +28472,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28414,7 +28485,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28424,10 +28495,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVB,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28437,10 +28508,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVH,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28450,10 +28521,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28463,10 +28534,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28476,10 +28547,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVBU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28489,10 +28560,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVHU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28502,10 +28573,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVWU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28515,10 +28586,10 @@ var opcodeTable = [...]opInfo{
resultInArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28528,11 +28599,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLL,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28542,11 +28613,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASRA,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28556,11 +28627,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASRL,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28571,10 +28642,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLLI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28585,10 +28656,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASRAI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28599,10 +28670,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASRLI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28613,11 +28684,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AXOR,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28628,10 +28699,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AXORI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28642,11 +28713,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AOR,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28657,10 +28728,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AORI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28671,11 +28742,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.AAND,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28686,10 +28757,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AANDI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28699,10 +28770,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ANOT,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28712,10 +28783,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASEQZ,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28725,10 +28796,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASNEZ,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28738,11 +28809,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLT,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28753,10 +28824,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLTI,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28766,11 +28837,11 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLTU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28781,10 +28852,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.ASLTIU,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28794,10 +28865,10 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOV,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28807,16 +28878,17 @@ var opcodeTable = [...]opInfo{
argLen: 1,
call: true,
reg: regInfo{
- clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+ clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
{
- name: "CALLtail",
- auxType: auxCallOff,
- argLen: 1,
- call: true,
+ name: "CALLtail",
+ auxType: auxCallOff,
+ argLen: 1,
+ call: true,
+ tailCall: true,
reg: regInfo{
- clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+ clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
{
@@ -28827,9 +28899,9 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{1, 524288}, // X20
- {0, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
- clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+ clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
{
@@ -28839,9 +28911,9 @@ var opcodeTable = [...]opInfo{
call: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
- clobbers: 9223372035781033972, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
+ clobbers: 9223372035781033968, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
{
@@ -28878,7 +28950,7 @@ var opcodeTable = [...]opInfo{
reg: regInfo{
inputs: []inputInfo{
{0, 16}, // X5
- {1, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {1, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
clobbers: 16, // X5
},
@@ -28893,7 +28965,7 @@ var opcodeTable = [...]opInfo{
inputs: []inputInfo{
{0, 16}, // X5
{1, 32}, // X6
- {2, 1006632884}, // X3 X5 X6 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {2, 1006632880}, // X5 X6 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
clobbers: 112, // X5 X6 X7
},
@@ -28904,10 +28976,10 @@ var opcodeTable = [...]opInfo{
faultOnNilArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28917,10 +28989,10 @@ var opcodeTable = [...]opInfo{
faultOnNilArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28930,10 +29002,10 @@ var opcodeTable = [...]opInfo{
faultOnNilArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28944,8 +29016,8 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28956,8 +29028,8 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28968,8 +29040,8 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {1, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
},
},
@@ -28981,11 +29053,11 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -28997,11 +29069,11 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29014,11 +29086,11 @@ var opcodeTable = [...]opInfo{
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29031,11 +29103,11 @@ var opcodeTable = [...]opInfo{
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29048,12 +29120,12 @@ var opcodeTable = [...]opInfo{
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {2, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {2, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29066,12 +29138,12 @@ var opcodeTable = [...]opInfo{
unsafePoint: true,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {2, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {2, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29083,8 +29155,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AAMOANDW,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
},
},
@@ -29096,8 +29168,8 @@ var opcodeTable = [...]opInfo{
asm: riscv.AAMOORW,
reg: regInfo{
inputs: []inputInfo{
- {1, 1073741812}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
- {0, 9223372037928517622}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
+ {1, 1073741808}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30
+ {0, 9223372037928517618}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 g X28 X29 X30 SB
},
},
},
@@ -29108,7 +29180,7 @@ var opcodeTable = [...]opInfo{
faultOnNilArg0: true,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632950}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632946}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29127,7 +29199,7 @@ var opcodeTable = [...]opInfo{
rematerializeable: true,
reg: regInfo{
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29137,7 +29209,7 @@ var opcodeTable = [...]opInfo{
rematerializeable: true,
reg: regInfo{
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29281,7 +29353,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFMVSX,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29294,7 +29366,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFCVTSW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29307,7 +29379,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFCVTSL,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29323,7 +29395,7 @@ var opcodeTable = [...]opInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29336,7 +29408,7 @@ var opcodeTable = [...]opInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29349,7 +29421,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVF,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29365,7 +29437,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVF,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
@@ -29381,7 +29453,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29396,7 +29468,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29410,7 +29482,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29424,7 +29496,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29609,7 +29681,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFMVDX,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29622,7 +29694,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFCVTDW,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29635,7 +29707,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AFCVTDL,
reg: regInfo{
inputs: []inputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29651,7 +29723,7 @@ var opcodeTable = [...]opInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29664,7 +29736,7 @@ var opcodeTable = [...]opInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29703,7 +29775,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVD,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
},
outputs: []outputInfo{
{0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
@@ -29719,7 +29791,7 @@ var opcodeTable = [...]opInfo{
asm: riscv.AMOVD,
reg: regInfo{
inputs: []inputInfo{
- {0, 9223372037861408758}, // SP X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
+ {0, 9223372037861408754}, // SP X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30 SB
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
},
@@ -29735,7 +29807,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29750,7 +29822,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29764,7 +29836,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -29778,7 +29850,7 @@ var opcodeTable = [...]opInfo{
{1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31
},
outputs: []outputInfo{
- {0, 1006632948}, // X3 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
+ {0, 1006632944}, // X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 X17 X18 X19 X20 X21 X22 X23 X24 X25 X26 X28 X29 X30
},
},
},
@@ -32580,6 +32652,7 @@ var opcodeTable = [...]opInfo{
argLen: 1,
clobberFlags: true,
call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 4294933503, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 g R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
},
@@ -33255,10 +33328,11 @@ var opcodeTable = [...]opInfo{
},
},
{
- name: "LoweredTailCall",
- auxType: auxCallOff,
- argLen: 1,
- call: true,
+ name: "LoweredTailCall",
+ auxType: auxCallOff,
+ argLen: 1,
+ call: true,
+ tailCall: true,
reg: regInfo{
clobbers: 844424930131967, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 g
},
@@ -36826,6 +36900,7 @@ func (o Op) Scale() int16 { return int16(opcodeTable[o].scale) }
func (o Op) String() string { return opcodeTable[o].name }
func (o Op) SymEffect() SymEffect { return opcodeTable[o].symEffect }
func (o Op) IsCall() bool { return opcodeTable[o].call }
+func (o Op) IsTailCall() bool { return opcodeTable[o].tailCall }
func (o Op) HasSideEffects() bool { return opcodeTable[o].hasSideEffects }
func (o Op) UnsafePoint() bool { return opcodeTable[o].unsafePoint }
func (o Op) ResultInArg0() bool { return opcodeTable[o].resultInArg0 }
@@ -37218,34 +37293,34 @@ var linkRegPPC64 = int8(-1)
var registersRISCV64 = [...]Register{
{0, riscv.REG_X0, -1, "X0"},
{1, riscv.REGSP, -1, "SP"},
- {2, riscv.REG_X3, 0, "X3"},
+ {2, riscv.REG_X3, -1, "X3"},
{3, riscv.REG_X4, -1, "X4"},
- {4, riscv.REG_X5, 1, "X5"},
- {5, riscv.REG_X6, 2, "X6"},
- {6, riscv.REG_X7, 3, "X7"},
- {7, riscv.REG_X8, 4, "X8"},
- {8, riscv.REG_X9, 5, "X9"},
- {9, riscv.REG_X10, 6, "X10"},
- {10, riscv.REG_X11, 7, "X11"},
- {11, riscv.REG_X12, 8, "X12"},
- {12, riscv.REG_X13, 9, "X13"},
- {13, riscv.REG_X14, 10, "X14"},
- {14, riscv.REG_X15, 11, "X15"},
- {15, riscv.REG_X16, 12, "X16"},
- {16, riscv.REG_X17, 13, "X17"},
- {17, riscv.REG_X18, 14, "X18"},
- {18, riscv.REG_X19, 15, "X19"},
- {19, riscv.REG_X20, 16, "X20"},
- {20, riscv.REG_X21, 17, "X21"},
- {21, riscv.REG_X22, 18, "X22"},
- {22, riscv.REG_X23, 19, "X23"},
- {23, riscv.REG_X24, 20, "X24"},
- {24, riscv.REG_X25, 21, "X25"},
- {25, riscv.REG_X26, 22, "X26"},
+ {4, riscv.REG_X5, 0, "X5"},
+ {5, riscv.REG_X6, 1, "X6"},
+ {6, riscv.REG_X7, 2, "X7"},
+ {7, riscv.REG_X8, 3, "X8"},
+ {8, riscv.REG_X9, 4, "X9"},
+ {9, riscv.REG_X10, 5, "X10"},
+ {10, riscv.REG_X11, 6, "X11"},
+ {11, riscv.REG_X12, 7, "X12"},
+ {12, riscv.REG_X13, 8, "X13"},
+ {13, riscv.REG_X14, 9, "X14"},
+ {14, riscv.REG_X15, 10, "X15"},
+ {15, riscv.REG_X16, 11, "X16"},
+ {16, riscv.REG_X17, 12, "X17"},
+ {17, riscv.REG_X18, 13, "X18"},
+ {18, riscv.REG_X19, 14, "X19"},
+ {19, riscv.REG_X20, 15, "X20"},
+ {20, riscv.REG_X21, 16, "X21"},
+ {21, riscv.REG_X22, 17, "X22"},
+ {22, riscv.REG_X23, 18, "X23"},
+ {23, riscv.REG_X24, 19, "X24"},
+ {24, riscv.REG_X25, 20, "X25"},
+ {25, riscv.REG_X26, 21, "X26"},
{26, riscv.REGG, -1, "g"},
- {27, riscv.REG_X28, 23, "X28"},
- {28, riscv.REG_X29, 24, "X29"},
- {29, riscv.REG_X30, 25, "X30"},
+ {27, riscv.REG_X28, 22, "X28"},
+ {28, riscv.REG_X29, 23, "X29"},
+ {29, riscv.REG_X30, 24, "X30"},
{30, riscv.REG_X31, -1, "X31"},
{31, riscv.REG_F0, -1, "F0"},
{32, riscv.REG_F1, -1, "F1"},
@@ -37283,7 +37358,7 @@ var registersRISCV64 = [...]Register{
}
var paramIntRegRISCV64 = []int8(nil)
var paramFloatRegRISCV64 = []int8(nil)
-var gpRegMaskRISCV64 = regMask(1006632948)
+var gpRegMaskRISCV64 = regMask(1006632944)
var fpRegMaskRISCV64 = regMask(9223372034707292160)
var specialRegMaskRISCV64 = regMask(0)
var framepointerRegRISCV64 = int8(-1)
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 28fac6ccd0..64792d0c80 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -559,7 +559,8 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, pos
func isLeaf(f *Func) bool {
for _, b := range f.Blocks {
for _, v := range b.Values {
- if opcodeTable[v.Op].call {
+ if v.Op.IsCall() && !v.Op.IsTailCall() {
+ // tail call is not counted as it does not save the return PC or need a frame
return false
}
}
@@ -634,6 +635,8 @@ func (s *regAllocState) init(f *Func) {
// nothing to do
case "ppc64le": // R2 already reserved.
// nothing to do
+ case "riscv64": // X3 (aka GP) and X4 (aka TP) already reserved.
+ // nothing to do
case "s390x":
s.allocatable &^= 1 << 11 // R11
default:
@@ -1840,7 +1843,7 @@ func (s *regAllocState) regalloc(f *Func) {
if s.f.pass.debug > regDebug {
fmt.Printf("delete copied value %s\n", c.LongString())
}
- c.RemoveArg(0)
+ c.resetArgs()
f.freeValue(c)
delete(s.copies, c)
progress = true
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index 2fe0ca64c8..9136c59e65 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -816,7 +816,11 @@ func devirtLECall(v *Value, sym *obj.LSym) *Value {
v.Op = OpStaticLECall
auxcall := v.Aux.(*AuxCall)
auxcall.Fn = sym
- v.RemoveArg(0)
+ // Remove first arg
+ v.Args[0].Uses--
+ copy(v.Args[0:], v.Args[1:])
+ v.Args[len(v.Args)-1] = nil // aid GC
+ v.Args = v.Args[:len(v.Args)-1]
return v
}
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 201fbf2954..0c789d6b49 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -222,6 +222,10 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64LEAQ4(v)
case OpAMD64LEAQ8:
return rewriteValueAMD64_OpAMD64LEAQ8(v)
+ case OpAMD64MOVBELstore:
+ return rewriteValueAMD64_OpAMD64MOVBELstore(v)
+ case OpAMD64MOVBEQstore:
+ return rewriteValueAMD64_OpAMD64MOVBEQstore(v)
case OpAMD64MOVBQSX:
return rewriteValueAMD64_OpAMD64MOVBQSX(v)
case OpAMD64MOVBQSXload:
@@ -3623,6 +3627,43 @@ func rewriteValueAMD64_OpAMD64BSWAPL(v *Value) bool {
v.copyOf(p)
return true
}
+ // match: (BSWAPL x:(MOVLload [i] {s} p mem))
+ // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3
+ // result: (MOVBELload [i] {s} p mem)
+ for {
+ x := v_0
+ if x.Op != OpAMD64MOVLload {
+ break
+ }
+ i := auxIntToInt32(x.AuxInt)
+ s := auxToSym(x.Aux)
+ mem := x.Args[1]
+ p := x.Args[0]
+ if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64MOVBELload)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg2(p, mem)
+ return true
+ }
+ // match: (BSWAPL (MOVBELload [i] {s} p m))
+ // result: (MOVLload [i] {s} p m)
+ for {
+ if v_0.Op != OpAMD64MOVBELload {
+ break
+ }
+ i := auxIntToInt32(v_0.AuxInt)
+ s := auxToSym(v_0.Aux)
+ m := v_0.Args[1]
+ p := v_0.Args[0]
+ v.reset(OpAMD64MOVLload)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg2(p, m)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64BSWAPQ(v *Value) bool {
@@ -3637,6 +3678,43 @@ func rewriteValueAMD64_OpAMD64BSWAPQ(v *Value) bool {
v.copyOf(p)
return true
}
+ // match: (BSWAPQ x:(MOVQload [i] {s} p mem))
+ // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3
+ // result: (MOVBEQload [i] {s} p mem)
+ for {
+ x := v_0
+ if x.Op != OpAMD64MOVQload {
+ break
+ }
+ i := auxIntToInt32(x.AuxInt)
+ s := auxToSym(x.Aux)
+ mem := x.Args[1]
+ p := x.Args[0]
+ if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64MOVBEQload)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg2(p, mem)
+ return true
+ }
+ // match: (BSWAPQ (MOVBEQload [i] {s} p m))
+ // result: (MOVQload [i] {s} p m)
+ for {
+ if v_0.Op != OpAMD64MOVBEQload {
+ break
+ }
+ i := auxIntToInt32(v_0.AuxInt)
+ s := auxToSym(v_0.Aux)
+ m := v_0.Args[1]
+ p := v_0.Args[0]
+ v.reset(OpAMD64MOVQload)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg2(p, m)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64BTCLconst(v *Value) bool {
@@ -9395,6 +9473,52 @@ func rewriteValueAMD64_OpAMD64LEAQ8(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64MOVBELstore(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (MOVBELstore [i] {s} p (BSWAPL x) m)
+ // result: (MOVLstore [i] {s} p x m)
+ for {
+ i := auxIntToInt32(v.AuxInt)
+ s := auxToSym(v.Aux)
+ p := v_0
+ if v_1.Op != OpAMD64BSWAPL {
+ break
+ }
+ x := v_1.Args[0]
+ m := v_2
+ v.reset(OpAMD64MOVLstore)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg3(p, x, m)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64MOVBEQstore(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (MOVBEQstore [i] {s} p (BSWAPQ x) m)
+ // result: (MOVQstore [i] {s} p x m)
+ for {
+ i := auxIntToInt32(v.AuxInt)
+ s := auxToSym(v.Aux)
+ p := v_0
+ if v_1.Op != OpAMD64BSWAPQ {
+ break
+ }
+ x := v_1.Args[0]
+ m := v_2
+ v.reset(OpAMD64MOVQstore)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg3(p, x, m)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64MOVBQSX(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
@@ -12225,6 +12349,28 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value) bool {
v.AddArg3(ptr, val, mem)
return true
}
+ // match: (MOVLstore [i] {s} p x:(BSWAPL w) mem)
+ // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3
+ // result: (MOVBELstore [i] {s} p w mem)
+ for {
+ i := auxIntToInt32(v.AuxInt)
+ s := auxToSym(v.Aux)
+ p := v_0
+ x := v_1
+ if x.Op != OpAMD64BSWAPL {
+ break
+ }
+ w := x.Args[0]
+ mem := v_2
+ if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64MOVBELstore)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg3(p, w, mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool {
@@ -13164,6 +13310,28 @@ func rewriteValueAMD64_OpAMD64MOVQstore(v *Value) bool {
v.AddArg3(ptr, val, mem)
return true
}
+ // match: (MOVQstore [i] {s} p x:(BSWAPQ w) mem)
+ // cond: x.Uses == 1 && buildcfg.GOAMD64 >= 3
+ // result: (MOVBEQstore [i] {s} p w mem)
+ for {
+ i := auxIntToInt32(v.AuxInt)
+ s := auxToSym(v.Aux)
+ p := v_0
+ x := v_1
+ if x.Op != OpAMD64BSWAPQ {
+ break
+ }
+ w := x.Args[0]
+ mem := v_2
+ if !(x.Uses == 1 && buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64MOVBEQstore)
+ v.AuxInt = int32ToAuxInt(i)
+ v.Aux = symToAux(s)
+ v.AddArg3(p, w, mem)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool {
@@ -18657,6 +18825,81 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool {
}
break
}
+ // match: (ORQ x0:(MOVBELload [i0] {s} p mem) sh:(SHLQconst [32] x1:(MOVBELload [i1] {s} p mem)))
+ // cond: i0 == i1+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh)
+ // result: @mergePoint(b,x0,x1) (MOVBEQload [i1] {s} p mem)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ if x0.Op != OpAMD64MOVBELload {
+ continue
+ }
+ i0 := auxIntToInt32(x0.AuxInt)
+ s := auxToSym(x0.Aux)
+ mem := x0.Args[1]
+ p := x0.Args[0]
+ sh := v_1
+ if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 {
+ continue
+ }
+ x1 := sh.Args[0]
+ if x1.Op != OpAMD64MOVBELload {
+ continue
+ }
+ i1 := auxIntToInt32(x1.AuxInt)
+ if auxToSym(x1.Aux) != s {
+ continue
+ }
+ _ = x1.Args[1]
+ if p != x1.Args[0] || mem != x1.Args[1] || !(i0 == i1+4 && x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) {
+ continue
+ }
+ b = mergePoint(b, x0, x1)
+ v0 := b.NewValue0(x1.Pos, OpAMD64MOVBEQload, typ.UInt64)
+ v.copyOf(v0)
+ v0.AuxInt = int32ToAuxInt(i1)
+ v0.Aux = symToAux(s)
+ v0.AddArg2(p, mem)
+ return true
+ }
+ break
+ }
+ // match: (ORQ x0:(MOVBELload [i] {s} p0 mem) sh:(SHLQconst [32] x1:(MOVBELload [i] {s} p1 mem)))
+ // cond: x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p1, p0, 4) && mergePoint(b,x0,x1) != nil && clobber(x0, x1, sh)
+ // result: @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ if x0.Op != OpAMD64MOVBELload {
+ continue
+ }
+ i := auxIntToInt32(x0.AuxInt)
+ s := auxToSym(x0.Aux)
+ mem := x0.Args[1]
+ p0 := x0.Args[0]
+ sh := v_1
+ if sh.Op != OpAMD64SHLQconst || auxIntToInt8(sh.AuxInt) != 32 {
+ continue
+ }
+ x1 := sh.Args[0]
+ if x1.Op != OpAMD64MOVBELload || auxIntToInt32(x1.AuxInt) != i || auxToSym(x1.Aux) != s {
+ continue
+ }
+ _ = x1.Args[1]
+ p1 := x1.Args[0]
+ if mem != x1.Args[1] || !(x0.Uses == 1 && x1.Uses == 1 && sh.Uses == 1 && sequentialAddresses(p1, p0, 4) && mergePoint(b, x0, x1) != nil && clobber(x0, x1, sh)) {
+ continue
+ }
+ b = mergePoint(b, x0, x1)
+ v0 := b.NewValue0(x1.Pos, OpAMD64MOVBEQload, typ.UInt64)
+ v.copyOf(v0)
+ v0.AuxInt = int32ToAuxInt(i)
+ v0.Aux = symToAux(s)
+ v0.AddArg2(p1, mem)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueAMD64_OpAMD64ORQconst(v *Value) bool {
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 1e6624e906..7592b4f505 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -1168,23 +1168,8 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
// match: (CondSelect x y bool)
- // cond: flagArg(bool) != nil
- // result: (ISEL [2] x y bool)
- for {
- x := v_0
- y := v_1
- bool := v_2
- if !(flagArg(bool) != nil) {
- break
- }
- v.reset(OpPPC64ISEL)
- v.AuxInt = int32ToAuxInt(2)
- v.AddArg3(x, y, bool)
- return true
- }
- // match: (CondSelect x y bool)
// cond: flagArg(bool) == nil
- // result: (ISEL [2] x y (CMPWconst [0] bool))
+ // result: (ISEL [6] x y (CMPWconst [0] bool))
for {
x := v_0
y := v_1
@@ -1193,7 +1178,7 @@ func rewriteValuePPC64_OpCondSelect(v *Value) bool {
break
}
v.reset(OpPPC64ISEL)
- v.AuxInt = int32ToAuxInt(2)
+ v.AuxInt = int32ToAuxInt(6)
v0 := b.NewValue0(v.Pos, OpPPC64CMPWconst, types.TypeFlags)
v0.AuxInt = int32ToAuxInt(0)
v0.AddArg(bool)
@@ -5910,6 +5895,28 @@ func rewriteValuePPC64_OpPPC64ISEL(v *Value) bool {
v.AddArg(y)
return true
}
+ // match: (ISEL [6] x y (CMPWconst [0] (ISELB [c] one cmp)))
+ // result: (ISEL [c] x y cmp)
+ for {
+ if auxIntToInt32(v.AuxInt) != 6 {
+ break
+ }
+ x := v_0
+ y := v_1
+ if v_2.Op != OpPPC64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 {
+ break
+ }
+ v_2_0 := v_2.Args[0]
+ if v_2_0.Op != OpPPC64ISELB {
+ break
+ }
+ c := auxIntToInt32(v_2_0.AuxInt)
+ cmp := v_2_0.Args[1]
+ v.reset(OpPPC64ISEL)
+ v.AuxInt = int32ToAuxInt(c)
+ v.AddArg3(x, y, cmp)
+ return true
+ }
// match: (ISEL [2] x _ (FlagEQ))
// result: x
for {
@@ -11372,6 +11379,28 @@ func rewriteValuePPC64_OpPPC64NEG(v *Value) bool {
v.AddArg(x)
return true
}
+ // match: (NEG (SUB x y))
+ // result: (SUB y x)
+ for {
+ if v_0.Op != OpPPC64SUB {
+ break
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ v.reset(OpPPC64SUB)
+ v.AddArg2(y, x)
+ return true
+ }
+ // match: (NEG (NEG x))
+ // result: x
+ for {
+ if v_0.Op != OpPPC64NEG {
+ break
+ }
+ x := v_0.Args[0]
+ v.copyOf(x)
+ return true
+ }
return false
}
func rewriteValuePPC64_OpPPC64NOR(v *Value) bool {
@@ -13912,6 +13941,8 @@ func rewriteValuePPC64_OpPPC64XOR(v *Value) bool {
}
func rewriteValuePPC64_OpPPC64XORconst(v *Value) bool {
v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
// match: (XORconst [c] (XORconst [d] x))
// result: (XORconst [c^d] x)
for {
@@ -13936,6 +13967,60 @@ func rewriteValuePPC64_OpPPC64XORconst(v *Value) bool {
v.copyOf(x)
return true
}
+ // match: (XORconst [1] (ISELB [6] (MOVDconst [1]) cmp))
+ // result: (ISELB [2] (MOVDconst [1]) cmp)
+ for {
+ if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 6 {
+ break
+ }
+ cmp := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = int32ToAuxInt(2)
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(1)
+ v.AddArg2(v0, cmp)
+ return true
+ }
+ // match: (XORconst [1] (ISELB [5] (MOVDconst [1]) cmp))
+ // result: (ISELB [1] (MOVDconst [1]) cmp)
+ for {
+ if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 5 {
+ break
+ }
+ cmp := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = int32ToAuxInt(1)
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(1)
+ v.AddArg2(v0, cmp)
+ return true
+ }
+ // match: (XORconst [1] (ISELB [4] (MOVDconst [1]) cmp))
+ // result: (ISELB [0] (MOVDconst [1]) cmp)
+ for {
+ if auxIntToInt64(v.AuxInt) != 1 || v_0.Op != OpPPC64ISELB || auxIntToInt32(v_0.AuxInt) != 4 {
+ break
+ }
+ cmp := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpPPC64MOVDconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpPPC64ISELB)
+ v.AuxInt = int32ToAuxInt(0)
+ v0 := b.NewValue0(v.Pos, OpPPC64MOVDconst, typ.Int64)
+ v0.AuxInt = int64ToAuxInt(1)
+ v.AddArg2(v0, cmp)
+ return true
+ }
return false
}
func rewriteValuePPC64_OpPanicBounds(v *Value) bool {
diff --git a/src/cmd/compile/internal/ssa/shortcircuit.go b/src/cmd/compile/internal/ssa/shortcircuit.go
index 29abf3c591..c0b9eacf41 100644
--- a/src/cmd/compile/internal/ssa/shortcircuit.go
+++ b/src/cmd/compile/internal/ssa/shortcircuit.go
@@ -196,11 +196,7 @@ func shortcircuitBlock(b *Block) bool {
// Remove b's incoming edge from p.
b.removePred(cidx)
- n := len(b.Preds)
- ctl.Args[cidx].Uses--
- ctl.Args[cidx] = ctl.Args[n]
- ctl.Args[n] = nil
- ctl.Args = ctl.Args[:n]
+ b.removePhiArg(ctl, cidx)
// Redirect p's outgoing edge to t.
p.Succs[pi] = Edge{t, len(t.Preds)}
diff --git a/src/cmd/compile/internal/ssa/stmtlines_test.go b/src/cmd/compile/internal/ssa/stmtlines_test.go
index 843db8c07e..90dd261c55 100644
--- a/src/cmd/compile/internal/ssa/stmtlines_test.go
+++ b/src/cmd/compile/internal/ssa/stmtlines_test.go
@@ -2,7 +2,7 @@ package ssa_test
import (
cmddwarf "cmd/internal/dwarf"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"debug/dwarf"
"debug/elf"
"debug/macho"
@@ -58,7 +58,7 @@ func TestStmtLines(t *testing.T) {
if extld == "" {
extld = "gcc"
}
- extldArgs, err := str.SplitQuotedFields(extld)
+ extldArgs, err := quoted.Split(extld)
if err != nil {
t.Fatal(err)
}
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 630143cc50..7b411a4612 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -302,12 +302,6 @@ func (v *Value) SetArg(i int, w *Value) {
v.Args[i] = w
w.Uses++
}
-func (v *Value) RemoveArg(i int) {
- v.Args[i].Uses--
- copy(v.Args[i:], v.Args[i+1:])
- v.Args[len(v.Args)-1] = nil // aid GC
- v.Args = v.Args[:len(v.Args)-1]
-}
func (v *Value) SetArgs1(a *Value) {
v.resetArgs()
v.AddArg(a)
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 08114b7828..b84199790f 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -108,6 +108,8 @@ func InitConfig() {
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
+ ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread")
+ ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite")
ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject")
ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc")
ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide")
@@ -1245,10 +1247,10 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind)
}
// instrumentFields instruments a read/write operation on addr.
-// If it is instrumenting for MSAN and t is a struct type, it instruments
+// If it is instrumenting for MSAN or ASAN and t is a struct type, it instruments
// operation for each field, instead of for the whole struct.
func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) {
- if !base.Flag.MSan || !t.IsStruct() {
+ if !(base.Flag.MSan || base.Flag.ASan) || !t.IsStruct() {
s.instrument(t, addr, kind)
return
}
@@ -1327,6 +1329,16 @@ func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrume
default:
panic("unreachable")
}
+ } else if base.Flag.ASan {
+ switch kind {
+ case instrumentRead:
+ fn = ir.Syms.Asanread
+ case instrumentWrite:
+ fn = ir.Syms.Asanwrite
+ default:
+ panic("unreachable")
+ }
+ needWidth = true
} else {
panic("unreachable")
}
@@ -3002,7 +3014,7 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
}
// If n is addressable and can't be represented in
// SSA, then load just the selected field. This
- // prevents false memory dependencies in race/msan
+ // prevents false memory dependencies in race/msan/asan
// instrumentation.
if ir.IsAddressable(n) && !s.canSSA(n) {
p := s.addr(n)
@@ -4421,7 +4433,7 @@ func InitTables() {
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpBitLen32, types.Types[types.TINT], args[0])
},
- sys.AMD64, sys.ARM64)
+ sys.AMD64, sys.ARM64, sys.PPC64)
addF("math/bits", "Len32",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
@@ -4430,7 +4442,7 @@ func InitTables() {
x := s.newValue1(ssa.OpZeroExt32to64, types.Types[types.TUINT64], args[0])
return s.newValue1(ssa.OpBitLen64, types.Types[types.TINT], x)
},
- sys.ARM, sys.S390X, sys.MIPS, sys.PPC64, sys.Wasm)
+ sys.ARM, sys.S390X, sys.MIPS, sys.Wasm)
addF("math/bits", "Len16",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if s.config.PtrSize == 4 {
@@ -6717,6 +6729,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
s.livenessMap, s.partLiveArgs = liveness.Compute(e.curfn, f, e.stkptrsize, pp)
emitArgInfo(e, f, pp)
+ argLiveBlockMap, argLiveValueMap := liveness.ArgLiveness(e.curfn, f, pp)
openDeferInfo := e.curfn.LSym.Func().OpenCodedDeferInfo
if openDeferInfo != nil {
@@ -6774,6 +6787,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
// Progs that are in the set above and have that source position.
var inlMarksByPos map[src.XPos][]*obj.Prog
+ var argLiveIdx int = -1 // argument liveness info index
+
// Emit basic blocks
for i, b := range f.Blocks {
s.bstart[b.ID] = s.pp.Next
@@ -6787,6 +6802,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
// preemptible, unless this function is "all unsafe".
s.pp.NextLive = objw.LivenessIndex{StackMapIndex: -1, IsUnsafePoint: liveness.IsUnsafe(f)}
+ if idx, ok := argLiveBlockMap[b.ID]; ok && idx != argLiveIdx {
+ argLiveIdx = idx
+ p := s.pp.Prog(obj.APCDATA)
+ p.From.SetConst(objabi.PCDATA_ArgLiveIndex)
+ p.To.SetConst(int64(idx))
+ }
+
// Emit values in block
Arch.SSAMarkMoves(&s, b)
for _, v := range b.Values {
@@ -6843,6 +6865,13 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
Arch.SSAGenValue(&s, v)
}
+ if idx, ok := argLiveValueMap[v.ID]; ok && idx != argLiveIdx {
+ argLiveIdx = idx
+ p := s.pp.Prog(obj.APCDATA)
+ p.From.SetConst(objabi.PCDATA_ArgLiveIndex)
+ p.To.SetConst(int64(idx))
+ }
+
if base.Ctxt.Flag_locationlists {
valueToProgAfter[v.ID] = s.pp.Next
}
diff --git a/src/cmd/compile/internal/syntax/error_test.go b/src/cmd/compile/internal/syntax/error_test.go
index 0ab3c20ce5..d87e8eaee3 100644
--- a/src/cmd/compile/internal/syntax/error_test.go
+++ b/src/cmd/compile/internal/syntax/error_test.go
@@ -130,7 +130,7 @@ func testSyntaxErrors(t *testing.T, filename string) {
var mode Mode
if strings.HasSuffix(filename, ".go2") {
- mode = AllowGenerics | AllowTypeSets
+ mode = AllowGenerics
}
ParseFile(filename, func(err error) {
e, ok := err.(Error)
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index a669c54712..770175fe54 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -586,35 +586,54 @@ func (p *parser) typeDecl(group *Group) Decl {
d.Pragma = p.takePragma()
d.Name = p.name()
- if p.tok == _Lbrack {
- // array/slice or generic type
+ if p.allowGenerics() && p.tok == _Lbrack {
+ // d.Name "[" ...
+ // array/slice or type parameter list
pos := p.pos()
p.next()
switch p.tok {
- case _Rbrack:
- p.next()
- d.Type = p.sliceType(pos)
case _Name:
- // array or generic type
- p.xnest++
- x := p.expr()
- p.xnest--
- if name0, ok := x.(*Name); p.allowGenerics() && ok && p.tok != _Rbrack {
- // generic type
- d.TParamList = p.paramList(name0, _Rbrack, true)
- pos := p.pos()
- if p.gotAssign() {
- p.syntaxErrorAt(pos, "generic type cannot be alias")
+ // d.Name "[" name ...
+ // array or type parameter list
+ name := p.name()
+ // Index or slice expressions are never constant and thus invalid
+ // array length expressions. Thus, if we see a "[" following name
+ // we can safely assume that "[" name starts a type parameter list.
+ var x Expr // x != nil means x is the array length expression
+ if p.tok != _Lbrack {
+ // d.Name "[" name ...
+ // If we reach here, the next token is not a "[", and we need to
+ // parse the expression starting with name. If that expression is
+ // just that name, not followed by a "]" (in which case we might
+ // have the array length "[" name "]"), we can also safely assume
+ // a type parameter list.
+ p.xnest++
+ // To parse the expression starting with name, expand the call
+ // sequence we would get by passing in name to parser.expr, and
+ // pass in name to parser.pexpr.
+ x = p.binaryExpr(p.pexpr(name, false), 0)
+ p.xnest--
+ if x == name && p.tok != _Rbrack {
+ x = nil
}
+ }
+ if x == nil {
+ // d.Name "[" name ...
+ // type parameter list
+ d.TParamList = p.paramList(name, _Rbrack, true)
+ d.Alias = p.gotAssign()
d.Type = p.typeOrNil()
} else {
+ // d.Name "[" x "]" ...
// x is the array length expression
- if debug && x == nil {
- panic("length expression is nil")
- }
d.Type = p.arrayType(pos, x)
}
+ case _Rbrack:
+ // d.Name "[" "]" ...
+ p.next()
+ d.Type = p.sliceType(pos)
default:
+ // d.Name "[" ...
d.Type = p.arrayType(pos, nil)
}
} else {
@@ -689,15 +708,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
}
f.Name = p.name()
- if p.allowGenerics() && p.got(_Lbrack) {
- if p.tok == _Rbrack {
- p.syntaxError("empty type parameter list")
- p.next()
- } else {
- f.TParamList = p.paramList(nil, _Rbrack, true)
- }
- }
- f.Type = p.funcType()
+ f.TParamList, f.Type = p.funcType("")
if p.tok == _Lbrace {
f.Body = p.funcBody()
}
@@ -729,14 +740,16 @@ func (p *parser) expr() Expr {
defer p.trace("expr")()
}
- return p.binaryExpr(0)
+ return p.binaryExpr(nil, 0)
}
// Expression = UnaryExpr | Expression binary_op Expression .
-func (p *parser) binaryExpr(prec int) Expr {
+func (p *parser) binaryExpr(x Expr, prec int) Expr {
// don't trace binaryExpr - only leads to overly nested trace output
- x := p.unaryExpr()
+ if x == nil {
+ x = p.unaryExpr()
+ }
for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
t := new(Operation)
t.pos = p.pos()
@@ -744,7 +757,7 @@ func (p *parser) binaryExpr(prec int) Expr {
tprec := p.prec
p.next()
t.X = x
- t.Y = p.binaryExpr(tprec)
+ t.Y = p.binaryExpr(nil, tprec)
x = t
}
return x
@@ -839,7 +852,7 @@ func (p *parser) unaryExpr() Expr {
// TODO(mdempsky): We need parens here so we can report an
// error for "(x) := true". It should be possible to detect
// and reject that more efficiently though.
- return p.pexpr(true)
+ return p.pexpr(nil, true)
}
// callStmt parses call-like statements that can be preceded by 'defer' and 'go'.
@@ -853,7 +866,7 @@ func (p *parser) callStmt() *CallStmt {
s.Tok = p.tok // _Defer or _Go
p.next()
- x := p.pexpr(p.tok == _Lparen) // keep_parens so we can report error below
+ x := p.pexpr(nil, p.tok == _Lparen) // keep_parens so we can report error below
if t := unparen(x); t != x {
p.errorAt(x.Pos(), fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
// already progressed, no need to advance
@@ -923,7 +936,7 @@ func (p *parser) operand(keep_parens bool) Expr {
case _Func:
pos := p.pos()
p.next()
- ftyp := p.funcType()
+ _, ftyp := p.funcType("function literal")
if p.tok == _Lbrace {
p.xnest++
@@ -969,12 +982,14 @@ func (p *parser) operand(keep_parens bool) Expr {
// "]" .
// TypeAssertion = "." "(" Type ")" .
// Arguments = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-func (p *parser) pexpr(keep_parens bool) Expr {
+func (p *parser) pexpr(x Expr, keep_parens bool) Expr {
if trace {
defer p.trace("pexpr")()
}
- x := p.operand(keep_parens)
+ if x == nil {
+ x = p.operand(keep_parens)
+ }
loop:
for {
@@ -1261,7 +1276,8 @@ func (p *parser) typeOrNil() Expr {
case _Func:
// fntype
p.next()
- return p.funcType()
+ _, t := p.funcType("function type")
+ return t
case _Lbrack:
// '[' oexpr ']' ntype
@@ -1334,18 +1350,34 @@ func (p *parser) typeInstance(typ Expr) Expr {
return x
}
-func (p *parser) funcType() *FuncType {
+// If context != "", type parameters are not permitted.
+func (p *parser) funcType(context string) ([]*Field, *FuncType) {
if trace {
defer p.trace("funcType")()
}
typ := new(FuncType)
typ.pos = p.pos()
+
+ var tparamList []*Field
+ if p.allowGenerics() && p.got(_Lbrack) {
+ if context != "" {
+ // accept but complain
+ p.syntaxErrorAt(typ.pos, context+" cannot have type parameters")
+ }
+ if p.tok == _Rbrack {
+ p.syntaxError("empty type parameter list")
+ p.next()
+ } else {
+ tparamList = p.paramList(nil, _Rbrack, true)
+ }
+ }
+
p.want(_Lparen)
typ.ParamList = p.paramList(nil, _Rparen, false)
typ.ResultList = p.funcResult()
- return typ
+ return tparamList, typ
}
// "[" has already been consumed, and pos is its position.
@@ -1674,11 +1706,13 @@ func (p *parser) methodDecl() *Field {
// already progressed, no need to advance
}
+ const context = "interface method"
+
switch p.tok {
case _Lparen:
// method
f.Name = name
- f.Type = p.funcType()
+ _, f.Type = p.funcType(context)
case _Lbrack:
if p.allowGenerics() {
@@ -1698,7 +1732,7 @@ func (p *parser) methodDecl() *Field {
// name[](
p.errorAt(pos, "empty type parameter list")
f.Name = name
- f.Type = p.funcType()
+ _, f.Type = p.funcType(context)
} else {
p.errorAt(pos, "empty type argument list")
f.Type = name
@@ -1715,7 +1749,7 @@ func (p *parser) methodDecl() *Field {
// as if [] were absent.
if p.tok == _Lparen {
f.Name = name
- f.Type = p.funcType()
+ _, f.Type = p.funcType(context)
} else {
f.Type = name
}
@@ -1726,7 +1760,7 @@ func (p *parser) methodDecl() *Field {
if list[0].Name != nil {
// generic method
f.Name = name
- f.Type = p.funcType()
+ _, f.Type = p.funcType(context)
// TODO(gri) Record list as type parameter list with f.Type
// if we want to type-check the generic method.
// For now, report an error so this is not a silent event.
@@ -1816,11 +1850,11 @@ func (p *parser) embeddedTerm() Expr {
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
if trace {
- defer p.trace("paramDecl")()
+ defer p.trace("paramDeclOrNil")()
}
// type set notation is ok in type parameter lists
- typeSetsOk := p.mode&AllowTypeSets != 0 && follow == _Rbrack
+ typeSetsOk := follow == _Rbrack
pos := p.pos()
if name != nil {
@@ -1849,6 +1883,11 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field {
// name "[" n "]" E
f.Name = name
}
+ if typeSetsOk && p.tok == _Operator && p.op == Or {
+ // name "[" ... "]" "|" ...
+ // name "[" n "]" E "|" ...
+ f = p.embeddedElem(f)
+ }
return f
}
diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go
index 29682012e5..68f3c376c9 100644
--- a/src/cmd/compile/internal/syntax/parser_test.go
+++ b/src/cmd/compile/internal/syntax/parser_test.go
@@ -26,11 +26,11 @@ var (
)
func TestParse(t *testing.T) {
- ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
+ ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
}
func TestVerify(t *testing.T) {
- ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
+ ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
if err != nil {
return // error already reported
}
@@ -46,7 +46,7 @@ func TestParseGo2(t *testing.T) {
for _, fi := range list {
name := fi.Name()
if !fi.IsDir() && !strings.HasPrefix(name, ".") {
- ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics|AllowTypeSets)
+ ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics)
}
}
}
diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go
index 9b5331b148..604f1fc1ca 100644
--- a/src/cmd/compile/internal/syntax/printer_test.go
+++ b/src/cmd/compile/internal/syntax/printer_test.go
@@ -72,7 +72,7 @@ var stringTests = []string{
"package p; func (*R[A, B, C]) _()",
"package p; func (_ *R[A, B, C]) _()",
- // type constraint literals with elided interfaces (only if AllowTypeSets is set)
+ // type constraint literals with elided interfaces
"package p; func _[P ~int, Q int | string]() {}",
"package p; func _[P struct{f int}, Q *P]() {}",
@@ -94,7 +94,7 @@ var stringTests = []string{
func TestPrintString(t *testing.T) {
for _, want := range stringTests {
- ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics|AllowTypeSets)
+ ast, err := Parse(nil, strings.NewReader(want), nil, nil, AllowGenerics)
if err != nil {
t.Error(err)
continue
diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go
index 49ba87786e..f3d4c09ed5 100644
--- a/src/cmd/compile/internal/syntax/syntax.go
+++ b/src/cmd/compile/internal/syntax/syntax.go
@@ -17,7 +17,6 @@ type Mode uint
const (
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
AllowGenerics
- AllowTypeSets // requires AllowGenerics; remove once #48424 is decided
)
// Error describes a syntax error. Error implements the error interface.
diff --git a/src/cmd/compile/internal/syntax/testdata/issue48382.go2 b/src/cmd/compile/internal/syntax/testdata/issue48382.go2
new file mode 100644
index 0000000000..1e8f4b0ec6
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue48382.go2
@@ -0,0 +1,15 @@
+// Copyright 2021 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 _ func /* ERROR function type cannot have type parameters */ [ /* ERROR empty type parameter list */ ]()
+type _ func /* ERROR function type cannot have type parameters */ [ x /* ERROR missing type constraint */ ]()
+type _ func /* ERROR function type cannot have type parameters */ [P any]()
+
+var _ = func /* ERROR function literal cannot have type parameters */ [P any]() {}
+
+type _ interface{
+ m /* ERROR interface method cannot have type parameters */ [P any]()
+}
diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go2 b/src/cmd/compile/internal/syntax/testdata/typeset.go2
index a173bb1d4f..19b74f28ea 100644
--- a/src/cmd/compile/internal/syntax/testdata/typeset.go2
+++ b/src/cmd/compile/internal/syntax/testdata/typeset.go2
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
// This file contains test cases for typeset-only constraint elements.
-// TODO(gri) gofmt once/if gofmt supports this notation.
package p
@@ -44,8 +43,26 @@ type (
_[_ ~t|struct{}] t
_[_ t|~struct{}] t
_[_ ~t|~struct{}] t
+
+ // test cases for issue #49175
+ _[_ []t]t
+ _[_ [1]t]t
+ _[_ ~[]t]t
+ _[_ ~[1]t]t
+ t [ /* ERROR type parameters must be named */ t[0]]t
)
+// test cases for issue #49174
+func _[_ t]() {}
+func _[_ []t]() {}
+func _[_ [1]t]() {}
+func _[_ []t | t]() {}
+func _[_ [1]t | t]() {}
+func _[_ t | []t]() {}
+func _[_ []t | []t]() {}
+func _[_ [1]t | [1]t]() {}
+func _[_ t[t] | t[t]]() {}
+
// Single-expression type parameter lists and those that don't start
// with a (type parameter) name are considered array sizes.
// The term must be a valid expression (it could be a type - and then
diff --git a/src/cmd/compile/internal/test/abiutils_test.go b/src/cmd/compile/internal/test/abiutils_test.go
index f26cb89c6d..12b4a0c361 100644
--- a/src/cmd/compile/internal/test/abiutils_test.go
+++ b/src/cmd/compile/internal/test/abiutils_test.go
@@ -313,7 +313,7 @@ func TestABIUtilsInterfaces(t *testing.T) {
fldt := mkFuncType(types.FakeRecvType(), []*types.Type{},
[]*types.Type{types.Types[types.TSTRING]})
field := types.NewField(src.NoXPos, typecheck.Lookup("F"), fldt)
- nei := types.NewInterface(types.LocalPkg, []*types.Field{field})
+ nei := types.NewInterface(types.LocalPkg, []*types.Field{field}, false)
i16 := types.Types[types.TINT16]
tb := types.Types[types.TBOOL]
s1 := mkstruct([]*types.Type{i16, i16, tb})
diff --git a/src/cmd/compile/internal/test/inst_test.go b/src/cmd/compile/internal/test/inst_test.go
index 59a67cb545..951f6a05aa 100644
--- a/src/cmd/compile/internal/test/inst_test.go
+++ b/src/cmd/compile/internal/test/inst_test.go
@@ -50,7 +50,9 @@ func TestInst(t *testing.T) {
if output, err = cmd.CombinedOutput(); err != nil {
t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
}
- re := regexp.MustCompile(`\bSort\[.*shape.*\]`)
+ // Look for shape-based instantiation of Sort, but ignore any extra wrapper
+ // ending in "-tramp" (which are created on riscv).
+ re := regexp.MustCompile(`\bSort\[.*shape.*\][^-]`)
r := re.FindAllIndex(output, -1)
if len(r) != 1 {
t.Fatalf("Wanted 1 instantiations of Sort function, got %d\n", len(r))
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 524360e8df..67597cebb4 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -192,6 +192,8 @@ var runtimeDecls = [...]struct {
{"msanread", funcTag, 140},
{"msanwrite", funcTag, 140},
{"msanmove", funcTag, 141},
+ {"asanread", funcTag, 140},
+ {"asanwrite", funcTag, 140},
{"checkptrAlignment", funcTag, 142},
{"checkptrArithmetic", funcTag, 144},
{"libfuzzerTraceCmp1", funcTag, 145},
diff --git a/src/cmd/compile/internal/typecheck/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go
index 66641fb5aa..04ae4f23a3 100644
--- a/src/cmd/compile/internal/typecheck/builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go
@@ -250,6 +250,10 @@ func msanread(addr, size uintptr)
func msanwrite(addr, size uintptr)
func msanmove(dst, src, size uintptr)
+// address sanitizer
+func asanread(addr, size uintptr)
+func asanwrite(addr, size uintptr)
+
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go
index 667e76e130..ae6542d071 100644
--- a/src/cmd/compile/internal/typecheck/crawler.go
+++ b/src/cmd/compile/internal/typecheck/crawler.go
@@ -20,6 +20,7 @@ func crawlExports(exports []*ir.Name) {
p := crawler{
marked: make(map[*types.Type]bool),
embedded: make(map[*types.Type]bool),
+ generic: make(map[*types.Type]bool),
}
for _, n := range exports {
p.markObject(n)
@@ -29,6 +30,7 @@ func crawlExports(exports []*ir.Name) {
type crawler struct {
marked map[*types.Type]bool // types already seen by markType
embedded map[*types.Type]bool // types already seen by markEmbed
+ generic map[*types.Type]bool // types already seen by markGeneric
}
// markObject visits a reachable object (function, method, global type, or global variable)
@@ -168,6 +170,30 @@ func (p *crawler) markEmbed(t *types.Type) {
}
}
+// markGeneric takes an instantiated type or a base generic type t, and
+// marks all the methods of the base generic type of t. If a base generic
+// type is written to export file, even if not explicitly marked for export,
+// all of its methods need to be available for instantiation if needed.
+func (p *crawler) markGeneric(t *types.Type) {
+ if t.IsPtr() {
+ t = t.Elem()
+ }
+ if t.OrigSym() != nil {
+ // Convert to the base generic type.
+ t = t.OrigSym().Def.Type()
+ }
+ if p.generic[t] {
+ return
+ }
+ p.generic[t] = true
+
+ if t.Sym() != nil && t.Kind() != types.TINTER {
+ for _, m := range t.Methods().Slice() {
+ p.markObject(m.Nname.(*ir.Name))
+ }
+ }
+}
+
// markInlBody marks n's inline body for export and recursively
// ensures all called functions are marked too.
func (p *crawler) markInlBody(n *ir.Name) {
@@ -181,7 +207,7 @@ func (p *crawler) markInlBody(n *ir.Name) {
if fn == nil {
base.Fatalf("markInlBody: missing Func on %v", n)
}
- if fn.Inl == nil {
+ if !HaveInlineBody(fn) {
return
}
@@ -195,18 +221,36 @@ func (p *crawler) markInlBody(n *ir.Name) {
var doFlood func(n ir.Node)
doFlood = func(n ir.Node) {
t := n.Type()
- if t != nil && (t.HasTParam() || t.IsFullyInstantiated()) {
- // Ensure that we call markType() on any base generic type
- // that is written to the export file (even if not explicitly
- // marked for export), so we will call markInlBody on its
- // methods, and the methods will be available for
- // instantiation if needed.
- p.markType(t)
+ if t != nil {
+ if t.HasTParam() || t.IsFullyInstantiated() {
+ p.markGeneric(t)
+ }
+ if base.Debug.Unified == 0 {
+ // If a method of un-exported type is promoted and accessible by
+ // embedding in an exported type, it makes that type reachable.
+ //
+ // Example:
+ //
+ // type t struct {}
+ // func (t) M() {}
+ //
+ // func F() interface{} { return struct{ t }{} }
+ //
+ // We generate the wrapper for "struct{ t }".M, and inline call
+ // to "struct{ t }".M, which makes "t.M" reachable.
+ if t.IsStruct() {
+ for _, f := range t.FieldSlice() {
+ if f.Embedded != 0 {
+ p.markEmbed(f.Type)
+ }
+ }
+ }
+ }
}
+
switch n.Op() {
case ir.OMETHEXPR, ir.ODOTMETH:
p.markInlBody(ir.MethodExprName(n))
-
case ir.ONAME:
n := n.(*ir.Name)
switch n.Class {
diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go
index 76fc6de621..68ab05a538 100644
--- a/src/cmd/compile/internal/typecheck/dcl.go
+++ b/src/cmd/compile/internal/typecheck/dcl.go
@@ -360,8 +360,7 @@ func funcargs(nt *ir.FuncType) {
if n.Sym == nil {
// Name so that escape analysis can track it. ~r stands for 'result'.
n.Sym = LookupNum("~r", i)
- }
- if n.Sym.IsBlank() {
+ } else if n.Sym.IsBlank() {
// Give it a name so we can assign to it during return. ~b stands for 'blank'.
// The name must be different from ~r above because if you have
// func f() (_ int)
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 3c0b8bc319..f685851e40 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -97,9 +97,10 @@
//
// // "Automatic" declaration of each typeparam
// type TypeParam struct {
-// Tag byte // 'P'
-// Pos Pos
-// Bound typeOff
+// Tag byte // 'P'
+// Pos Pos
+// Implicit bool
+// Constraint typeOff
// }
//
// typeOff means a uvarint that either indicates a predeclared type,
@@ -108,7 +109,7 @@
// types list (see predeclared in bexport.go for order). Otherwise,
// subtracting predeclReserved yields the offset of a type descriptor.
//
-// Value means a type and type-specific value. See
+// Value means a type, kind, and type-specific value. See
// (*exportWriter).value for details.
//
//
@@ -254,15 +255,16 @@ import (
// Current indexed export format version. Increase with each format change.
// 0: Go1.11 encoding
// 1: added column details to Pos
-// 2: added information for generic function/types (currently unstable)
+// 2: added information for generic function/types. The export of non-generic
+// functions/types remains largely backward-compatible. Breaking changes include:
+// - a 'kind' byte is added to constant values
const (
- iexportVersionGo1_11 = 0
- iexportVersionPosCol = 1
- // TODO: before release, change this back to 2. Kept at previous version
- // for now (for testing).
- iexportVersionGenerics = iexportVersionPosCol
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGenerics = 1 // probably change to 2 before release
+ iexportVersionGo1_18 = 2
- iexportVersionCurrent = iexportVersionGenerics
+ iexportVersionCurrent = 2
)
// predeclReserved is the number of type offsets reserved for types
@@ -561,6 +563,10 @@ func (p *iexporter) doDecl(n *ir.Name) {
// A typeparam has a name, and has a type bound rather
// than an underlying type.
w.pos(n.Pos())
+ if iexportVersionCurrent >= iexportVersionGo1_18 {
+ implicit := n.Type().Bound().IsImplicit()
+ w.bool(implicit)
+ }
w.typ(n.Type().Bound())
break
}
@@ -1137,17 +1143,24 @@ func constTypeOf(typ *types.Type) constant.Kind {
func (w *exportWriter) value(typ *types.Type, v constant.Value) {
w.typ(typ)
+
+ if iexportVersionCurrent >= iexportVersionGo1_18 {
+ w.int64(int64(v.Kind()))
+ }
+
var kind constant.Kind
var valType *types.Type
if typ.IsTypeParam() {
- // A constant will have a TYPEPARAM type if it appears in a place
- // where it must match that typeparam type (e.g. in a binary
- // operation with a variable of that typeparam type). If so, then
- // we must write out its actual constant kind as well, so its
- // constant val can be read in properly during import.
kind = v.Kind()
- w.int64(int64(kind))
+ if iexportVersionCurrent < iexportVersionGo1_18 {
+ // A constant will have a TYPEPARAM type if it appears in a place
+ // where it must match that typeparam type (e.g. in a binary
+ // operation with a variable of that typeparam type). If so, then
+ // we must write out its actual constant kind as well, so its
+ // constant val can be read in properly during import.
+ w.int64(int64(kind))
+ }
switch kind {
case constant.Int:
@@ -1763,6 +1776,19 @@ func (w *exportWriter) expr(n ir.Node) {
w.op(ir.OTYPE)
w.typ(n.Type())
+ case ir.ODYNAMICTYPE:
+ n := n.(*ir.DynamicType)
+ w.op(ir.ODYNAMICTYPE)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ if n.ITab != nil {
+ w.bool(true)
+ w.expr(n.ITab)
+ } else {
+ w.bool(false)
+ }
+ w.typ(n.Type())
+
case ir.OTYPESW:
n := n.(*ir.TypeSwitchGuard)
w.op(ir.OTYPESW)
@@ -1888,6 +1914,14 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.X)
w.typ(n.Type())
+ case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
+ n := n.(*ir.DynamicTypeAssertExpr)
+ w.op(n.Op())
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.expr(n.T)
+ w.typ(n.Type())
+
case ir.OINDEX, ir.OINDEXMAP:
n := n.(*ir.IndexExpr)
if go117ExportTypes {
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index df49d74a40..26bc838ed9 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -81,6 +81,27 @@ func ImportBody(fn *ir.Func) {
inimport = false
}
+// HaveInlineBody reports whether we have fn's inline body available
+// for inlining.
+func HaveInlineBody(fn *ir.Func) bool {
+ if fn.Inl == nil {
+ return false
+ }
+
+ // Unified IR is much more conservative about pruning unreachable
+ // methods (at the cost of increased build artifact size).
+ if base.Debug.Unified != 0 {
+ return true
+ }
+
+ if fn.Inl.Body != nil {
+ return true
+ }
+
+ _, ok := inlineImporter[fn.Nname.Sym()]
+ return ok
+}
+
func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
x, ok := importers[sym]
if !ok {
@@ -118,13 +139,9 @@ func ReadImports(pkg *types.Pkg, data string) {
version := ird.uint64()
switch version {
- case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
- if version > iexportVersionGenerics {
- base.Errorf("import %q: unstable export format version %d, just recompile", pkg.Path, version)
- } else {
- base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
- }
+ base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
base.ErrorExit()
}
@@ -176,7 +193,7 @@ func ReadImports(pkg *types.Pkg, data string) {
}
for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
- s := pkg.Lookup(p.stringAt(ird.uint64()))
+ s := pkg.Lookup(p.nameAt(ird.uint64()))
off := ird.uint64()
if _, ok := DeclImporter[s]; !ok {
@@ -188,18 +205,9 @@ func ReadImports(pkg *types.Pkg, data string) {
// Inline body index.
for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
pkg := p.pkgAt(ird.uint64())
- pkgPrefix := pkg.Prefix + "."
for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
- s2 := p.stringAt(ird.uint64())
- // Function/method instantiation names may include "" to
- // represent the path name of the imported package (in type
- // names), so replace "" with pkg.Prefix. The "" in the names
- // will get replaced by the linker as well, so will not
- // appear in the executable. Include the dot to avoid
- // matching with struct tags ending in '"'.
- s2 = strings.Replace(s2, "\"\".", pkgPrefix, -1)
- s := pkg.Lookup(s2)
+ s := pkg.Lookup(p.nameAt(ird.uint64()))
off := ird.uint64()
if _, ok := inlineImporter[s]; !ok {
@@ -233,6 +241,22 @@ func (p *iimporter) stringAt(off uint64) string {
return p.stringData[spos : spos+slen]
}
+// nameAt is the same as stringAt, except it replaces instances
+// of "" with the path of the package being imported.
+func (p *iimporter) nameAt(off uint64) string {
+ s := p.stringAt(off)
+ // Names of objects (functions, methods, globals) may include ""
+ // to represent the path name of the imported package.
+ // Replace "" with the imported package prefix. This occurs
+ // specifically for generics where the names of instantiations
+ // and dictionaries contain package-qualified type names.
+ // Include the dot to avoid matching with struct tags ending in '"'.
+ if strings.Contains(s, "\"\".") {
+ s = strings.Replace(s, "\"\".", p.ipkg.Prefix+".", -1)
+ }
+ return s
+}
+
func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
if posBase, ok := p.posBaseCache[off]; ok {
return posBase
@@ -288,6 +312,7 @@ func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
}
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+func (r *importReader) name() string { return r.p.nameAt(r.uint64()) }
func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
@@ -400,8 +425,15 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
sym.Def = nname
nname.SetType(t)
t.SetNod(nname)
-
- t.SetBound(r.typ())
+ implicit := false
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ implicit = r.bool()
+ }
+ bound := r.typ()
+ if implicit {
+ bound.MarkImplicit()
+ }
+ t.SetBound(bound)
return nname
case 'V':
@@ -421,10 +453,17 @@ func (r *importReader) value(typ *types.Type) constant.Value {
var kind constant.Kind
var valType *types.Type
- if typ.IsTypeParam() {
- // If a constant had a typeparam type, then we wrote out its
- // actual constant kind as well.
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ // TODO: add support for using the kind in the non-typeparam case.
kind = constant.Kind(r.int64())
+ }
+
+ if typ.IsTypeParam() {
+ if r.p.exportVersion < iexportVersionGo1_18 {
+ // If a constant had a typeparam type, then we wrote out its
+ // actual constant kind as well.
+ kind = constant.Kind(r.int64())
+ }
switch kind {
case constant.Int:
valType = types.Types[types.TINT64]
@@ -539,7 +578,7 @@ func (r *importReader) localIdent() *types.Sym { return r.ident(false) }
func (r *importReader) selector() *types.Sym { return r.ident(true) }
func (r *importReader) qualifiedIdent() *ir.Ident {
- name := r.string()
+ name := r.name()
pkg := r.pkg()
sym := pkg.Lookup(name)
return ir.NewIdent(src.NoXPos, sym)
@@ -807,7 +846,7 @@ func (r *importReader) typ1() *types.Type {
return types.Types[types.TINTER]
}
- t := types.NewInterface(r.currPkg, append(embeddeds, methods...))
+ t := types.NewInterface(r.currPkg, append(embeddeds, methods...), false)
// Ensure we expand the interface in the frontend (#25055).
types.CheckSize(t)
@@ -1304,6 +1343,14 @@ func (r *importReader) node() ir.Node {
case ir.OTYPE:
return ir.TypeNode(r.typ())
+ case ir.ODYNAMICTYPE:
+ n := ir.NewDynamicType(r.pos(), r.expr())
+ if r.bool() {
+ n.ITab = r.expr()
+ }
+ n.SetType(r.typ())
+ return n
+
case ir.OTYPESW:
pos := r.pos()
var tag *ir.Ident
@@ -1457,6 +1504,11 @@ func (r *importReader) node() ir.Node {
}
return n
+ case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
+ n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr())
+ n.SetType(r.typ())
+ return n
+
case ir.OINDEX, ir.OINDEXMAP:
n := ir.NewIndexExpr(r.pos(), r.expr(), r.expr())
if go117ExportTypes {
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 6288d15a01..1986845f64 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -1007,19 +1007,22 @@ func assert(p bool) {
// List of newly fully-instantiated types who should have their methods generated.
var instTypeList []*types.Type
-// NeedInstType adds a new fully-instantied type to instTypeList.
+// NeedInstType adds a new fully-instantiated type to instTypeList.
func NeedInstType(t *types.Type) {
instTypeList = append(instTypeList, t)
}
-// GetInstTypeList returns the current contents of instTypeList, and sets
-// instTypeList to nil.
+// GetInstTypeList returns the current contents of instTypeList.
func GetInstTypeList() []*types.Type {
r := instTypeList
- instTypeList = nil
return r
}
+// ClearInstTypeList clears the contents of instTypeList.
+func ClearInstTypeList() {
+ instTypeList = nil
+}
+
// General type substituter, for replacing typeparams with type args.
type Tsubster struct {
Tparams []*types.Type
@@ -1321,9 +1324,6 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
}
}
if newfields != nil {
- // TODO(danscales): make sure this works for the field
- // names of embedded types (which should keep the name of
- // the type param, not the instantiated type).
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
newfields[i].Embedded = f.Embedded
newfields[i].Note = f.Note
@@ -1369,7 +1369,7 @@ func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type {
// For an empty interface, we need to return a new type,
// since it may now be fully instantiated (HasTParam
// becomes false).
- return types.NewInterface(t.Pkg(), nil)
+ return types.NewInterface(t.Pkg(), nil, false)
}
return t
}
@@ -1390,7 +1390,7 @@ func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type {
}
}
if newfields != nil {
- return types.NewInterface(t.Pkg(), newfields)
+ return types.NewInterface(t.Pkg(), newfields, t.IsImplicit())
}
return t
}
@@ -1421,7 +1421,9 @@ func Shapify(t *types.Type, index int) *types.Type {
// All pointers have the same shape.
// TODO: Make unsafe.Pointer the same shape as normal pointers.
- if u.Kind() == types.TPTR {
+ // Note: pointers to arrays are special because of slice-to-array-pointer
+ // conversions. See issue 49295.
+ if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY {
u = types.Types[types.TUINT8].PtrTo()
}
diff --git a/src/cmd/compile/internal/typecheck/type.go b/src/cmd/compile/internal/typecheck/type.go
index af694c2d94..c4c1ef58ca 100644
--- a/src/cmd/compile/internal/typecheck/type.go
+++ b/src/cmd/compile/internal/typecheck/type.go
@@ -108,7 +108,7 @@ func tcInterfaceType(n *ir.InterfaceType) ir.Node {
methods := tcFields(n.Methods, nil)
base.Pos = lno
- n.SetOTYPE(types.NewInterface(types.LocalPkg, methods))
+ n.SetOTYPE(types.NewInterface(types.LocalPkg, methods, false))
return n
}
diff --git a/src/cmd/compile/internal/types/pkg.go b/src/cmd/compile/internal/types/pkg.go
index f63a357f0d..fe42049cee 100644
--- a/src/cmd/compile/internal/types/pkg.go
+++ b/src/cmd/compile/internal/types/pkg.go
@@ -9,6 +9,7 @@ import (
"cmd/internal/objabi"
"fmt"
"sort"
+ "strings"
"sync"
)
@@ -48,7 +49,13 @@ func NewPkg(path, name string) *Pkg {
p := new(Pkg)
p.Path = path
p.Name = name
- p.Prefix = objabi.PathToPrefix(path)
+ if strings.HasPrefix(path, "go.") {
+ // Special compiler-internal packages don't need to be escaped.
+ // This particularly helps with the go.shape package.
+ p.Prefix = path
+ } else {
+ p.Prefix = objabi.PathToPrefix(path)
+ }
p.Syms = make(map[string]*Sym)
pkgMap[path] = p
diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go
index 7349e52a73..d37c173058 100644
--- a/src/cmd/compile/internal/types/sizeof_test.go
+++ b/src/cmd/compile/internal/types/sizeof_test.go
@@ -26,7 +26,7 @@ func TestSizeof(t *testing.T) {
{Forward{}, 20, 32},
{Func{}, 28, 48},
{Struct{}, 16, 32},
- {Interface{}, 4, 8},
+ {Interface{}, 8, 16},
{Chan{}, 8, 16},
{Array{}, 12, 16},
{FuncArgs{}, 4, 8},
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 392c54ba79..6288df30d6 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -417,7 +417,8 @@ func (t *Type) StructType() *Struct {
// Interface contains Type fields specific to interface types.
type Interface struct {
- pkg *Pkg
+ pkg *Pkg
+ implicit bool
}
// Typeparam contains Type fields specific to typeparam types.
@@ -1820,7 +1821,7 @@ func newBasic(kind Kind, obj Object) *Type {
// NewInterface returns a new interface for the given methods and
// embedded types. Embedded types are specified as fields with no Sym.
-func NewInterface(pkg *Pkg, methods []*Field) *Type {
+func NewInterface(pkg *Pkg, methods []*Field, implicit bool) *Type {
t := newType(TINTER)
t.SetInterface(methods)
for _, f := range methods {
@@ -1838,6 +1839,7 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type {
t.SetBroke(true)
}
t.extra.(*Interface).pkg = pkg
+ t.extra.(*Interface).implicit = implicit
return t
}
@@ -1875,6 +1877,19 @@ func (t *Type) Bound() *Type {
return t.extra.(*Typeparam).bound
}
+// IsImplicit reports whether an interface is implicit (i.e. elided from a type
+// parameter constraint).
+func (t *Type) IsImplicit() bool {
+ t.wantEtype(TINTER)
+ return t.extra.(*Interface).implicit
+}
+
+// MarkImplicit marks the interface as implicit.
+func (t *Type) MarkImplicit() {
+ t.wantEtype(TINTER)
+ t.extra.(*Interface).implicit = true
+}
+
// NewUnion returns a new union with the specified set of terms (types). If
// tildes[i] is true, then terms[i] represents ~T, rather than just T.
func NewUnion(terms []*Type, tildes []bool) *Type {
@@ -2187,4 +2202,4 @@ var (
var SimType [NTYPE]Kind
-var ShapePkg = NewPkg(".shape", ".shape")
+var ShapePkg = NewPkg("go.shape", "go.shape")
diff --git a/src/cmd/compile/internal/types/universe.go b/src/cmd/compile/internal/types/universe.go
index 8fa4b7cd20..d5239eb10c 100644
--- a/src/cmd/compile/internal/types/universe.go
+++ b/src/cmd/compile/internal/types/universe.go
@@ -58,7 +58,7 @@ func InitTypes(defTypeName func(sym *Sym, typ *Type) Object) {
}
Types[TANY] = newType(TANY)
- Types[TINTER] = NewInterface(LocalPkg, nil)
+ Types[TINTER] = NewInterface(LocalPkg, nil, false)
defBasic := func(kind Kind, pkg *Pkg, name string) *Type {
typ := newType(kind)
@@ -111,7 +111,7 @@ func InitTypes(defTypeName func(sym *Sym, typ *Type) Object) {
if base.Flag.G > 0 {
DeferCheckSize()
AnyType = defBasic(TFORW, BuiltinPkg, "any")
- AnyType.SetUnderlying(NewInterface(NoPkg, []*Field{}))
+ AnyType.SetUnderlying(NewInterface(NoPkg, []*Field{}, false))
ResumeCheckSize()
}
@@ -145,11 +145,11 @@ func makeErrorInterface() *Type {
NewField(src.NoXPos, nil, Types[TSTRING]),
})
method := NewField(src.NoXPos, LocalPkg.Lookup("Error"), sig)
- return NewInterface(NoPkg, []*Field{method})
+ return NewInterface(NoPkg, []*Field{method}, false)
}
func makeComparableInterface() *Type {
sig := NewSignature(NoPkg, FakeRecv(), nil, nil, nil)
method := NewField(src.NoXPos, LocalPkg.Lookup("=="), sig)
- return NewInterface(NoPkg, []*Field{method})
+ return NewInterface(NoPkg, []*Field{method}, false)
}
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
index faf570c32e..83c4b02abf 100644
--- a/src/cmd/compile/internal/types2/api.go
+++ b/src/cmd/compile/internal/types2/api.go
@@ -141,12 +141,6 @@ type Config struct {
// TODO(gri) Consolidate error messages and remove this flag.
CompilerErrorMessages bool
- // If AllowTypeLists is set, the type list syntax is permitted
- // in an interface in addition to the type set syntax.
- // TODO(gri) Remove once type lists are no longer supported by
- // the parser.
- AllowTypeLists bool
-
// If go115UsesCgo is set, the type checker expects the
// _cgo_gotypes.go file generated by running cmd/cgo to be
// provided as a package source file. Qualified identifiers
@@ -444,7 +438,7 @@ func AssignableTo(V, T Type) bool {
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
- return x.convertibleTo(nil, T) // check not needed for non-constant x
+ return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
}
// Implements reports whether type V implements interface T.
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
index 4966848ed4..30cfbe0ee4 100644
--- a/src/cmd/compile/internal/types2/api_test.go
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -326,25 +326,25 @@ func TestTypesInfo(t *testing.T) {
{brokenPkg + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`},
// parameterized functions
- {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[generic_p0.T₁ interface{}](generic_p0.T₁)`},
+ {genericPkg + `p0; func f[T any](T) {}; var _ = f[int]`, `f`, `func[T interface{}](T)`},
{genericPkg + `p1; func f[T any](T) {}; var _ = f[int]`, `f[int]`, `func(int)`},
{genericPkg + `p2; func f[T any](T) {}; func _() { f(42) }`, `f`, `func(int)`},
{genericPkg + `p3; func f[T any](T) {}; func _() { f[int](42) }`, `f[int]`, `func(int)`},
- {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[generic_p4.T₁ interface{}](generic_p4.T₁)`},
+ {genericPkg + `p4; func f[T any](T) {}; func _() { f[int](42) }`, `f`, `func[T interface{}](T)`},
{genericPkg + `p5; func f[T any](T) {}; func _() { f(42) }`, `f(42)`, `()`},
// type parameters
{genericPkg + `t0; type t[] int; var _ t`, `t`, `generic_t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
- {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[generic_t1.P₁ interface{}]`},
- {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[generic_t2.P₁ interface{}]`},
- {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[generic_t3.P₁, generic_t3.Q₂ interface{}]`},
- {brokenPkg + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t[broken_t4.P₁, broken_t4.Q₂ interface{m()}]`},
+ {genericPkg + `t1; type t[P any] int; var _ t[int]`, `t`, `generic_t1.t[P interface{}]`},
+ {genericPkg + `t2; type t[P interface{}] int; var _ t[int]`, `t`, `generic_t2.t[P interface{}]`},
+ {genericPkg + `t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `generic_t3.t[P, Q interface{}]`},
+ {brokenPkg + `t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `broken_t4.t[P, Q interface{m()}]`},
// instantiated types must be sanitized
{genericPkg + `g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `generic_g0.t[int]`},
// issue 45096
- {genericPkg + `issue45096; func _[T interface{ ~int8 | ~int16 | ~int32 }](x T) { _ = x < 0 }`, `0`, `generic_issue45096.T₁`},
+ {genericPkg + `issue45096; func _[T interface{ ~int8 | ~int16 | ~int32 }](x T) { _ = x < 0 }`, `0`, `T`},
// issue 47895
{`package p; import "unsafe"; type S struct { f int }; var s S; var _ = unsafe.Offsetof(s.f)`, `s.f`, `int`},
@@ -622,13 +622,6 @@ func TestDefsInfo(t *testing.T) {
{`package p3; type x int`, `x`, `type p3.x int`},
{`package p4; func f()`, `f`, `func p4.f()`},
{`package p5; func f() int { x, _ := 1, 2; return x }`, `_`, `var _ int`},
-
- // generic types must be sanitized
- // (need to use sufficiently nested types to provoke unexpanded types)
- {genericPkg + `g0; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
- {genericPkg + `g1; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
- {genericPkg + `g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
- {genericPkg + `g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var generic_g3.g func(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
@@ -667,13 +660,6 @@ func TestUsesInfo(t *testing.T) {
{`package p2; func _() { _ = x }; var x int`, `x`, `var p2.x int`},
{`package p3; func _() { type _ x }; type x int`, `x`, `type p3.x int`},
{`package p4; func _() { _ = f }; func f()`, `f`, `func p4.f()`},
-
- // generic types must be sanitized
- // (need to use sufficiently nested types to provoke unexpanded types)
- {genericPkg + `g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const generic_g0.x generic_g0.t[int]`},
- {genericPkg + `g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var generic_g1.x generic_g1.t[int]`},
- {genericPkg + `g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type generic_g2.x struct{f generic_g2.t[int]}`},
- {genericPkg + `g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func generic_g3.f(x struct{f generic_g3.t[string]})`},
}
for _, test := range tests {
@@ -1655,6 +1641,7 @@ func TestConvertibleTo(t *testing.T) {
}{
{Typ[Int], Typ[Int], true},
{Typ[Int], Typ[Float32], true},
+ {Typ[Int], Typ[String], true},
{newDefined(Typ[Int]), Typ[Int], true},
{newDefined(new(Struct)), new(Struct), true},
{newDefined(Typ[Int]), new(Struct), false},
@@ -1662,8 +1649,7 @@ func TestConvertibleTo(t *testing.T) {
{NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Int], 10)), true},
{NewSlice(Typ[Int]), NewArray(Typ[Int], 10), false},
{NewSlice(Typ[Int]), NewPointer(NewArray(Typ[Uint], 10)), false},
- // Untyped string values are not permitted by the spec, so the below
- // behavior is undefined.
+ // Untyped string values are not permitted by the spec, so the behavior below is undefined.
{Typ[UntypedString], Typ[String], true},
} {
if got := ConvertibleTo(test.v, test.t); got != test.want {
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index cb4d93c6c4..548d55e10c 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -82,7 +82,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// of S and the respective parameter passing rules apply."
S := x.typ
var T Type
- if s := asSlice(S); s != nil {
+ if s, _ := structure(S).(*Slice); s != nil {
T = s.elem
} else {
check.errorf(x, invalidArg+"%s is not a slice", x)
@@ -101,7 +101,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
if x.mode == invalid {
return
}
- if isString(x.typ) {
+ if allString(x.typ) {
if check.Types != nil {
sig := makeSig(S, S, x.typ)
sig.variadic = true
@@ -291,8 +291,10 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
// the argument types must be of floating-point type
- f := func(x Type) Type {
- if t := asBasic(x); t != nil {
+ // (applyTypeFunc never calls f with a type parameter)
+ f := func(typ Type) Type {
+ assert(asTypeParam(typ) == nil)
+ if t := asBasic(typ); t != nil {
switch t.kind {
case Float32:
return Typ[Complex64]
@@ -325,33 +327,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Copy:
// copy(x, y []T) int
- var dst Type
- if t := asSlice(x.typ); t != nil {
- dst = t.elem
- }
+ dst, _ := structure(x.typ).(*Slice)
var y operand
arg(&y, 1)
if y.mode == invalid {
return
}
- var src Type
- switch t := optype(y.typ).(type) {
- case *Basic:
- if isString(y.typ) {
- src = universeByte
- }
- case *Slice:
- src = t.elem
- }
+ src, _ := structureString(y.typ).(*Slice)
if dst == nil || src == nil {
check.errorf(x, invalidArg+"copy expects slice arguments; found %s and %s", x, &y)
return
}
- if !Identical(dst, src) {
- check.errorf(x, invalidArg+"arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ if !Identical(dst.elem, src.elem) {
+ check.errorf(x, invalidArg+"arguments to copy %s and %s have different element types %s and %s", x, &y, dst.elem, src.elem)
return
}
@@ -424,8 +415,10 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
// the argument must be of complex type
- f := func(x Type) Type {
- if t := asBasic(x); t != nil {
+ // (applyTypeFunc never calls f with a type parameter)
+ f := func(typ Type) Type {
+ assert(asTypeParam(typ) == nil)
+ if t := asBasic(typ); t != nil {
switch t.kind {
case Complex64:
return Typ[Float32]
@@ -471,13 +464,13 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
var min int // minimum number of arguments
- switch optype(T).(type) {
+ switch structure(T).(type) {
case *Slice:
min = 2
case *Map, *Chan:
min = 1
- case *top:
- check.errorf(arg0, invalidArg+"cannot make %s; type parameter has no structural type", arg0)
+ case nil:
+ check.errorf(arg0, invalidArg+"cannot make %s; type set has no single underlying type", arg0)
return
default:
check.errorf(arg0, invalidArg+"cannot make %s; type must be slice, map, or channel", arg0)
@@ -774,6 +767,56 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
return true
}
+// Structure is exported for the compiler.
+
+// If typ is a type parameter, Structure returns the single underlying
+// type of all types in the corresponding type constraint if it exists,
+// or nil otherwise. If typ is not a type parameter, Structure returns
+// the underlying type.
+func Structure(typ Type) Type {
+ return structure(typ)
+}
+
+// If typ is a type parameter, structure returns the single underlying
+// type of all types in the corresponding type constraint if it exists,
+// or nil otherwise. If typ is not a type parameter, structure returns
+// the underlying type.
+func structure(typ Type) Type {
+ var su Type
+ if underIs(typ, func(u Type) bool {
+ if su != nil && !Identical(su, u) {
+ return false
+ }
+ // su == nil || Identical(su, u)
+ su = u
+ return true
+ }) {
+ return su
+ }
+ return nil
+}
+
+// structureString is like structure but also considers []byte and
+// string as "identical". In this case, if successful, the result
+// is always []byte.
+func structureString(typ Type) Type {
+ var su Type
+ if underIs(typ, func(u Type) bool {
+ if isString(u) {
+ u = NewSlice(universeByte)
+ }
+ if su != nil && !Identical(su, u) {
+ return false
+ }
+ // su == nil || Identical(su, u)
+ su = u
+ return true
+ }) {
+ return su
+ }
+ return nil
+}
+
// hasVarSize reports if the size of type t is variable due to type parameters.
func hasVarSize(t Type) bool {
switch t := under(t).(type) {
@@ -787,7 +830,7 @@ func hasVarSize(t Type) bool {
}
case *TypeParam:
return true
- case *Named, *Union, *top:
+ case *Named, *Union:
unreachable()
}
return false
@@ -805,7 +848,10 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
// Test if t satisfies the requirements for the argument
// type and collect possible result types at the same time.
var terms []*Term
- if !tp.iface().typeSet().is(func(t *term) bool {
+ if !tp.is(func(t *term) bool {
+ if t == nil {
+ return false
+ }
if r := f(t.typ); r != nil {
terms = append(terms, NewTerm(t.tilde, r))
return true
@@ -815,13 +861,8 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
return nil
}
- // TODO(gri) Would it be ok to return just the one type
- // if len(rtypes) == 1? What about top-level
- // uses of real() where the result is used to
- // define type and initialize a variable?
-
- // Construct a suitable new type parameter for the sum type. The
- // type param is placed in the current package so export/import
+ // Construct a suitable new type parameter for the result type.
+ // The type parameter is placed in the current package so export/import
// works as expected.
tpar := NewTypeName(nopos, check.pkg, "", nil)
ptyp := check.newTypeParam(tpar, NewInterfaceType(nil, []Type{NewUnion(terms)})) // assigns type to tpar as a side-effect
diff --git a/src/cmd/compile/internal/types2/builtins_test.go b/src/cmd/compile/internal/types2/builtins_test.go
index 52dbba1cb9..be5707cdfe 100644
--- a/src/cmd/compile/internal/types2/builtins_test.go
+++ b/src/cmd/compile/internal/types2/builtins_test.go
@@ -112,15 +112,15 @@ var builtinCalls = []struct {
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
- {"Alignof", `var x P; _ = unsafe.Alignof(x)`, `func(p.P₁) uintptr`},
+ {"Alignof", `var x P; _ = unsafe.Alignof(x)`, `func(P) uintptr`},
{"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
{"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
- {"Offsetof", `var x struct{_ int; f P}; _ = unsafe.Offsetof((&x).f)`, `func(p.P₁) uintptr`},
+ {"Offsetof", `var x struct{_ int; f P}; _ = unsafe.Offsetof((&x).f)`, `func(P) uintptr`},
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
- {"Sizeof", `var x P; _ = unsafe.Sizeof(x)`, `func(p.P₁) uintptr`},
+ {"Sizeof", `var x P; _ = unsafe.Sizeof(x)`, `func(P) uintptr`},
{"Slice", `var p *int; _ = unsafe.Slice(p, 1)`, `func(*int, int) []int`},
{"Slice", `var p *byte; var n uintptr; _ = unsafe.Slice(p, n)`, `func(*byte, uintptr) []byte`},
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index 8b45b28017..49cae5a930 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -57,7 +57,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
}
// instantiate function signature
- res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
+ res := check.instantiateSignature(x.Pos(), sig, targs, poslist)
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
check.recordInstance(inst.X, targs, res)
x.typ = res
@@ -65,6 +65,36 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
x.expr = inst
}
+func (check *Checker) instantiateSignature(pos syntax.Pos, typ *Signature, targs []Type, posList []syntax.Pos) (res *Signature) {
+ assert(check != nil)
+ assert(len(targs) == typ.TypeParams().Len())
+
+ if check.conf.Trace {
+ check.trace(pos, "-- instantiating %s with %s", typ, targs)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(pos, "=> %s (under = %s)", res, res.Underlying())
+ }()
+ }
+
+ inst := check.instance(pos, typ, targs, check.conf.Context).(*Signature)
+ assert(len(posList) <= len(targs))
+ tparams := typ.TypeParams().list()
+ if i, err := check.verify(pos, tparams, targs); err != nil {
+ // best position for error reporting
+ pos := pos
+ if i < len(posList) {
+ pos = posList[i]
+ }
+ check.softErrorf(pos, err.Error())
+ } else {
+ check.mono.recordInstance(check.pkg, pos, tparams, targs, posList)
+ }
+
+ return inst
+}
+
func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
var inst *syntax.IndexExpr // function instantiation, if any
if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
@@ -104,7 +134,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
if x.mode != invalid {
if t := asInterface(T); t != nil {
if !t.IsMethodSet() {
- check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ check.errorf(call, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", T)
break
}
}
@@ -139,7 +169,8 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
// signature may be generic
cgocall := x.mode == cgofunc
- sig := asSignature(x.typ)
+ // a type parameter may be "called" if all types have the same signature
+ sig, _ := structure(x.typ).(*Signature)
if sig == nil {
check.errorf(x, invalidOp+"cannot call non-function %s", x)
x.mode = invalid
@@ -345,7 +376,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
}
// compute result signature
- rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
+ rsig = check.instantiateSignature(call.Pos(), sig, targs, nil)
assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
check.recordInstance(call.Fun, targs, rsig)
diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go
index d89ec3d29f..b9a76a8990 100644
--- a/src/cmd/compile/internal/types2/check.go
+++ b/src/cmd/compile/internal/types2/check.go
@@ -74,6 +74,28 @@ type dotImportKey struct {
name string
}
+// An action describes a (delayed) action.
+type action struct {
+ f func() // action to be executed
+ desc *actionDesc // action description; may be nil, requires debug to be set
+}
+
+// If debug is set, describef sets a printf-formatted description for action a.
+// Otherwise, it is a no-op.
+func (a *action) describef(pos poser, format string, args ...interface{}) {
+ if debug {
+ a.desc = &actionDesc{pos, format, args}
+ }
+}
+
+// An actionDesc provides information on an action.
+// For debugging only.
+type actionDesc struct {
+ pos poser
+ format string
+ args []interface{}
+}
+
// A Checker maintains the state of the type checker.
// It must be created with NewChecker.
type Checker struct {
@@ -104,12 +126,14 @@ type Checker struct {
imports []*PkgName // list of imported packages
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
+ mono monoGraph // graph for detecting non-monomorphizable instantiation loops
firstErr error // first error encountered
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
untyped map[syntax.Expr]exprInfo // map of expressions without final type
- delayed []func() // stack of delayed action segments; segments are processed in FIFO order
+ delayed []action // stack of delayed action segments; segments are processed in FIFO order
objPath []Object // path of object dependencies during type inference (for cycle reporting)
+ defTypes []*Named // defined types created during type checking, for final validation.
// context within which the current object is type-checked
// (valid only for the duration of type-checking a specific object)
@@ -144,8 +168,12 @@ func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode,
// either at the end of the current statement, or in case of a local constant
// or variable declaration, before the constant or variable is in scope
// (so that f still sees the scope before any new declarations).
-func (check *Checker) later(f func()) {
- check.delayed = append(check.delayed, f)
+// later returns the pushed action so one can provide a description
+// via action.describef for debugging, if desired.
+func (check *Checker) later(f func()) *action {
+ i := len(check.delayed)
+ check.delayed = append(check.delayed, action{f: f})
+ return &check.delayed[i]
}
// push pushes obj onto the object path and returns its index in the path.
@@ -259,6 +287,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print := func(msg string) {
if check.conf.Trace {
+ fmt.Println()
fmt.Println(msg)
}
}
@@ -275,6 +304,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print("== processDelayed ==")
check.processDelayed(0) // incl. all functions
+ print("== expandDefTypes ==")
+ check.expandDefTypes()
+
print("== initOrder ==")
check.initOrder()
@@ -286,6 +318,11 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print("== recordUntyped ==")
check.recordUntyped()
+ if check.firstErr == nil {
+ // TODO(mdempsky): Ensure monomorph is safe when errors exist.
+ check.monomorph()
+ }
+
check.pkg.complete = true
// no longer needed - release memory
@@ -294,6 +331,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
check.pkgPathMap = nil
check.seenPkgMap = nil
check.recvTParamMap = nil
+ check.defTypes = nil
// TODO(gri) There's more memory we should release at this point.
@@ -309,12 +347,40 @@ func (check *Checker) processDelayed(top int) {
// add more actions (such as nested functions), so
// this is a sufficiently bounded process.
for i := top; i < len(check.delayed); i++ {
- check.delayed[i]() // may append to check.delayed
+ a := &check.delayed[i]
+ if check.conf.Trace && a.desc != nil {
+ fmt.Println()
+ check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...)
+ }
+ a.f() // may append to check.delayed
}
assert(top <= len(check.delayed)) // stack must not have shrunk
check.delayed = check.delayed[:top]
}
+func (check *Checker) expandDefTypes() {
+ // Ensure that every defined type created in the course of type-checking has
+ // either non-*Named underlying, or is unresolved.
+ //
+ // This guarantees that we don't leak any types whose underlying is *Named,
+ // because any unresolved instances will lazily compute their underlying by
+ // substituting in the underlying of their origin. The origin must have
+ // either been imported or type-checked and expanded here, and in either case
+ // its underlying will be fully expanded.
+ for i := 0; i < len(check.defTypes); i++ {
+ n := check.defTypes[i]
+ switch n.underlying.(type) {
+ case nil:
+ if n.resolver == nil {
+ panic("nil underlying")
+ }
+ case *Named:
+ n.under() // n.under may add entries to check.defTypes
+ }
+ n.check = nil
+ }
+}
+
func (check *Checker) record(x *operand) {
// convert x into a user-friendly set of values
// TODO(gri) this code can be simplified
@@ -364,9 +430,9 @@ func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Ty
}
if mode == constant_ {
assert(val != nil)
- // We check is(typ, IsConstType) here as constant expressions may be
+ // We check allBasic(typ, IsConstType) here as constant expressions may be
// recorded as type parameters.
- assert(typ == Typ[Invalid] || is(typ, IsConstType))
+ assert(typ == Typ[Invalid] || allBasic(typ, IsConstType))
}
if m := check.Types; m != nil {
m[x] = TypeAndValue{mode, typ, val}
diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go
index 5b2f09425b..d4c7b7b39b 100644
--- a/src/cmd/compile/internal/types2/check_test.go
+++ b/src/cmd/compile/internal/types2/check_test.go
@@ -99,8 +99,8 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
}
var mode syntax.Mode
- if strings.HasSuffix(filenames[0], ".go2") {
- mode |= syntax.AllowGenerics | syntax.AllowTypeSets
+ if strings.HasSuffix(filenames[0], ".go2") || manual {
+ mode |= syntax.AllowGenerics
}
// parse files and collect parser errors
files, errlist := parseFiles(t, filenames, mode)
@@ -278,6 +278,7 @@ func TestManual(t *testing.T) {
// TODO(gri) go/types has extra TestLongConstants and TestIndexRepresentability tests
func TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", 75, false) } // TODO(gri) narrow column tolerance
+func TestSpec(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", 0, false) }
func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", 0, false) }
func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", 0, false) }
diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go
index 6c26a4c446..44e8aad84f 100644
--- a/src/cmd/compile/internal/types2/conversions.go
+++ b/src/cmd/compile/internal/types2/conversions.go
@@ -16,32 +16,64 @@ import (
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant_
- var ok bool
- switch {
- case constArg && isConstType(T):
- // constant conversion
+ constConvertibleTo := func(T Type, val *constant.Value) bool {
switch t := asBasic(T); {
- case representableConst(x.val, check, t, &x.val):
- ok = true
+ case t == nil:
+ // nothing to do
+ case representableConst(x.val, check, t, val):
+ return true
case isInteger(x.typ) && isString(t):
codepoint := unicode.ReplacementChar
if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
codepoint = rune(i)
}
- x.val = constant.MakeString(string(codepoint))
- ok = true
+ if val != nil {
+ *val = constant.MakeString(string(codepoint))
+ }
+ return true
}
- case x.convertibleTo(check, T):
+ return false
+ }
+
+ var ok bool
+ var cause string
+ switch {
+ case constArg && isConstType(T):
+ // constant conversion
+ ok = constConvertibleTo(T, &x.val)
+ case constArg && isTypeParam(T):
+ // x is convertible to T if it is convertible
+ // to each specific type in the type set of T.
+ // If T's type set is empty, or if it doesn't
+ // have specific types, constant x cannot be
+ // converted.
+ ok = under(T).(*TypeParam).underIs(func(u Type) bool {
+ // t is nil if there are no specific type terms
+ if u == nil {
+ cause = check.sprintf("%s does not contain specific types", T)
+ return false
+ }
+ if !constConvertibleTo(u, nil) {
+ cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T)
+ return false
+ }
+ return true
+ })
+ x.mode = value // type parameters are not constants
+ case x.convertibleTo(check, T, &cause):
// non-constant conversion
- x.mode = value
ok = true
+ x.mode = value
}
if !ok {
- if x.mode != invalid {
- check.errorf(x, "cannot convert %s to %s", x, T)
- x.mode = invalid
+ var err error_
+ err.errorf(x, "cannot convert %s to %s", x, T)
+ if cause != "" {
+ err.errorf(nopos, cause)
}
+ check.report(&err)
+ x.mode = invalid
return
}
@@ -61,7 +93,7 @@ func (check *Checker) conversion(x *operand, T Type) {
// ok
} else if IsInterface(T) || constArg && !isConstType(T) {
final = Default(x.typ)
- } else if isInteger(x.typ) && isString(T) {
+ } else if isInteger(x.typ) && allString(T) {
final = x.typ
}
check.updateExprType(x.expr, final, true)
@@ -80,16 +112,17 @@ func (check *Checker) conversion(x *operand, T Type) {
// is tricky because we'd have to run updateExprType on the argument first.
// (Issue #21982.)
-// convertibleTo reports whether T(x) is valid.
+// convertibleTo reports whether T(x) is valid. In the failure case, *cause
+// may be set to the cause for the failure.
// The check parameter may be nil if convertibleTo is invoked through an
// exported API call, i.e., when all methods have been type-checked.
-func (x *operand) convertibleTo(check *Checker, T Type) bool {
+func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
// "x is assignable to T"
- if ok, _ := x.assignableTo(check, T, nil); ok {
+ if ok, _ := x.assignableTo(check, T, cause); ok {
return true
}
- // "x's type and T have identical underlying types if tags are ignored"
+ // "V and T have identical underlying types if tags are ignored"
V := x.typ
Vu := under(V)
Tu := under(T)
@@ -97,7 +130,7 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
return true
}
- // "x's type and T are unnamed pointer types and their pointer base types
+ // "V and T are unnamed pointer types and their pointer base types
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
@@ -107,22 +140,22 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
}
}
- // "x's type and T are both integer or floating point types"
+ // "V and T are both integer or floating point types"
if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
return true
}
- // "x's type and T are both complex types"
+ // "V and T are both complex types"
if isComplex(V) && isComplex(T) {
return true
}
- // "x is an integer or a slice of bytes or runes and T is a string type"
+ // "V is an integer or a slice of bytes or runes and T is a string type"
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
return true
}
- // "x is a string and T is a slice of bytes or runes"
+ // "V is a string and T is a slice of bytes or runes"
if isString(V) && isBytesOrRunes(Tu) {
return true
}
@@ -137,7 +170,7 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
return true
}
- // "x is a slice, T is a pointer-to-array type,
+ // "V a slice, T is a pointer-to-array type,
// and the slice and array types have identical element types."
if s := asSlice(V); s != nil {
if p := asPointer(T); p != nil {
@@ -147,20 +180,88 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
return true
}
// check != nil
- if check.conf.CompilerErrorMessages {
- check.error(x, "conversion of slices to array pointers only supported as of -lang=go1.17")
- } else {
- check.error(x, "conversion of slices to array pointers requires go1.17 or later")
+ if cause != nil {
+ if check.conf.CompilerErrorMessages {
+ // compiler error message assumes a -lang flag
+ *cause = "conversion of slices to array pointers only supported as of -lang=go1.17"
+ } else {
+ *cause = "conversion of slices to array pointers requires go1.17 or later"
+ }
}
- x.mode = invalid // avoid follow-up error
+ return false
}
}
}
}
+ // optimization: if we don't have type parameters, we're done
+ Vp, _ := Vu.(*TypeParam)
+ Tp, _ := Tu.(*TypeParam)
+ if Vp == nil && Tp == nil {
+ return false
+ }
+
+ errorf := func(format string, args ...interface{}) {
+ if check != nil && cause != nil {
+ msg := check.sprintf(format, args...)
+ if *cause != "" {
+ msg += "\n\t" + *cause
+ }
+ *cause = msg
+ }
+ }
+
+ // generic cases with specific type terms
+ // (generic operands cannot be constants, so we can ignore x.val)
+ switch {
+ case Vp != nil && Tp != nil:
+ x := *x // don't clobber outer x
+ return Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ return Tp.is(func(T *term) bool {
+ if !x.convertibleTo(check, T.typ, cause) {
+ errorf("cannot convert %s (in %s) to %s (in %s)", V.typ, Vp, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ })
+ case Vp != nil:
+ x := *x // don't clobber outer x
+ return Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ if !x.convertibleTo(check, T, cause) {
+ errorf("cannot convert %s (in %s) to %s", V.typ, Vp, T)
+ return false
+ }
+ return true
+ })
+ case Tp != nil:
+ return Tp.is(func(T *term) bool {
+ if T == nil {
+ return false // no specific types
+ }
+ if !x.convertibleTo(check, T.typ, cause) {
+ errorf("cannot convert %s to %s (in %s)", x.typ, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ }
+
return false
}
+// Helper predicates for convertibleToImpl. The types provided to convertibleToImpl
+// may be type parameters but they won't have specific type terms. Thus it is ok to
+// use the toT convenience converters in the predicates below.
+
func isUintptr(typ Type) bool {
t := asBasic(typ)
return t != nil && t.kind == Uintptr
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
index 26a16d9917..5d2a6c531b 100644
--- a/src/cmd/compile/internal/types2/decl.go
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -330,7 +330,16 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
case *Named:
- t.resolve(check.conf.Context)
+ // If t is parameterized, we should be considering the instantiated (expanded)
+ // form of t, but in general we can't with this algorithm: if t is an invalid
+ // type it may be so because it infinitely expands through a type parameter.
+ // Instantiating such a type would lead to an infinite sequence of instantiations.
+ // In general, we need "type flow analysis" to recognize those cases.
+ // Example: type A[T any] struct{ x A[*T] } (issue #48951)
+ // In this algorithm we always only consider the orginal, uninstantiated type.
+ // This won't recognize some invalid cases with parameterized types, but it
+ // will terminate.
+ t = t.orig
// don't touch the type if it is from a different package or the Universe scope
// (doing so would lead to a race condition - was issue #35049)
@@ -359,7 +368,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
check.cycleError(path[i:])
t.info = invalid
t.underlying = Typ[Invalid]
- return t.info
+ return invalid
}
}
panic("cycle start not found")
@@ -548,7 +557,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(tdecl.Type.Pos(), "using type constraint %s requires go1.18 or later", rhs)
}
- })
+ }).describef(obj, "validType(%s)", obj.Name())
alias := tdecl.Alias
if alias && tdecl.TParamList != nil {
@@ -588,26 +597,19 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
rhs = check.definedType(tdecl.Type, named)
assert(rhs != nil)
named.fromRHS = rhs
- // The underlying type of named may be itself a named type that is
- // incomplete:
- //
- // type (
- // A B
- // B *C
- // C A
- // )
- //
- // The type of C is the (named) type of A which is incomplete,
- // and which has as its underlying type the named type B.
- // Determine the (final, unnamed) underlying type by resolving
- // any forward chain.
- // TODO(gri) Investigate if we can just use named.fromRHS here
- // and rely on lazy computation of the underlying type.
- named.underlying = under(named)
- // If the RHS is a type parameter, it must be from this type declaration.
- if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.TypeParams().list(), tpar) < 0 {
- check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar)
+ // If the underlying was not set while type-checking the right-hand side, it
+ // is invalid and an error should have been reported elsewhere.
+ if named.underlying == nil {
+ named.underlying = Typ[Invalid]
+ }
+
+ // Disallow a lone type parameter as the RHS of a type declaration (issue #45639).
+ // We can look directly at named.underlying because even if it is still a *Named
+ // type (underlying not fully resolved yet) it cannot become a type parameter due
+ // to this very restriction.
+ if tpar, _ := named.underlying.(*TypeParam); tpar != nil {
+ check.error(tdecl.Type, "cannot use a type parameter as RHS in type declaration")
named.underlying = Typ[Invalid]
}
}
@@ -702,7 +704,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
// and field names must be distinct."
base := asNamed(obj.typ) // shouldn't fail but be conservative
if base != nil {
- u := safeUnderlying(base) // base should be expanded, but use safeUnderlying to be conservative
+ u := base.under()
if t, _ := u.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go
index ea43fab178..b56d11a28b 100644
--- a/src/cmd/compile/internal/types2/errors.go
+++ b/src/cmd/compile/internal/types2/errors.go
@@ -61,9 +61,12 @@ func (err *error_) msg(qf Qualifier) string {
for i := range err.desc {
p := &err.desc[i]
if i > 0 {
- fmt.Fprintf(&buf, "\n\t%s: ", p.pos)
+ fmt.Fprint(&buf, "\n\t")
+ if p.pos.IsKnown() {
+ fmt.Fprintf(&buf, "%s: ", p.pos)
+ }
}
- buf.WriteString(sprintf(qf, p.format, p.args...))
+ buf.WriteString(sprintf(qf, false, p.format, p.args...))
}
return buf.String()
}
@@ -82,7 +85,7 @@ func (err *error_) errorf(at poser, format string, args ...interface{}) {
err.desc = append(err.desc, errorDesc{posFor(at), format, args})
}
-func sprintf(qf Qualifier, format string, args ...interface{}) string {
+func sprintf(qf Qualifier, debug bool, format string, args ...interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
@@ -98,7 +101,7 @@ func sprintf(qf Qualifier, format string, args ...interface{}) string {
case Object:
arg = ObjectString(a, qf)
case Type:
- arg = TypeString(a, qf)
+ arg = typeString(a, qf, debug)
}
args[i] = arg
}
@@ -143,7 +146,7 @@ func (check *Checker) markImports(pkg *Package) {
}
func (check *Checker) sprintf(format string, args ...interface{}) string {
- return sprintf(check.qualifier, format, args...)
+ return sprintf(check.qualifier, false, format, args...)
}
func (check *Checker) report(err *error_) {
@@ -157,13 +160,13 @@ func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{})
fmt.Printf("%s:\t%s%s\n",
pos,
strings.Repeat(". ", check.indent),
- check.sprintf(format, args...),
+ sprintf(check.qualifier, true, format, args...),
)
}
// dump is only needed for debugging
func (check *Checker) dump(format string, args ...interface{}) {
- fmt.Println(check.sprintf(format, args...))
+ fmt.Println(sprintf(check.qualifier, true, format, args...))
}
func (check *Checker) err(at poser, msg string, soft bool) {
diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go
index 72a2ce3655..ac73ca4650 100644
--- a/src/cmd/compile/internal/types2/errors_test.go
+++ b/src/cmd/compile/internal/types2/errors_test.go
@@ -19,7 +19,7 @@ func TestError(t *testing.T) {
t.Errorf("simple error: got %q, want %q", got, want)
}
- want = ": foo 42\n\t: bar 43"
+ want = ": foo 42\n\tbar 43"
err.errorf(nopos, "bar %d", 43)
if got := err.String(); got != want {
t.Errorf("simple error: got %q, want %q", got, want)
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 3a3a139156..95b96f2334 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -63,10 +63,10 @@ var unaryOpPredicates opPredicates
func init() {
// Setting unaryOpPredicates in init avoids declaration cycles.
unaryOpPredicates = opPredicates{
- syntax.Add: isNumeric,
- syntax.Sub: isNumeric,
- syntax.Xor: isInteger,
- syntax.Not: isBoolean,
+ syntax.Add: allNumeric,
+ syntax.Sub: allNumeric,
+ syntax.Xor: allInteger,
+ syntax.Not: allBoolean,
}
}
@@ -113,6 +113,8 @@ func (check *Checker) overflow(x *operand) {
// Typed constants must be representable in
// their type after each constant operation.
+ // x.typ cannot be a type parameter (type
+ // parameters cannot be constant types).
if isTyped(x.typ) {
check.representable(x, asBasic(x.typ))
return
@@ -155,6 +157,8 @@ var op2str2 = [...]string{
syntax.Shl: "shift",
}
+// If typ is a type parameter, underIs returns the result of typ.underIs(f).
+// Otherwise, underIs returns the result of f(under(typ)).
func underIs(typ Type, f func(Type) bool) bool {
u := under(typ)
if tpar, _ := u.(*TypeParam); tpar != nil {
@@ -706,10 +710,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion
}
- switch t := under(target).(type) {
+ switch u := under(target).(type) {
case *Basic:
if x.mode == constant_ {
- v, code := check.representation(x, t)
+ v, code := check.representation(x, u)
if code != 0 {
return nil, nil, code
}
@@ -739,7 +743,8 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion
}
case *TypeParam:
- ok := t.underIs(func(t Type) bool {
+ // TODO(gri) review this code - doesn't look quite right
+ ok := u.underIs(func(t Type) bool {
target, _, _ := check.implicitTypeAndValue(x, t)
return target != nil
})
@@ -750,7 +755,7 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
// Update operand types to the default type rather than the target
// (interface) type: values must have concrete dynamic types.
// Untyped nil was handled upfront.
- if !t.Empty() {
+ if !u.Empty() {
return nil, nil, _InvalidUntypedConversion // cannot assign untyped values to non-empty interfaces
}
return Default(x.typ), nil, 0 // default type for nil is nil
@@ -774,7 +779,7 @@ func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
// spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
- defined = isOrdered(x.typ) && isOrdered(y.typ)
+ defined = allOrdered(x.typ) && allOrdered(y.typ)
default:
unreachable()
}
@@ -828,7 +833,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
xval = constant.ToInt(x.val)
}
- if isInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
+ if allInteger(x.typ) || isUntyped(x.typ) && xval != nil && xval.Kind() == constant.Int {
// The lhs is of integer type or an untyped constant representable
// as an integer. Nothing to do.
} else {
@@ -859,11 +864,11 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
x.mode = invalid
return
}
- } else if !isInteger(y.typ) {
+ } else if !allInteger(y.typ) {
check.errorf(y, invalidOp+"shift count %s must be integer", y)
x.mode = invalid
return
- } else if !isUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ } else if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y)
x.mode = invalid
return
@@ -934,7 +939,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
}
// non-constant shift - lhs must be an integer
- if !isInteger(x.typ) {
+ if !allInteger(x.typ) {
check.errorf(x, invalidOp+"shifted operand %s must be integer", x)
x.mode = invalid
return
@@ -948,19 +953,19 @@ var binaryOpPredicates opPredicates
func init() {
// Setting binaryOpPredicates in init avoids declaration cycles.
binaryOpPredicates = opPredicates{
- syntax.Add: isNumericOrString,
- syntax.Sub: isNumeric,
- syntax.Mul: isNumeric,
- syntax.Div: isNumeric,
- syntax.Rem: isInteger,
+ syntax.Add: allNumericOrString,
+ syntax.Sub: allNumeric,
+ syntax.Mul: allNumeric,
+ syntax.Div: allNumeric,
+ syntax.Rem: allInteger,
- syntax.And: isInteger,
- syntax.Or: isInteger,
- syntax.Xor: isInteger,
- syntax.AndNot: isInteger,
+ syntax.And: allInteger,
+ syntax.Or: allInteger,
+ syntax.Xor: allInteger,
+ syntax.AndNot: allInteger,
- syntax.AndAnd: isBoolean,
- syntax.OrOr: isBoolean,
+ syntax.AndAnd: allBoolean,
+ syntax.OrOr: allBoolean,
}
}
@@ -990,10 +995,10 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
if IsInterface(x.typ) || IsInterface(y.typ) {
return true
}
- if isBoolean(x.typ) != isBoolean(y.typ) {
+ if allBoolean(x.typ) != allBoolean(y.typ) {
return false
}
- if isString(x.typ) != isString(y.typ) {
+ if allString(x.typ) != allString(y.typ) {
return false
}
if x.isNil() && !hasNil(y.typ) {
@@ -1025,7 +1030,11 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
- check.errorf(x, invalidOp+"%s (mismatched types %s and %s)", e, x.typ, y.typ)
+ if e != nil {
+ check.errorf(x, invalidOp+"%s (mismatched types %s and %s)", e, x.typ, y.typ)
+ } else {
+ check.errorf(x, invalidOp+"%s %s= %s (mismatched types %s and %s)", lhs, op, rhs, x.typ, y.typ)
+ }
}
x.mode = invalid
return
@@ -1038,7 +1047,7 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
if op == syntax.Div || op == syntax.Rem {
// check for zero divisor
- if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
+ if (x.mode == constant_ || allInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
check.error(&y, invalidOp+"division by zero")
x.mode = invalid
return
@@ -1249,7 +1258,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
goto Error
}
- switch utyp := optype(base).(type) {
+ switch utyp := structure(base).(type) {
case *Struct:
if len(e.ElemList) == 0 {
break
@@ -1463,7 +1472,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
if T == Typ[Invalid] {
goto Error
}
- check.typeAssertion(posFor(x), x, xtyp, T)
+ check.typeAssertion(e, x, xtyp, T, false)
x.mode = commaok
x.typ = T
@@ -1605,26 +1614,32 @@ func keyVal(x constant.Value) interface{} {
}
// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
-func (check *Checker) typeAssertion(pos syntax.Pos, x *operand, xtyp *Interface, T Type) {
+func (check *Checker) typeAssertion(e syntax.Expr, x *operand, xtyp *Interface, T Type, typeSwitch bool) {
method, wrongType := check.assertableTo(xtyp, T)
if method == nil {
return
}
+
var msg string
if wrongType != nil {
if Identical(method.typ, wrongType.typ) {
- msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
+ msg = fmt.Sprintf("%s method has pointer receiver", method.name)
} else {
- msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
+ msg = fmt.Sprintf("wrong type for method %s: have %s, want %s", method.name, wrongType.typ, method.typ)
}
} else {
- msg = "missing method " + method.name
+ msg = fmt.Sprintf("missing %s method", method.name)
}
- if check.conf.CompilerErrorMessages {
- check.errorf(pos, "impossible type assertion: %s (%s)", x, msg)
+
+ var err error_
+ if typeSwitch {
+ err.errorf(e.Pos(), "impossible type switch case: %s", e)
+ err.errorf(nopos, "%s cannot have dynamic type %s (%s)", x, T, msg)
} else {
- check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
+ err.errorf(e.Pos(), "impossible type assertion: %s", e)
+ err.errorf(nopos, "%s does not implement %s (%s)", T, x.typ, msg)
}
+ check.report(&err)
}
// expr typechecks expression e and initializes x with the expression value.
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
index 47a5e50f62..67110704e9 100644
--- a/src/cmd/compile/internal/types2/index.go
+++ b/src/cmd/compile/internal/types2/index.go
@@ -101,77 +101,80 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
case *TypeParam:
// TODO(gri) report detailed failure cause for better error messages
- var tkey, telem Type // tkey != nil if we have maps
+ var key, elem Type // key != nil: we must have all maps
+ mode := variable // non-maps result mode
+ // TODO(gri) factor out closure and use it for non-typeparam cases as well
if typ.underIs(func(u Type) bool {
- var key, elem Type
- alen := int64(-1) // valid if >= 0
+ l := int64(-1) // valid if >= 0
+ var k, e Type // k is only set for maps
switch t := u.(type) {
case *Basic:
- if !isString(t) {
- return false
+ if isString(t) {
+ e = universeByte
+ mode = value
}
- elem = universeByte
case *Array:
- elem = t.elem
- alen = t.len
- case *Pointer:
- a, _ := under(t.base).(*Array)
- if a == nil {
- return false
+ l = t.len
+ e = t.elem
+ if x.mode != variable {
+ mode = value
+ }
+ case *Pointer:
+ if t := asArray(t.base); t != nil {
+ l = t.len
+ e = t.elem
}
- elem = a.elem
- alen = a.len
case *Slice:
- elem = t.elem
+ e = t.elem
case *Map:
- key = t.key
- elem = t.elem
- default:
+ k = t.key
+ e = t.elem
+ }
+ if e == nil {
return false
}
- assert(elem != nil)
- if telem == nil {
+ if elem == nil {
// first type
- tkey, telem = key, elem
- length = alen
- } else {
- // all map keys must be identical (incl. all nil)
- if !Identical(key, tkey) {
- return false
- }
- // all element types must be identical
- if !Identical(elem, telem) {
- return false
- }
- tkey, telem = key, elem
- // track the minimal length for arrays
- if alen >= 0 && alen < length {
- length = alen
- }
+ length = l
+ key, elem = k, e
+ return true
+ }
+ // all map keys must be identical (incl. all nil)
+ // (that is, we cannot mix maps with other types)
+ if !Identical(key, k) {
+ return false
+ }
+ // all element types must be identical
+ if !Identical(elem, e) {
+ return false
+ }
+ // track the minimal length for arrays, if any
+ if l >= 0 && l < length {
+ length = l
}
return true
}) {
// For maps, the index expression must be assignable to the map key type.
- if tkey != nil {
+ if key != nil {
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
return false
}
- var key operand
- check.expr(&key, index)
- check.assignment(&key, tkey, "map index")
+ var k operand
+ check.expr(&k, index)
+ check.assignment(&k, key, "map index")
// ok to continue even if indexing failed - map element type is known
x.mode = mapindex
- x.typ = telem
+ x.typ = elem
x.expr = e
return false
}
// no maps
valid = true
- x.mode = variable
- x.typ = telem
+ x.mode = mode
+ x.typ = elem
}
}
@@ -207,9 +210,14 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ).(type) {
+ switch u := structure(x.typ).(type) {
+ case nil:
+ check.errorf(x, invalidOp+"cannot slice %s: type set has no single underlying type", x)
+ x.mode = invalid
+ return
+
case *Basic:
- if isString(typ) {
+ if isString(u) {
if e.Full {
check.error(x, invalidOp+"3-index slice of string")
x.mode = invalid
@@ -221,26 +229,26 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
}
// spec: "For untyped string operands the result
// is a non-constant value of type string."
- if typ.kind == UntypedString {
+ if u.kind == UntypedString {
x.typ = Typ[String]
}
}
case *Array:
valid = true
- length = typ.len
+ length = u.len
if x.mode != variable {
check.errorf(x, invalidOp+"%s (slice of unaddressable value)", x)
x.mode = invalid
return
}
- x.typ = &Slice{elem: typ.elem}
+ x.typ = &Slice{elem: u.elem}
case *Pointer:
- if typ := asArray(typ.base); typ != nil {
+ if u := asArray(u.base); u != nil {
valid = true
- length = typ.len
- x.typ = &Slice{elem: typ.elem}
+ length = u.len
+ x.typ = &Slice{elem: u.elem}
}
case *Slice:
@@ -378,7 +386,7 @@ func (check *Checker) isValidIndex(x *operand, what string, allowNegative bool)
}
// spec: "the index x must be of integer type or an untyped constant"
- if !isInteger(x.typ) {
+ if !allInteger(x.typ) {
check.errorf(x, invalidArg+"%s %s must be integer", what, x)
return false
}
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index ad8c6ac412..24c461f1c3 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -218,7 +218,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
// At least one type argument couldn't be inferred.
assert(targs != nil && index >= 0 && targs[index] == nil)
tpar := tparams[index]
- check.errorf(pos, "cannot infer %s (%s) (%s)", tpar.obj.name, tpar.obj.pos, targs)
+ check.errorf(pos, "cannot infer %s (%s)", tpar.obj.name, tpar.obj.pos)
return nil
}
@@ -275,7 +275,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}()
switch t := typ.(type) {
- case nil, *top, *Basic: // TODO(gri) should nil be handled here?
+ case nil, *Basic: // TODO(gri) should nil be handled here?
break
case *Array:
@@ -320,7 +320,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}
}
return tset.is(func(t *term) bool {
- return w.isParameterized(t.typ)
+ return t != nil && w.isParameterized(t.typ)
})
case *Map:
@@ -363,7 +363,7 @@ func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type, index int) {
assert(len(tparams) >= len(targs) && len(targs) > 0)
- // Setup bidirectional unification between those structural bounds
+ // Setup bidirectional unification between constraints
// and the corresponding type arguments (which may be nil!).
u := newUnifier(false)
u.x.init(tparams)
@@ -376,20 +376,26 @@ func (check *Checker) inferB(tparams []*TypeParam, targs []Type) (types []Type,
}
}
- // Unify type parameters with their structural constraints, if any.
+ // If a constraint has a structural type, unify the corresponding type parameter with it.
for _, tpar := range tparams {
- typ := tpar
- sbound := typ.structuralType()
+ sbound := structure(tpar)
if sbound != nil {
- if !u.unify(typ, sbound) {
- check.errorf(tpar.obj, "%s does not match %s", tpar.obj, sbound)
+ // If the structural type is the underlying type of a single
+ // defined type in the constraint, use that defined type instead.
+ if named, _ := tpar.singleType().(*Named); named != nil {
+ sbound = named
+ }
+ if !u.unify(tpar, sbound) {
+ // TODO(gri) improve error message by providing the type arguments
+ // which we know already
+ check.errorf(tpar.obj, "%s does not match %s", tpar, sbound)
return nil, 0
}
}
}
// u.x.types() now contains the incoming type arguments plus any additional type
- // arguments for which there were structural constraints. The newly inferred non-
+ // arguments which were inferred from structural types. The newly inferred non-
// nil entries may still contain references to other type parameters.
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
// was given, unification produced the type list [int, []C, *A]. We eliminate the
@@ -504,7 +510,7 @@ func (w *cycleFinder) typ(typ Type) {
defer delete(w.seen, typ)
switch t := typ.(type) {
- case *Basic, *top:
+ case *Basic:
// nothing to do
case *Array:
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 5da371f201..44cf593ffb 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -49,56 +49,6 @@ func Instantiate(ctxt *Context, typ Type, targs []Type, validate bool) (Type, er
return inst, err
}
-// instantiate creates an instance and defers verification of constraints to
-// later in the type checking pass. For Named types the resulting instance will
-// be unexpanded.
-func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posList []syntax.Pos) (res Type) {
- assert(check != nil)
- if check.conf.Trace {
- check.trace(pos, "-- instantiating %s with %s", typ, NewTypeList(targs))
- check.indent++
- defer func() {
- check.indent--
- var under Type
- if res != nil {
- // Calling under() here may lead to endless instantiations.
- // Test case: type T[P any] T[P]
- // TODO(gri) investigate if that's a bug or to be expected.
- under = safeUnderlying(res)
- }
- check.trace(pos, "=> %s (under = %s)", res, under)
- }()
- }
-
- inst := check.instance(pos, typ, targs, check.conf.Context)
-
- assert(len(posList) <= len(targs))
- check.later(func() {
- // Collect tparams again because lazily loaded *Named types may not have
- // had tparams set up above.
- var tparams []*TypeParam
- switch t := typ.(type) {
- case *Named:
- tparams = t.TypeParams().list()
- case *Signature:
- tparams = t.TypeParams().list()
- }
- // Avoid duplicate errors; instantiate will have complained if tparams
- // and targs do not have the same length.
- if len(tparams) == len(targs) {
- if i, err := check.verify(pos, tparams, targs); err != nil {
- // best position for error reporting
- pos := pos
- if i < len(posList) {
- pos = posList[i]
- }
- check.softErrorf(pos, err.Error())
- }
- }
- })
- return inst
-}
-
// instance creates a type or function instance using the given original type
// typ and arguments targs. For Named types the resulting instance will be
// unexpanded.
@@ -115,7 +65,7 @@ func (check *Checker) instance(pos syntax.Pos, typ Type, targs []Type, ctxt *Con
}
}
tname := NewTypeName(pos, t.obj.pkg, t.obj.name, nil)
- named := check.newNamed(tname, t, nil, nil, nil) // methods and tparams are set when named is resolved
+ named := check.newNamed(tname, t, nil, nil, nil) // underlying, tparams, and methods are set when named is resolved
named.targs = NewTypeList(targs)
named.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
return expandNamed(ctxt, n, pos)
@@ -185,8 +135,16 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type)
// TODO(gri) This should be a method of interfaces or type sets.
func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap substMap) error {
iface := tpar.iface()
+
+ // Every type argument satisfies interface{}.
if iface.Empty() {
- return nil // no type bound
+ return nil
+ }
+
+ // A type argument that is a type parameter with an empty type set satisfies any constraint.
+ // (The empty set is a subset of any set.)
+ if targ := asTypeParam(targ); targ != nil && targ.iface().typeSet().IsEmpty() {
+ return nil
}
// TODO(rfindley): it would be great if users could pass in a qualifier here,
@@ -197,7 +155,12 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
qf = check.qualifier
}
errorf := func(format string, args ...interface{}) error {
- return errors.New(sprintf(qf, format, args...))
+ return errors.New(sprintf(qf, false, format, args...))
+ }
+
+ // No type argument with non-empty type set satisfies the empty type set.
+ if iface.typeSet().IsEmpty() {
+ return errorf("%s does not satisfy %s (constraint type set is empty)", targ, tpar.bound)
}
// The type parameter bound is parameterized with the same type parameters
@@ -240,28 +203,27 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
}
}
- // targ's underlying type must also be one of the interface types listed, if any
+ // targ must also be in the set of types of iface, if any.
+ // Constraints with empty type sets were already excluded above.
if !iface.typeSet().hasTerms() {
return nil // nothing to do
}
- // If targ is itself a type parameter, each of its possible types, but at least one, must be in the
- // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
+ // If targ is itself a type parameter, each of its possible types must be in the set
+ // of iface types (i.e., the targ type set must be a subset of the iface type set).
+ // Type arguments with empty type sets were already excluded above.
if targ := asTypeParam(targ); targ != nil {
targBound := targ.iface()
- if !targBound.typeSet().hasTerms() {
- return errorf("%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
- }
if !targBound.typeSet().subsetOf(iface.typeSet()) {
- // TODO(gri) need better error message
+ // TODO(gri) report which type is missing
return errorf("%s does not satisfy %s", targ, tpar.bound)
}
return nil
}
- // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
+ // Otherwise, targ's type must be included in the iface type set.
if !iface.typeSet().includes(targ) {
- // TODO(gri) better error message
+ // TODO(gri) report which type is missing
return errorf("%s does not satisfy %s", targ, tpar.bound)
}
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
index 0879d29d3d..96c92ccaec 100644
--- a/src/cmd/compile/internal/types2/interface.go
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -54,6 +54,14 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
return typ
}
+// MarkImplicit marks the interface t as implicit, meaning this interface
+// corresponds to a constraint literal such as ~T or A|B without explicit
+// interface embedding. MarkImplicit should be called before any concurrent use
+// of implicit interfaces.
+func (t *Interface) MarkImplicit() {
+ t.implicit = true
+}
+
// NumExplicitMethods returns the number of explicitly declared methods of interface t.
func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
@@ -108,7 +116,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
}
// f.Name != nil
- // We have a method with name f.Name, or a type of a type list (f.Name.Value == "type").
+ // We have a method with name f.Name.
name := f.Name.Value
if name == "_" {
if check.conf.CompilerErrorMessages {
@@ -172,7 +180,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
check.later(func() {
computeInterfaceTypeSet(check, iface.Pos(), ityp)
ityp.check = nil
- })
+ }).describef(iface, "compute type set for %s", ityp)
}
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
diff --git a/src/cmd/compile/internal/types2/mono.go b/src/cmd/compile/internal/types2/mono.go
new file mode 100644
index 0000000000..39c4d4fbef
--- /dev/null
+++ b/src/cmd/compile/internal/types2/mono.go
@@ -0,0 +1,337 @@
+// Copyright 2021 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+)
+
+// This file implements a check to validate that a Go package doesn't
+// have unbounded recursive instantiation, which is not compatible
+// with compilers using static instantiation (such as
+// monomorphization).
+//
+// It implements a sort of "type flow" analysis by detecting which
+// type parameters are instantiated with other type parameters (or
+// types derived thereof). A package cannot be statically instantiated
+// if the graph has any cycles involving at least one derived type.
+//
+// Concretely, we construct a directed, weighted graph. Vertices are
+// used to represent type parameters as well as some defined
+// types. Edges are used to represent how types depend on each other:
+//
+// * Everywhere a type-parameterized function or type is instantiated,
+// we add edges to each type parameter from the vertices (if any)
+// representing each type parameter or defined type referenced by
+// the type argument. If the type argument is just the referenced
+// type itself, then the edge has weight 0, otherwise 1.
+//
+// * For every defined type declared within a type-parameterized
+// function or method, we add an edge of weight 1 to the defined
+// type from each ambient type parameter.
+//
+// For example, given:
+//
+// func f[A, B any]() {
+// type T int
+// f[T, map[A]B]()
+// }
+//
+// we construct vertices representing types A, B, and T. Because of
+// declaration "type T int", we construct edges T<-A and T<-B with
+// weight 1; and because of instantiation "f[T, map[A]B]" we construct
+// edges A<-T with weight 0, and B<-A and B<-B with weight 1.
+//
+// Finally, we look for any positive-weight cycles. Zero-weight cycles
+// are allowed because static instantiation will reach a fixed point.
+
+type monoGraph struct {
+ vertices []monoVertex
+ edges []monoEdge
+
+ // canon maps method receiver type parameters to their respective
+ // receiver type's type parameters.
+ canon map[*TypeParam]*TypeParam
+
+ // nameIdx maps a defined type or (canonical) type parameter to its
+ // vertex index.
+ nameIdx map[*TypeName]int
+}
+
+type monoVertex struct {
+ weight int // weight of heaviest known path to this vertex
+ pre int // previous edge (if any) in the above path
+ len int // length of the above path
+
+ // obj is the defined type or type parameter represented by this
+ // vertex.
+ obj *TypeName
+}
+
+type monoEdge struct {
+ dst, src int
+ weight int
+
+ pos syntax.Pos
+ typ Type
+}
+
+func (check *Checker) monomorph() {
+ // We detect unbounded instantiation cycles using a variant of
+ // Bellman-Ford's algorithm. Namely, instead of always running |V|
+ // iterations, we run until we either reach a fixed point or we've
+ // found a path of length |V|. This allows us to terminate earlier
+ // when there are no cycles, which should be the common case.
+
+ again := true
+ for again {
+ again = false
+
+ for i, edge := range check.mono.edges {
+ src := &check.mono.vertices[edge.src]
+ dst := &check.mono.vertices[edge.dst]
+
+ // N.B., we're looking for the greatest weight paths, unlike
+ // typical Bellman-Ford.
+ w := src.weight + edge.weight
+ if w <= dst.weight {
+ continue
+ }
+
+ dst.pre = i
+ dst.len = src.len + 1
+ if dst.len == len(check.mono.vertices) {
+ check.reportInstanceLoop(edge.dst)
+ return
+ }
+
+ dst.weight = w
+ again = true
+ }
+ }
+}
+
+func (check *Checker) reportInstanceLoop(v int) {
+ var stack []int
+ seen := make([]bool, len(check.mono.vertices))
+
+ // We have a path that contains a cycle and ends at v, but v may
+ // only be reachable from the cycle, not on the cycle itself. We
+ // start by walking backwards along the path until we find a vertex
+ // that appears twice.
+ for !seen[v] {
+ stack = append(stack, v)
+ seen[v] = true
+ v = check.mono.edges[check.mono.vertices[v].pre].src
+ }
+
+ // Trim any vertices we visited before visiting v the first
+ // time. Since v is the first vertex we found within the cycle, any
+ // vertices we visited earlier cannot be part of the cycle.
+ for stack[0] != v {
+ stack = stack[1:]
+ }
+
+ // TODO(mdempsky): Pivot stack so we report the cycle from the top?
+
+ var err error_
+ obj0 := check.mono.vertices[v].obj
+ err.errorf(obj0, "instantiation cycle:")
+
+ qf := RelativeTo(check.pkg)
+ for _, v := range stack {
+ edge := check.mono.edges[check.mono.vertices[v].pre]
+ obj := check.mono.vertices[edge.dst].obj
+
+ switch obj.Type().(type) {
+ default:
+ panic("unexpected type")
+ case *Named:
+ err.errorf(edge.pos, "%s implicitly parameterized by %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
+ case *TypeParam:
+ err.errorf(edge.pos, "%s instantiated as %s", obj.Name(), TypeString(edge.typ, qf)) // secondary error, \t indented
+ }
+ }
+ check.report(&err)
+}
+
+// recordCanon records that tpar is the canonical type parameter
+// corresponding to method type parameter mpar.
+func (w *monoGraph) recordCanon(mpar, tpar *TypeParam) {
+ if w.canon == nil {
+ w.canon = make(map[*TypeParam]*TypeParam)
+ }
+ w.canon[mpar] = tpar
+}
+
+// recordInstance records that the given type parameters were
+// instantiated with the corresponding type arguments.
+func (w *monoGraph) recordInstance(pkg *Package, pos syntax.Pos, tparams []*TypeParam, targs []Type, posList []syntax.Pos) {
+ for i, tpar := range tparams {
+ pos := pos
+ if i < len(posList) {
+ pos = posList[i]
+ }
+ w.assign(pkg, pos, tpar, targs[i])
+ }
+}
+
+// assign records that tpar was instantiated as targ at pos.
+func (w *monoGraph) assign(pkg *Package, pos syntax.Pos, tpar *TypeParam, targ Type) {
+ // Go generics do not have an analog to C++`s template-templates,
+ // where a template parameter can itself be an instantiable
+ // template. So any instantiation cycles must occur within a single
+ // package. Accordingly, we can ignore instantiations of imported
+ // type parameters.
+ //
+ // TODO(mdempsky): Push this check up into recordInstance? All type
+ // parameters in a list will appear in the same package.
+ if tpar.Obj().Pkg() != pkg {
+ return
+ }
+
+ // flow adds an edge from vertex src representing that typ flows to tpar.
+ flow := func(src int, typ Type) {
+ weight := 1
+ if typ == targ {
+ weight = 0
+ }
+
+ w.addEdge(w.typeParamVertex(tpar), src, weight, pos, targ)
+ }
+
+ // Recursively walk the type argument to find any defined types or
+ // type parameters.
+ var do func(typ Type)
+ do = func(typ Type) {
+ switch typ := typ.(type) {
+ default:
+ panic("unexpected type")
+
+ case *TypeParam:
+ assert(typ.Obj().Pkg() == pkg)
+ flow(w.typeParamVertex(typ), typ)
+
+ case *Named:
+ if src := w.localNamedVertex(pkg, typ.Origin()); src >= 0 {
+ flow(src, typ)
+ }
+
+ targs := typ.TypeArgs()
+ for i := 0; i < targs.Len(); i++ {
+ do(targs.At(i))
+ }
+
+ case *Array:
+ do(typ.Elem())
+ case *Basic:
+ // ok
+ case *Chan:
+ do(typ.Elem())
+ case *Map:
+ do(typ.Key())
+ do(typ.Elem())
+ case *Pointer:
+ do(typ.Elem())
+ case *Slice:
+ do(typ.Elem())
+
+ case *Interface:
+ for i := 0; i < typ.NumMethods(); i++ {
+ do(typ.Method(i).Type())
+ }
+ case *Signature:
+ tuple := func(tup *Tuple) {
+ for i := 0; i < tup.Len(); i++ {
+ do(tup.At(i).Type())
+ }
+ }
+ tuple(typ.Params())
+ tuple(typ.Results())
+ case *Struct:
+ for i := 0; i < typ.NumFields(); i++ {
+ do(typ.Field(i).Type())
+ }
+ }
+ }
+ do(targ)
+}
+
+// localNamedVertex returns the index of the vertex representing
+// named, or -1 if named doesn't need representation.
+func (w *monoGraph) localNamedVertex(pkg *Package, named *Named) int {
+ obj := named.Obj()
+ if obj.Pkg() != pkg {
+ return -1 // imported type
+ }
+
+ root := pkg.Scope()
+ if obj.Parent() == root {
+ return -1 // package scope, no ambient type parameters
+ }
+
+ if idx, ok := w.nameIdx[obj]; ok {
+ return idx
+ }
+
+ idx := -1
+
+ // Walk the type definition's scope to find any ambient type
+ // parameters that it's implicitly parameterized by.
+ for scope := obj.Parent(); scope != root; scope = scope.Parent() {
+ for _, elem := range scope.elems {
+ if elem, ok := elem.(*TypeName); ok && !elem.IsAlias() && elem.Pos().Cmp(obj.Pos()) < 0 {
+ if tpar, ok := elem.Type().(*TypeParam); ok {
+ if idx < 0 {
+ idx = len(w.vertices)
+ w.vertices = append(w.vertices, monoVertex{obj: obj})
+ }
+
+ w.addEdge(idx, w.typeParamVertex(tpar), 1, obj.Pos(), tpar)
+ }
+ }
+ }
+ }
+
+ if w.nameIdx == nil {
+ w.nameIdx = make(map[*TypeName]int)
+ }
+ w.nameIdx[obj] = idx
+ return idx
+}
+
+// typeParamVertex returns the index of the vertex representing tpar.
+func (w *monoGraph) typeParamVertex(tpar *TypeParam) int {
+ if x, ok := w.canon[tpar]; ok {
+ tpar = x
+ }
+
+ obj := tpar.Obj()
+
+ if idx, ok := w.nameIdx[obj]; ok {
+ return idx
+ }
+
+ if w.nameIdx == nil {
+ w.nameIdx = make(map[*TypeName]int)
+ }
+
+ idx := len(w.vertices)
+ w.vertices = append(w.vertices, monoVertex{obj: obj})
+ w.nameIdx[obj] = idx
+ return idx
+}
+
+func (w *monoGraph) addEdge(dst, src, weight int, pos syntax.Pos, typ Type) {
+ // TODO(mdempsky): Deduplicate redundant edges?
+ w.edges = append(w.edges, monoEdge{
+ dst: dst,
+ src: src,
+ weight: weight,
+
+ pos: pos,
+ typ: typ,
+ })
+}
diff --git a/src/cmd/compile/internal/types2/mono_test.go b/src/cmd/compile/internal/types2/mono_test.go
new file mode 100644
index 0000000000..19d0e95637
--- /dev/null
+++ b/src/cmd/compile/internal/types2/mono_test.go
@@ -0,0 +1,89 @@
+// Copyright 2021 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 types2_test
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "errors"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func checkMono(t *testing.T, body string) error {
+ src := "package x; import `unsafe`; var _ unsafe.Pointer;\n" + body
+ file, err := syntax.Parse(syntax.NewFileBase("x.go"), strings.NewReader(src), nil, nil, syntax.AllowGenerics)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files := []*syntax.File{file}
+
+ var buf bytes.Buffer
+ conf := types2.Config{
+ Error: func(err error) { fmt.Fprintln(&buf, err) },
+ Importer: defaultImporter(),
+ }
+ conf.Check("x", files, nil)
+ if buf.Len() == 0 {
+ return nil
+ }
+ return errors.New(strings.TrimRight(buf.String(), "\n"))
+}
+
+func TestMonoGood(t *testing.T) {
+ for i, good := range goods {
+ if err := checkMono(t, good); err != nil {
+ t.Errorf("%d: unexpected failure: %v", i, err)
+ }
+ }
+}
+
+func TestMonoBad(t *testing.T) {
+ for i, bad := range bads {
+ if err := checkMono(t, bad); err == nil {
+ t.Errorf("%d: unexpected success", i)
+ } else {
+ t.Log(err)
+ }
+ }
+}
+
+var goods = []string{
+ "func F[T any](x T) { F(x) }",
+ "func F[T, U, V any]() { F[U, V, T](); F[V, T, U]() }",
+ "type Ring[A, B, C any] struct { L *Ring[B, C, A]; R *Ring[C, A, B] }",
+ "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte }",
+ "func F[T any]() { type U[T any] [unsafe.Sizeof(F[*T])]byte; var _ U[int] }",
+ "type U[T any] [unsafe.Sizeof(F[*T])]byte; func F[T any]() { var _ U[U[int]] }",
+ "func F[T any]() { type A = int; F[A]() }",
+}
+
+// TODO(mdempsky): Validate specific error messages and positioning.
+
+var bads = []string{
+ "func F[T any](x T) { F(&x) }",
+ "func F[T any]() { F[*T]() }",
+ "func F[T any]() { F[[]T]() }",
+ "func F[T any]() { F[[1]T]() }",
+ "func F[T any]() { F[chan T]() }",
+ "func F[T any]() { F[map[*T]int]() }",
+ "func F[T any]() { F[map[error]T]() }",
+ "func F[T any]() { F[func(T)]() }",
+ "func F[T any]() { F[func() T]() }",
+ "func F[T any]() { F[struct{ t T }]() }",
+ "func F[T any]() { F[interface{ t() T }]() }",
+ "type U[_ any] int; func F[T any]() { F[U[T]]() }",
+ "func F[T any]() { type U int; F[U]() }",
+ "func F[T any]() { type U int; F[*U]() }",
+ "type U[T any] int; func (U[T]) m() { var _ U[*T] }",
+ "type U[T any] int; func (*U[T]) m() { var _ U[*T] }",
+ "type U[T1 any] [unsafe.Sizeof(F[*T1])]byte; func F[T2 any]() { var _ U[T2] }",
+ "func F[A, B, C, D, E any]() { F[B, C, D, E, *A]() }",
+ "type U[_ any] int; const X = unsafe.Sizeof(func() { type A[T any] U[A[*T]] })",
+ "func F[T any]() { type A = *T; F[A]() }",
+ "type A[T any] struct { _ A[*T] }",
+}
diff --git a/src/cmd/compile/internal/types2/named.go b/src/cmd/compile/internal/types2/named.go
index 3971e03179..6ebad8fbb5 100644
--- a/src/cmd/compile/internal/types2/named.go
+++ b/src/cmd/compile/internal/types2/named.go
@@ -65,22 +65,9 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
if obj.typ == nil {
obj.typ = typ
}
- // Ensure that typ is always expanded, at which point the check field can be
- // nilled out.
- //
- // Note that currently we cannot nil out check inside typ.under(), because
- // it's possible that typ is expanded multiple times.
- //
- // TODO(gri): clean this up so that under is the only function mutating
- // named types.
+ // Ensure that typ is always expanded and sanity-checked.
if check != nil {
- check.later(func() {
- switch typ.under().(type) {
- case *Named:
- panic("unexpanded underlying type")
- }
- typ.check = nil
- })
+ check.defTypes = append(check.defTypes, typ)
}
return typ
}
@@ -143,6 +130,18 @@ func (t *Named) String() string { return TypeString(t, nil) }
// chain before returning it. If no underlying type is found or a cycle
// is detected, the result is Typ[Invalid]. If a cycle is detected and
// n0.check != nil, the cycle is reported.
+//
+// This is necessary because the underlying type of named may be itself a
+// named type that is incomplete:
+//
+// type (
+// A B
+// B *C
+// C A
+// )
+//
+// The type of C is the (named) type of A which is incomplete,
+// and which has as its underlying type the named type B.
func (n0 *Named) under() Type {
u := n0.Underlying()
@@ -152,7 +151,9 @@ func (n0 *Named) under() Type {
var n1 *Named
switch u1 := u.(type) {
case nil:
- return Typ[Invalid]
+ // After expansion via Underlying(), we should never encounter a nil
+ // underlying.
+ panic("nil underlying")
default:
// common case
return u
@@ -236,10 +237,18 @@ func (check *Checker) bestContext(ctxt *Context) *Context {
// The underlying type will be Typ[Invalid] if there was an error.
func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypeParamList, underlying Type, methods []*Func) {
n.orig.resolve(ctxt)
+ assert(n.orig.underlying != nil)
check := n.check
- if check.validateTArgLen(instPos, n.orig.tparams.Len(), n.targs.Len()) {
+ if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
+ // We should only get an unexpanded underlying here during type checking
+ // (for example, in recursive type declarations).
+ assert(check != nil)
+ }
+
+ // Mismatching arg and tparam length may be checked elsewhere.
+ if n.orig.tparams.Len() == n.targs.Len() {
// We must always have a context, to avoid infinite recursion.
ctxt = check.bestContext(ctxt)
h := ctxt.TypeHash(n.orig, n.targs.list())
diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go
index affeaf61f2..c7d6709c26 100644
--- a/src/cmd/compile/internal/types2/object.go
+++ b/src/cmd/compile/internal/types2/object.go
@@ -315,6 +315,8 @@ func (obj *TypeName) IsAlias() bool {
return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
case *Named:
return obj != t.obj
+ case *TypeParam:
+ return obj != t.obj
default:
return true
}
diff --git a/src/cmd/compile/internal/types2/object_test.go b/src/cmd/compile/internal/types2/object_test.go
index a86733a5c9..ed3c123023 100644
--- a/src/cmd/compile/internal/types2/object_test.go
+++ b/src/cmd/compile/internal/types2/object_test.go
@@ -33,6 +33,8 @@ func TestIsAlias(t *testing.T) {
pkg := NewPackage("p", "p")
t1 := NewTypeName(nopos, pkg, "t1", nil)
n1 := NewNamed(t1, new(Struct), nil)
+ t5 := NewTypeName(nopos, pkg, "t5", nil)
+ NewTypeParam(t5, nil)
for _, test := range []struct {
name *TypeName
alias bool
@@ -46,6 +48,7 @@ func TestIsAlias(t *testing.T) {
{NewTypeName(nopos, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
{NewTypeName(nopos, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
{NewTypeName(nopos, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
+ {t5, false}, // type name refers to type parameter and vice versa
} {
check(test.name, test.alias)
}
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
index 19326b8342..2f85802701 100644
--- a/src/cmd/compile/internal/types2/operand.go
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -176,17 +176,14 @@ func operandString(x *operand, qf Qualifier) string {
if hasType {
if x.typ != Typ[Invalid] {
var intro string
- var tpar *TypeParam
if isGeneric(x.typ) {
intro = " of parameterized type "
- } else if tpar = asTypeParam(x.typ); tpar != nil {
- intro = " of type parameter "
} else {
intro = " of type "
}
buf.WriteString(intro)
WriteType(&buf, x.typ, qf)
- if tpar != nil {
+ if tpar := asTypeParam(x.typ); tpar != nil {
buf.WriteString(" constrained by ")
WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here
}
@@ -252,53 +249,49 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
V := x.typ
- const debugAssignableTo = false
- if debugAssignableTo && check != nil {
- check.dump("V = %s", V)
- check.dump("T = %s", T)
- }
-
// x's type is identical to T
if Identical(V, T) {
return true, 0
}
- Vu := optype(V)
- Tu := optype(T)
-
- if debugAssignableTo && check != nil {
- check.dump("Vu = %s", Vu)
- check.dump("Tu = %s", Tu)
- }
+ Vu := under(V)
+ Tu := under(T)
+ Vp, _ := Vu.(*TypeParam)
+ Tp, _ := Tu.(*TypeParam)
// x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- if t, ok := under(T).(*TypeParam); ok {
- return t.is(func(t *term) bool {
- // TODO(gri) this could probably be more efficient
- if t.tilde {
- // TODO(gri) We need to check assignability
- // for the underlying type of x.
+ assert(Vp == nil)
+ if Tp != nil {
+ // T is a type parameter: x is assignable to T if it is
+ // representable by each specific type in the type set of T.
+ return Tp.is(func(t *term) bool {
+ if t == nil {
+ return false
}
- ok, _ := x.assignableTo(check, t.typ, reason)
- return ok
+ // A term may be a tilde term but the underlying
+ // type of an untyped value doesn't change so we
+ // don't need to do anything special.
+ newType, _, _ := check.implicitTypeAndValue(x, t.typ)
+ return newType != nil
}), _IncompatibleAssign
}
- newType, _, _ := check.implicitTypeAndValue(x, Tu)
+ newType, _, _ := check.implicitTypeAndValue(x, T)
return newType != nil, _IncompatibleAssign
}
// Vu is typed
// x's type V and T have identical underlying types
- // and at least one of V or T is not a named type
- if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ // and at least one of V or T is not a named type.
+ if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) {
return true, 0
}
- // T is an interface type and x implements T
+ // T is an interface type and x implements T and T is not a type parameter
if Ti, ok := Tu.(*Interface); ok {
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
if reason != nil {
+ // TODO(gri) the error messages here should follow the style in Checker.typeAssertion (factor!)
if wrongType != nil {
if Identical(m.typ, wrongType.typ) {
*reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
@@ -317,13 +310,69 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
// x is a bidirectional channel value, T is a channel
// type, x's type V and T have identical element types,
- // and at least one of V or T is not a named type
+ // and at least one of V or T is not a named type.
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
- return !isNamed(V) || !isNamed(T), _InvalidChanAssign
+ return !hasName(V) || !hasName(T), _InvalidChanAssign
}
}
+ // optimization: if we don't have type parameters, we're done
+ if Vp == nil && Tp == nil {
+ return false, _IncompatibleAssign
+ }
+
+ errorf := func(format string, args ...interface{}) {
+ if check != nil && reason != nil {
+ msg := check.sprintf(format, args...)
+ if *reason != "" {
+ msg += "\n\t" + *reason
+ }
+ *reason = msg
+ }
+ }
+
+ // x's type V is not a named type and T is a type parameter, and
+ // x is assignable to each specific type in T's type set.
+ if !hasName(V) && Tp != nil {
+ ok := false
+ code := _IncompatibleAssign
+ Tp.is(func(T *term) bool {
+ if T == nil {
+ return false // no specific types
+ }
+ ok, code = x.assignableTo(check, T.typ, reason)
+ if !ok {
+ errorf("cannot assign %s to %s (in %s)", x.typ, T.typ, Tp)
+ return false
+ }
+ return true
+ })
+ return ok, code
+ }
+
+ // x's type V is a type parameter and T is not a named type,
+ // and values x' of each specific type in V's type set are
+ // assignable to T.
+ if Vp != nil && !hasName(T) {
+ x := *x // don't clobber outer x
+ ok := false
+ code := _IncompatibleAssign
+ Vp.is(func(V *term) bool {
+ if V == nil {
+ return false // no specific types
+ }
+ x.typ = V.typ
+ ok, code = x.assignableTo(check, T, reason)
+ if !ok {
+ errorf("cannot assign %s (in %s) to %s", V.typ, Vp, T)
+ return false
+ }
+ return true
+ })
+ return ok, code
+ }
+
return false, _IncompatibleAssign
}
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index 74ad3da72c..7fbb91eb61 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -6,77 +6,100 @@
package types2
-// isNamed reports whether typ has a name.
-// isNamed may be called with types that are not fully set up.
-func isNamed(typ Type) bool {
- switch typ.(type) {
+// The isX predicates below report whether t is an X.
+// If t is a type parameter the result is false; i.e.,
+// these predicates don't look inside a type parameter.
+
+func isBoolean(t Type) bool { return isBasic(t, IsBoolean) }
+func isInteger(t Type) bool { return isBasic(t, IsInteger) }
+func isUnsigned(t Type) bool { return isBasic(t, IsUnsigned) }
+func isFloat(t Type) bool { return isBasic(t, IsFloat) }
+func isComplex(t Type) bool { return isBasic(t, IsComplex) }
+func isNumeric(t Type) bool { return isBasic(t, IsNumeric) }
+func isString(t Type) bool { return isBasic(t, IsString) }
+func isIntegerOrFloat(t Type) bool { return isBasic(t, IsInteger|IsFloat) }
+func isConstType(t Type) bool { return isBasic(t, IsConstType) }
+
+// isBasic reports whether under(t) is a basic type with the specified info.
+// If t is a type parameter the result is false; i.e.,
+// isBasic does not look inside a type parameter.
+func isBasic(t Type, info BasicInfo) bool {
+ u, _ := under(t).(*Basic)
+ return u != nil && u.info&info != 0
+}
+
+// The allX predicates below report whether t is an X.
+// If t is a type parameter the result is true if isX is true
+// for all specified types of the type parameter's type set.
+// allX is an optimized version of isX(structure(t)) (which
+// is the same as underIs(t, isX)).
+
+func allBoolean(t Type) bool { return allBasic(t, IsBoolean) }
+func allInteger(t Type) bool { return allBasic(t, IsInteger) }
+func allUnsigned(t Type) bool { return allBasic(t, IsUnsigned) }
+func allNumeric(t Type) bool { return allBasic(t, IsNumeric) }
+func allString(t Type) bool { return allBasic(t, IsString) }
+func allOrdered(t Type) bool { return allBasic(t, IsOrdered) }
+func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
+
+// allBasic reports whether under(t) is a basic type with the specified info.
+// If t is a type parameter, the result is true if isBasic(t, info) is true
+// for all specific types of the type parameter's type set.
+// allBasic(t, info) is an optimized version of isBasic(structure(t), info).
+func allBasic(t Type, info BasicInfo) bool {
+ switch u := under(t).(type) {
+ case *Basic:
+ return u.info&info != 0
+ case *TypeParam:
+ return u.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
+ }
+ return false
+}
+
+// hasName reports whether t has a name. This includes
+// predeclared types, defined types, and type parameters.
+// hasName may be called with types that are not fully set up.
+func hasName(t Type) bool {
+ switch t.(type) {
case *Basic, *Named, *TypeParam:
return true
}
return false
}
-// isGeneric reports whether a type is a generic, uninstantiated type (generic
-// signatures are not included).
-func isGeneric(typ Type) bool {
- // A parameterized type is only instantiated if it doesn't have an instantiation already.
- named, _ := typ.(*Named)
- return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
-}
-
-func is(typ Type, what BasicInfo) bool {
- switch t := under(typ).(type) {
- case *Basic:
- return t.info&what != 0
- case *TypeParam:
- return t.underIs(func(t Type) bool { return is(t, what) })
- }
- return false
-}
-
-func isBoolean(typ Type) bool { return is(typ, IsBoolean) }
-func isInteger(typ Type) bool { return is(typ, IsInteger) }
-func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) }
-func isFloat(typ Type) bool { return is(typ, IsFloat) }
-func isComplex(typ Type) bool { return is(typ, IsComplex) }
-func isNumeric(typ Type) bool { return is(typ, IsNumeric) }
-func isString(typ Type) bool { return is(typ, IsString) }
-
-// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not
-// produce the expected result because a type list that contains both an integer
-// and a floating-point type is neither (all) integers, nor (all) floats.
-// Use isIntegerOrFloat instead.
-func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) }
-
-// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ).
-func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }
-
-// isTyped reports whether typ is typed; i.e., not an untyped
+// isTyped reports whether t is typed; i.e., not an untyped
// constant or boolean. isTyped may be called with types that
// are not fully set up.
-func isTyped(typ Type) bool {
+func isTyped(t Type) bool {
// isTyped is called with types that are not fully
// set up. Must not call asBasic()!
- t, _ := typ.(*Basic)
- return t == nil || t.info&IsUntyped == 0
+ b, _ := t.(*Basic)
+ return b == nil || b.info&IsUntyped == 0
}
-// isUntyped(typ) is the same as !isTyped(typ).
-func isUntyped(typ Type) bool {
- return !isTyped(typ)
+// isUntyped(t) is the same as !isTyped(t).
+func isUntyped(t Type) bool {
+ return !isTyped(t)
}
-func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
-
-func isConstType(typ Type) bool {
- // Type parameters are never const types.
- t, _ := under(typ).(*Basic)
- return t != nil && t.info&IsConstType != 0
+// IsInterface reports whether t is an interface type.
+func IsInterface(t Type) bool {
+ return asInterface(t) != nil
}
-// IsInterface reports whether typ is an interface type.
-func IsInterface(typ Type) bool {
- return asInterface(typ) != nil
+// isTypeParam reports whether t is a type parameter.
+func isTypeParam(t Type) bool {
+ _, ok := under(t).(*TypeParam)
+ return ok
+}
+
+// isGeneric reports whether a type is a generic, uninstantiated type
+// (generic signatures are not included).
+// TODO(gri) should we include signatures or assert that they are not present?
+func isGeneric(t Type) bool {
+ // A parameterized type is only generic if it doesn't have an instantiation already.
+ named, _ := t.(*Named)
+ return named != nil && named.obj != nil && named.targs == nil && named.TypeParams() != nil
}
// Comparable reports whether values of type T are comparable.
@@ -115,15 +138,15 @@ func comparable(T Type, seen map[Type]bool) bool {
return false
}
-// hasNil reports whether a type includes the nil value.
-func hasNil(typ Type) bool {
- switch t := under(typ).(type) {
+// hasNil reports whether type t includes the nil value.
+func hasNil(t Type) bool {
+ switch u := under(t).(type) {
case *Basic:
- return t.kind == UnsafePointer
+ return u.kind == UnsafePointer
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true
case *TypeParam:
- return t.underIs(hasNil)
+ return u.underIs(hasNil)
}
return false
}
@@ -339,10 +362,6 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
case *TypeParam:
// nothing to do (x and y being equal is caught in the very beginning of this function)
- case *top:
- // Either both types are theTop in which case the initial x == y check
- // will have caught them. Otherwise they are not identical.
-
case nil:
// avoid a crash in case of nil type
@@ -369,9 +388,8 @@ func identicalTParams(x, y []*TypeParam, cmpTags bool, p *ifacePair) bool {
// Default returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
-//
-func Default(typ Type) Type {
- if t, ok := typ.(*Basic); ok {
+func Default(t Type) Type {
+ if t, ok := t.(*Basic); ok {
switch t.kind {
case UntypedBool:
return Typ[Bool]
@@ -387,5 +405,5 @@ func Default(typ Type) Type {
return Typ[String]
}
}
- return typ
+ return t
}
diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go
index 204e456a91..6c3e1842ce 100644
--- a/src/cmd/compile/internal/types2/return.go
+++ b/src/cmd/compile/internal/types2/return.go
@@ -62,6 +62,11 @@ func (check *Checker) isTerminating(s syntax.Stmt, label string) bool {
return true
case *syntax.ForStmt:
+ if _, ok := s.Init.(*syntax.RangeClause); ok {
+ // Range clauses guarantee that the loop terminates,
+ // so the loop is not a terminating statement. See issue 49003.
+ break
+ }
if s.Cond == nil && !hasBreak(s.Body, label, true) {
return true
}
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
index 604d0c9dbd..4541435587 100644
--- a/src/cmd/compile/internal/types2/signature.go
+++ b/src/cmd/compile/internal/types2/signature.go
@@ -148,6 +148,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
list := make([]Type, sig.RecvTypeParams().Len())
for i, t := range sig.RecvTypeParams().list() {
list[i] = t
+ check.mono.recordCanon(t, recvTParams[i])
}
smap := makeSubstMap(recvTParams, list)
for i, tpar := range sig.RecvTypeParams().list() {
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index d47e23f735..99b846b80b 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -34,7 +34,6 @@ func TestSizeof(t *testing.T) {
{Named{}, 68, 128},
{TypeParam{}, 28, 48},
{term{}, 12, 24},
- {top{}, 0, 0},
// Objects
{PkgName{}, 64, 104},
diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go
index 6e340d0777..9c22f01673 100644
--- a/src/cmd/compile/internal/types2/stdlib_test.go
+++ b/src/cmd/compile/internal/types2/stdlib_test.go
@@ -193,7 +193,7 @@ func TestStdFixed(t *testing.T) {
"issue42058a.go", // types2 does not have constraints on channel element size
"issue42058b.go", // types2 does not have constraints on channel element size
"issue48097.go", // go/types doesn't check validity of //go:xxx directives, and non-init bodyless function
-
+ "issue48230.go", // go/types doesn't check validity of //go:xxx directives
)
}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index f3f345fd2f..eaf420aca7 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -306,7 +306,7 @@ L:
}
seen[T] = e
if T != nil {
- check.typeAssertion(e.Pos(), x, xtyp, T)
+ check.typeAssertion(e, x, xtyp, T, true)
}
}
return
@@ -347,7 +347,7 @@ L:
// }
// seen[hash] = e
// if T != nil {
-// check.typeAssertion(e.Pos(), x, xtyp, T)
+// check.typeAssertion(e, x, xtyp, T, true)
// }
// }
// return
@@ -443,7 +443,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
if x.mode == invalid {
return
}
- if !isNumeric(x.typ) {
+ if !allNumeric(x.typ) {
check.errorf(lhs[0], invalidOp+"%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ)
return
}
@@ -556,7 +556,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
check.simpleStmt(s.Init)
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, "non-boolean condition in if statement")
}
check.stmt(inner, s.Then)
@@ -645,7 +645,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond)
- if x.mode != invalid && !isBoolean(x.typ) {
+ if x.mode != invalid && !allBoolean(x.typ) {
check.error(s.Cond, "non-boolean condition in for statement")
}
}
@@ -834,19 +834,28 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
// determine key/value types
var key, val Type
if x.mode != invalid {
- // Ranging over a type parameter is permitted if it has a structural type.
- typ := optype(x.typ)
- if _, ok := typ.(*Chan); ok && sValue != nil {
- check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
- // ok to continue
- }
- var msg string
- key, val, msg = rangeKeyVal(typ, isVarName(sKey), isVarName(sValue))
- if key == nil || msg != "" {
- if msg != "" {
- msg = ": " + msg
+ // Ranging over a type parameter is permitted if it has a single underlying type.
+ var cause string
+ u := structure(x.typ)
+ switch t := u.(type) {
+ case nil:
+ cause = "type set has no single underlying type"
+ case *Chan:
+ if sValue != nil {
+ check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ if t.dir == SendOnly {
+ cause = "receive from send-only channel"
+ }
+ }
+ key, val = rangeKeyVal(u)
+ if key == nil || cause != "" {
+ if cause == "" {
+ check.softErrorf(&x, "cannot range over %s", &x)
+ } else {
+ check.softErrorf(&x, "cannot range over %s (%s)", &x, cause)
}
- check.softErrorf(&x, "cannot range over %s%s", &x, msg)
// ok to continue
}
}
@@ -927,44 +936,23 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
check.stmt(inner, s.Body)
}
-// isVarName reports whether x is a non-nil, non-blank (_) expression.
-func isVarName(x syntax.Expr) bool {
- if x == nil {
- return false
- }
- ident, _ := unparen(x).(*syntax.Name)
- return ident == nil || ident.Value != "_"
-}
-
// rangeKeyVal returns the key and value type produced by a range clause
-// over an expression of type typ, and possibly an error message. If the
-// range clause is not permitted the returned key is nil or msg is not
-// empty (in that case we still may have a non-nil key type which can be
-// used to reduce the chance for follow-on errors).
-// The wantKey, wantVal, and hasVal flags indicate which of the iteration
-// variables are used or present; this matters if we range over a generic
-// type where not all keys or values are of the same type.
-func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
+// over an expression of type typ. If the range clause is not permitted
+// the results are nil.
+func rangeKeyVal(typ Type) (key, val Type) {
switch typ := arrayPtrDeref(typ).(type) {
case *Basic:
if isString(typ) {
- return Typ[Int], universeRune, "" // use 'rune' name
+ return Typ[Int], universeRune // use 'rune' name
}
case *Array:
- return Typ[Int], typ.elem, ""
+ return Typ[Int], typ.elem
case *Slice:
- return Typ[Int], typ.elem, ""
+ return Typ[Int], typ.elem
case *Map:
- return typ.key, typ.elem, ""
+ return typ.key, typ.elem
case *Chan:
- var msg string
- if typ.dir == SendOnly {
- msg = "receive from send-only channel"
- }
- return typ.elem, Typ[Invalid], msg
- case *top:
- // we have a type parameter with no structural type
- return nil, nil, "no structural type"
+ return typ.elem, Typ[Invalid]
}
- return nil, nil, ""
+ return
}
diff --git a/src/cmd/compile/internal/types2/struct.go b/src/cmd/compile/internal/types2/struct.go
index f0c27c0150..933d7ef947 100644
--- a/src/cmd/compile/internal/types2/struct.go
+++ b/src/cmd/compile/internal/types2/struct.go
@@ -154,7 +154,7 @@ func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
}
}
- })
+ }).describef(embeddedPos, "check embedded type %s", embeddedTyp)
}
}
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index a5ebd416aa..269b284ac4 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -74,7 +74,7 @@ func (subst *subster) typ(typ Type) Type {
// Call typOrNil if it's possible that typ is nil.
panic("nil typ")
- case *Basic, *top:
+ case *Basic:
// nothing to do
case *Array:
diff --git a/src/cmd/compile/internal/types2/termlist.go b/src/cmd/compile/internal/types2/termlist.go
index 378ba6b8f4..844e39e3bf 100644
--- a/src/cmd/compile/internal/types2/termlist.go
+++ b/src/cmd/compile/internal/types2/termlist.go
@@ -93,8 +93,8 @@ func (xl termlist) norm() termlist {
}
// If the type set represented by xl is specified by a single (non-𝓤) term,
-// structuralType returns that type. Otherwise it returns nil.
-func (xl termlist) structuralType() Type {
+// singleType returns that type. Otherwise it returns nil.
+func (xl termlist) singleType() Type {
if nl := xl.norm(); len(nl) == 1 {
return nl[0].typ // if nl.isAll() then typ is nil, which is ok
}
diff --git a/src/cmd/compile/internal/types2/termlist_test.go b/src/cmd/compile/internal/types2/termlist_test.go
index ed1330d26f..1bdf9e1386 100644
--- a/src/cmd/compile/internal/types2/termlist_test.go
+++ b/src/cmd/compile/internal/types2/termlist_test.go
@@ -106,7 +106,7 @@ func TestTermlistNorm(t *testing.T) {
}
}
-func TestTermlistStructuralType(t *testing.T) {
+func TestTermlistSingleType(t *testing.T) {
// helper to deal with nil types
tstring := func(typ Type) string {
if typ == nil {
@@ -128,9 +128,9 @@ func TestTermlistStructuralType(t *testing.T) {
"∅ ∪ ~int ∪ string": "nil",
} {
xl := maketl(test)
- got := tstring(xl.structuralType())
+ got := tstring(xl.singleType())
if got != want {
- t.Errorf("(%v).structuralType() == %v; want %v", test, got, want)
+ t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
}
}
}
diff --git a/src/cmd/compile/internal/types2/testdata/check/builtins.go2 b/src/cmd/compile/internal/types2/testdata/check/builtins.go2
index 243e888ff7..d1067a190f 100644
--- a/src/cmd/compile/internal/types2/testdata/check/builtins.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/builtins.go2
@@ -78,8 +78,14 @@ func _[T ~string](x []byte, y T) {
func _[T ~[]byte|~string](x T, y []byte) {
copy(x /* ERROR expects slice arguments */ , y)
- // TODO(gri) should this be valid?
- copy(y /* ERROR expects slice arguments */ , x)
+ copy(y, x)
+}
+
+type L0 []int
+type L1 []int
+
+func _[T L0 | L1](x, y T) {
+ copy(x, y)
}
// delete
@@ -121,15 +127,18 @@ func _[T M4[K, V], K comparable, V any](m T) {
// make
+type myChan chan int
+
func _[
- S1 interface{ []int },
- S2 interface{ []int | chan int },
+ S1 ~[]int,
+ S2 ~[]int | ~chan int,
- M1 interface{ map[string]int },
- M2 interface{ map[string]int | chan int },
+ M1 ~map[string]int,
+ M2 ~map[string]int | ~chan int,
- C1 interface{ chan int },
- C2 interface{ chan int | chan string },
+ C1 ~chan int,
+ C2 ~chan int | ~chan string,
+ C3 chan int | myChan, // single underlying type
]() {
type S0 []int
_ = make([]int, 10)
@@ -139,7 +148,7 @@ func _[
_ = make /* ERROR expects 2 or 3 arguments */ (S1)
_ = make(S1, 10, 20)
_ = make /* ERROR expects 2 or 3 arguments */ (S1, 10, 20, 30)
- _ = make(S2 /* ERROR cannot make .* no structural type */ , 10)
+ _ = make(S2 /* ERROR cannot make .* no single underlying type */ , 10)
type M0 map[string]int
_ = make(map[string]int)
@@ -147,7 +156,7 @@ func _[
_ = make(M1)
_ = make(M1, 10)
_ = make/* ERROR expects 1 or 2 arguments */(M1, 10, 20)
- _ = make(M2 /* ERROR cannot make .* no structural type */ )
+ _ = make(M2 /* ERROR cannot make .* no single underlying type */ )
type C0 chan int
_ = make(chan int)
@@ -155,7 +164,8 @@ func _[
_ = make(C1)
_ = make(C1, 10)
_ = make/* ERROR expects 1 or 2 arguments */(C1, 10, 20)
- _ = make(C2 /* ERROR cannot make .* no structural type */ )
+ _ = make(C2 /* ERROR cannot make .* no single underlying type */ )
+ _ = make(C3)
}
// unsafe.Alignof
diff --git a/src/cmd/compile/internal/types2/testdata/check/compliterals.go2 b/src/cmd/compile/internal/types2/testdata/check/compliterals.go2
new file mode 100644
index 0000000000..60eac97a3f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/check/compliterals.go2
@@ -0,0 +1,22 @@
+// Copyright 2012 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.
+
+// Composite literals with parameterized types
+
+package comp_literals
+
+type myStruct struct {
+ f int
+}
+
+type slice[E any] []E
+
+func struct_literals[S struct{f int}|myStruct]() {
+ _ = S{}
+ _ = S{0}
+ _ = S{f: 0}
+
+ _ = slice[int]{1, 2, 3}
+ _ = slice[S]{{}, {0}, {f:0}}
+}
diff --git a/src/cmd/compile/internal/types2/testdata/check/expr3.src b/src/cmd/compile/internal/types2/testdata/check/expr3.src
index fd28421dc8..df4cf6a840 100644
--- a/src/cmd/compile/internal/types2/testdata/check/expr3.src
+++ b/src/cmd/compile/internal/types2/testdata/check/expr3.src
@@ -459,9 +459,9 @@ func type_asserts() {
var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type)
- _ = t /* ERROR "missing method m" */ .(T)
+ _ = t /* ERROR "m method has pointer receiver" */ .(T)
_ = t.(*T)
- _ = t /* ERROR "missing method m" */ .(T1)
+ _ = t /* ERROR "missing m method" */ .(T1)
_ = t /* ERROR "wrong type for method m" */ .(T2)
_ = t /* STRICT "wrong type for method m" */ .(I2) // only an error in strict mode (issue 8561)
diff --git a/src/cmd/compile/internal/types2/testdata/check/tinference.go2 b/src/cmd/compile/internal/types2/testdata/check/funcinference.go2
similarity index 82%
rename from src/cmd/compile/internal/types2/testdata/check/tinference.go2
rename to src/cmd/compile/internal/types2/testdata/check/funcinference.go2
index 2409fef4ae..7160e18b19 100644
--- a/src/cmd/compile/internal/types2/testdata/check/tinference.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/funcinference.go2
@@ -2,26 +2,25 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tinferenceB
+package funcInference
import "strconv"
type any interface{}
-// Embedding stand-alone type parameters is not permitted for now. Disabled.
-// func f0[A any, B interface{~C}, C interface{~D}, D interface{~A}](A, B, C, D)
-// func _() {
-// f := f0[string]
-// f("a", "b", "c", "d")
-// f0("a", "b", "c", "d")
-// }
-//
-// func f1[A any, B interface{~A}](A, B)
-// func _() {
-// f := f1[int]
-// f(int(0), int(0))
-// f1(int(0), int(0))
-// }
+func f0[A any, B interface{~*C}, C interface{~*D}, D interface{~*A}](A, B, C, D) {}
+func _() {
+ f := f0[string]
+ f("a", nil, nil, nil)
+ f0("a", nil, nil, nil)
+}
+
+func f1[A any, B interface{~*A}](A, B) {}
+func _() {
+ f := f1[int]
+ f(int(0), new(int))
+ f1(int(0), new(int))
+}
func f2[A any, B interface{~[]A}](A, B) {}
func _() {
diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.go2 b/src/cmd/compile/internal/types2/testdata/check/issues.go2
index effc2db7ae..8608473135 100644
--- a/src/cmd/compile/internal/types2/testdata/check/issues.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/issues.go2
@@ -145,8 +145,8 @@ type List3[TElem any] struct {
}
// Infinite generic type declarations must lead to an error.
-type inf1[T any] struct{ _ inf1 /* ERROR illegal cycle */ [T] }
-type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
+type inf1 /* ERROR illegal cycle */ [T any] struct{ _ inf1[T] }
+type inf2 /* ERROR illegal cycle */ [T any] struct{ inf2[T] }
// The implementation of conversions T(x) between integers and floating-point
// numbers checks that both T and x have either integer or floating-point
@@ -223,6 +223,13 @@ func _[T interface{ ~func() }](f T) {
go f()
}
+type F1 func()
+type F2 func()
+func _[T interface{ func()|F1|F2 }](f T) {
+ f()
+ go f()
+}
+
// We must compare against the underlying type of type list entries
// when checking if a constraint is satisfied by a type. The under-
// lying type of each type list entry must be computed after the
diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.src b/src/cmd/compile/internal/types2/testdata/check/issues.src
index d83a95af0e..dfd51006b9 100644
--- a/src/cmd/compile/internal/types2/testdata/check/issues.src
+++ b/src/cmd/compile/internal/types2/testdata/check/issues.src
@@ -132,12 +132,12 @@ func issue10260() {
var x I1
x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
- _ = x /* ERROR .* cannot have dynamic type T1 \(missing method foo \(foo has pointer receiver\)\) */ .(T1)
+ _ = x. /* ERROR impossible type assertion: x.\(T1\)\n\tT1 does not implement I1 \(foo method has pointer receiver\) */ (T1)
T1{}.foo /* ERROR cannot call pointer method foo on T1 */ ()
x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ ()
- _ = i2 /* ERROR i2 .* cannot have dynamic type \*T1 \(wrong type for method foo \(have func\(\), want func\(x int\)\)\) */ .(*T1)
+ _ = i2. /* ERROR impossible type assertion: i2.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo: have func\(\), want func\(x int\)\) */ (*T1)
i1 = i0 /* ERROR cannot use .* missing method foo */
i1 = t0 /* ERROR cannot use .* missing method foo */
diff --git a/src/cmd/compile/internal/types2/testdata/check/linalg.go2 b/src/cmd/compile/internal/types2/testdata/check/linalg.go2
index efc090a1d1..f02e773dbe 100644
--- a/src/cmd/compile/internal/types2/testdata/check/linalg.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/linalg.go2
@@ -4,8 +4,6 @@
package linalg
-import "math"
-
// Numeric is type bound that matches any numeric type.
// It would likely be in a constraints package in the standard library.
type Numeric interface {
@@ -52,32 +50,33 @@ type Complex interface {
~complex64 | ~complex128
}
-// OrderedAbs is a helper type that defines an Abs method for
-// ordered numeric types.
-type OrderedAbs[T OrderedNumeric] T
-
-func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
- if a < 0 {
- return -a
- }
- return a
-}
-
-// ComplexAbs is a helper type that defines an Abs method for
-// complex types.
-type ComplexAbs[T Complex] T
-
-func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
- r := float64(real(a))
- i := float64(imag(a))
- d := math.Sqrt(r * r + i * i)
- return ComplexAbs[T](complex(d, 0))
-}
-
-func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
- return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
-}
-
-func ComplexAbsDifference[T Complex](a, b T) T {
- return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // OrderedAbs is a helper type that defines an Abs method for
+// // ordered numeric types.
+// type OrderedAbs[T OrderedNumeric] T
+//
+// func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
+// if a < 0 {
+// return -a
+// }
+// return a
+// }
+//
+// // ComplexAbs is a helper type that defines an Abs method for
+// // complex types.
+// type ComplexAbs[T Complex] T
+//
+// func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
+// r := float64(real(a))
+// i := float64(imag(a))
+// d := math.Sqrt(r * r + i * i)
+// return ComplexAbs[T](complex(d, 0))
+// }
+//
+// func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
+// return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
+// }
+//
+// func ComplexAbsDifference[T Complex](a, b T) T {
+// return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
+// }
diff --git a/src/cmd/compile/internal/types2/testdata/check/stmt0.src b/src/cmd/compile/internal/types2/testdata/check/stmt0.src
index d744f2ba81..5ec37b4ace 100644
--- a/src/cmd/compile/internal/types2/testdata/check/stmt0.src
+++ b/src/cmd/compile/internal/types2/testdata/check/stmt0.src
@@ -715,7 +715,7 @@ func typeswitches() {
var t I
switch t.(type) {
case T:
- case T1 /* ERROR "missing method m" */ :
+ case T1 /* ERROR "missing m method" */ :
case T2 /* ERROR "wrong type for method m" */ :
case I2 /* STRICT "wrong type for method m" */ : // only an error in strict mode (issue 8561)
}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeinference.go2 b/src/cmd/compile/internal/types2/testdata/check/typeinference.go2
new file mode 100644
index 0000000000..8876ccaa4e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/check/typeinference.go2
@@ -0,0 +1,47 @@
+// Copyright 2021 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 typeInference
+
+// basic inference
+type Tb[P ~*Q, Q any] int
+func _() {
+ var x Tb[*int]
+ var y Tb[*int, int]
+ x = y
+ _ = x
+}
+
+// recursive inference
+type Tr[A any, B ~*C, C ~*D, D ~*A] int
+func _() {
+ var x Tr[string]
+ var y Tr[string, ***string, **string, *string]
+ var z Tr[int, ***int, **int, *int]
+ x = y
+ x = z // ERROR cannot use z .* as Tr
+ _ = x
+}
+
+// other patterns of inference
+type To0[A any, B ~[]A] int
+type To1[A any, B ~struct{a A}] int
+type To2[A any, B ~[][]A] int
+type To3[A any, B ~[3]*A] int
+type To4[A any, B any, C ~struct{a A; b B}] int
+func _() {
+ var _ To0[int]
+ var _ To1[int]
+ var _ To2[int]
+ var _ To3[int]
+ var _ To4[int, string]
+}
+
+// failed inference
+type Tf0[A, B any] int
+type Tf1[A any, B ~struct{a A; c C}, C any] int
+func _() {
+ var _ Tf0 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 2 type parameters */ [int]
+ var _ Tf1 /* ERROR cannot infer B */ /* ERROR got 1 arguments but 3 type parameters */ [int]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeinst.go2 b/src/cmd/compile/internal/types2/testdata/check/typeinst.go2
index 3fab2cb9ad..a3d1b5e28f 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeinst.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeinst.go2
@@ -8,7 +8,8 @@ type myInt int
// Parameterized type declarations
-type T1[P any] P
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+type T1[P any] P // ERROR cannot use a type parameter as RHS in type declaration
type T2[P any] struct {
f P
@@ -19,7 +20,7 @@ type List[P any] []P
// Alias type declarations cannot have type parameters.
// Issue #46477 proposses to change that.
-type A1[P any] = /* ERROR cannot be alias */ P
+type A1[P any] = /* ERROR cannot be alias */ struct{}
// Pending clarification of #46477 we disallow aliases
// of generic types.
@@ -57,5 +58,5 @@ var _ T3[int] = T3[int](List[int]{1, 2, 3})
// Self-recursive generic types are not permitted
-type self1[P any] self1 /* ERROR illegal cycle */ [P]
+type self1 /* ERROR illegal cycle */ [P any] self1[P]
type self2[P any] *self2[P] // this is ok
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2 b/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
index 49f48c7283..cd56c81bb9 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
@@ -87,25 +87,27 @@ type NumericAbs[T any] interface {
func AbsDifference[T NumericAbs[T]](x T) { panic(0) }
-type OrderedAbs[T any] T
-
-func (a OrderedAbs[T]) Abs() OrderedAbs[T]
-
-func OrderedAbsDifference[T any](x T) {
- AbsDifference(OrderedAbs[T](x))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type OrderedAbs[T any] T
+//
+// func (a OrderedAbs[T]) Abs() OrderedAbs[T]
+//
+// func OrderedAbsDifference[T any](x T) {
+// AbsDifference(OrderedAbs[T](x))
+// }
// same code, reduced to essence
func g[P interface{ m() P }](x P) { panic(0) }
-type T4[P any] P
-
-func (_ T4[P]) m() T4[P]
-
-func _[Q any](x Q) {
- g(T4[Q](x))
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type T4[P any] P
+//
+// func (_ T4[P]) m() T4[P]
+//
+// func _[Q any](x Q) {
+// g(T4[Q](x))
+// }
// Another test case that caused problems in the past
@@ -170,7 +172,7 @@ type _ interface {
~struct{f int} | ~struct{g int} | ~struct /* ERROR overlapping terms */ {f int}
}
-// Interface type lists can contain any type, incl. *Named types.
+// Interface term lists can contain any type, incl. *Named types.
// Verify that we use the underlying type to compute the operational type.
type MyInt int
func add1[T interface{MyInt}](x T) T {
@@ -182,9 +184,9 @@ func double[T interface{MyInt|MyString}](x T) T {
return x + x
}
-// Embedding of interfaces with type lists leads to interfaces
-// with type lists that are the intersection of the embedded
-// type lists.
+// Embedding of interfaces with term lists leads to interfaces
+// with term lists that are the intersection of the embedded
+// term lists.
type E0 interface {
~int | ~bool | ~string
@@ -226,10 +228,10 @@ type I012 interface {
}
func f012[T I012]() {}
-var _ = f012[int /* ERROR does not satisfy I012 */ ]
-var _ = f012[bool /* ERROR does not satisfy I012 */ ]
-var _ = f012[string /* ERROR does not satisfy I012 */ ]
-var _ = f012[float64 /* ERROR does not satisfy I012 */ ]
+var _ = f012[int /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[bool /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[string /* ERROR does not satisfy I012.*type set is empty */ ]
+var _ = f012[float64 /* ERROR does not satisfy I012.*type set is empty */ ]
type I12 interface {
E1
@@ -252,3 +254,27 @@ var _ = f0_[int]
var _ = f0_[bool /* ERROR does not satisfy I0_ */ ]
var _ = f0_[string /* ERROR does not satisfy I0_ */ ]
var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ]
+
+// Using a function instance as a type is an error.
+var _ f0 // ERROR not a type
+var _ f0 /* ERROR not a type */ [int]
+
+// Empty type sets can only be satisfied by empty type sets.
+type none interface {
+ // force an empty type set
+ int
+ string
+}
+
+func ff[T none]() {}
+func gg[T any]() {}
+func hh[T ~int]() {}
+
+func _[T none]() {
+ _ = ff[int /* ERROR int does not satisfy none \(constraint type set is empty\) */ ]
+ _ = ff[T] // pathological but ok because T's type set is empty, too
+ _ = gg[int]
+ _ = gg[T]
+ _ = hh[int]
+ _ = hh[T]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeinstcycles.go2 b/src/cmd/compile/internal/types2/testdata/check/typeinstcycles.go2
new file mode 100644
index 0000000000..74fe19195a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/check/typeinstcycles.go2
@@ -0,0 +1,11 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "unsafe"
+
+func F1[T any](_ [unsafe.Sizeof(F1[int])]T) (res T) { return }
+func F2[T any](_ T) (res [unsafe.Sizeof(F2[string])]int) { return }
+func F3[T any](_ [unsafe.Sizeof(F1[string])]int) {}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
index 29c25b0bb4..a1bf6c262f 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
@@ -114,6 +114,14 @@ func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERR
func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
+// indexing with strings and non-variable arrays (assignment not permitted)
+func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 }
+func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 }
+func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 }
+func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 }
+func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 }
+
// slicing
func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] }
@@ -123,6 +131,11 @@ func _[T interface{ ~[]byte }] (x T, i, j, k int) { var _ T = x[i:j:k] }
func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x[i:j] }
func _[T interface{ ~string }] (x T, i, j, k int) { var _ T = x /* ERROR 3-index slice of string */ [i:j:k] }
+type myByte1 []byte
+type myByte2 []byte
+func _[T interface{ []byte | myByte1 | myByte2 }] (x T, i, j, k int) { var _ T = x[i:j:k] }
+func _[T interface{ []byte | myByte1 | []int }] (x T, i, j, k int) { var _ T = x[ /* ERROR no single underlying type */ i:j:k] }
+
// len/cap built-ins
func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) }
@@ -184,7 +197,7 @@ func _[
for _, _ = range b1 {}
var b2 B2
- for range b2 /* ERROR cannot range over b2 .* no structural type */ {}
+ for range b2 {}
var c0 chan int
for range c0 {}
@@ -197,7 +210,7 @@ func _[
for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
var c2 C2
- for range c2 /* ERROR cannot range over c2 .* no structural type */ {}
+ for range c2 /* ERROR cannot range over c2.*no single underlying type */ {}
var c3 C3
for range c3 /* ERROR receive from send-only channel */ {}
@@ -213,7 +226,7 @@ func _[
for _, _ = range s1 {}
var s2 S2
- for range s2 /* ERROR cannot range over s2 .* no structural type */ {}
+ for range s2 /* ERROR cannot range over s2.*no single underlying type */ {}
var a0 []int
for range a0 {}
@@ -226,7 +239,7 @@ func _[
for _, _ = range a1 {}
var a2 A2
- for range a2 /* ERROR cannot range over a2 .* no structural type */ {}
+ for range a2 /* ERROR cannot range over a2.*no single underlying type */ {}
var p0 *[10]int
for range p0 {}
@@ -239,7 +252,7 @@ func _[
for _, _ = range p1 {}
var p2 P2
- for range p2 /* ERROR cannot range over p2 .* no structural type */ {}
+ for range p2 /* ERROR cannot range over p2.*no single underlying type */ {}
var m0 map[string]int
for range m0 {}
@@ -252,7 +265,7 @@ func _[
for _, _ = range m1 {}
var m2 M2
- for range m2 /* ERROR cannot range over m2 .* no structural type */ {}
+ for range m2 /* ERROR cannot range over m2.*no single underlying type */ {}
}
// type inference checks
@@ -348,15 +361,16 @@ func _() {
// the previous example was extracted from
-func f12[T interface{m() T}]() {}
-
-type A[T any] T
-
-func (a A[T]) m() A[T]
-
-func _[T any]() {
- f12[A[T]]()
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// func f12[T interface{m() T}]() {}
+//
+// type A[T any] T
+//
+// func (a A[T]) m() A[T]
+//
+// func _[T any]() {
+// f12[A[T]]()
+// }
// method expressions
diff --git a/src/cmd/compile/internal/types2/testdata/examples/inference.go2 b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
index e169aec746..4eb18eb239 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/inference.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
@@ -99,3 +99,26 @@ func _() {
related2(1.0, []int{})
related2( /* ERROR does not satisfy */ float64(1.0), []int{}) // TODO(gri) fix error position
}
+
+type List[P any] []P
+
+func related3[Elem any, Slice []Elem | List[Elem]]() Slice { return nil }
+
+func _() {
+ // related3 can be instantiated explicitly
+ related3[int, []int]()
+ related3[byte, List[byte]]()
+
+ // Alternatively, the 2nd type argument can be inferred
+ // from the first one through constraint type inference.
+ related3[int]()
+
+ // The inferred type is the structural type of the Slice
+ // type parameter.
+ var _ []int = related3[int]()
+
+ // It is not the defined parameterized type List.
+ type anotherList []float32
+ var _ anotherList = related3[float32]() // valid
+ var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
+}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/methods.go2 b/src/cmd/compile/internal/types2/testdata/examples/methods.go2
index 4e87041e54..1d76d553dc 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/methods.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/methods.go2
@@ -6,8 +6,6 @@
package p
-import "unsafe"
-
// Parameterized types may have methods.
type T1[A any] struct{ a A }
@@ -97,17 +95,18 @@ type T0 struct{}
func (T0) _() {}
func (T1[A]) _() {}
-// A generic receiver type may constrain its type parameter such
-// that it must be a pointer type. Such receiver types are not
-// permitted.
-type T3a[P interface{ ~int | ~string | ~float64 }] P
-
-func (T3a[_]) m() {} // this is ok
-
-type T3b[P interface{ ~unsafe.Pointer }] P
-
-func (T3b /* ERROR invalid receiver */ [_]) m() {}
-
-type T3c[P interface{ *int | *string }] P
-
-func (T3c /* ERROR invalid receiver */ [_]) m() {}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // A generic receiver type may constrain its type parameter such
+// // that it must be a pointer type. Such receiver types are not
+// // permitted.
+// type T3a[P interface{ ~int | ~string | ~float64 }] P
+//
+// func (T3a[_]) m() {} // this is ok
+//
+// type T3b[P interface{ ~unsafe.Pointer }] P
+//
+// func (T3b /* ERROR invalid receiver */ [_]) m() {}
+//
+// type T3c[P interface{ *int | *string }] P
+//
+// func (T3c /* ERROR invalid receiver */ [_]) m() {}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/types.go2 b/src/cmd/compile/internal/types2/testdata/examples/types.go2
index 55b1b0da57..077fcfdbb7 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/types.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/types.go2
@@ -185,12 +185,13 @@ type _ struct {
// _ = y < 0
// }
-// It is not permitted to declare a local type whose underlying
-// type is a type parameter not declared by that type declaration.
-func _[T any]() {
- type _ T // ERROR cannot use function type parameter T as RHS in type declaration
- type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // It is not permitted to declare a local type whose underlying
+// // type is a type parameter not declared by that type declaration.
+// func _[T any]() {
+// type _ T // ERROR cannot use function type parameter T as RHS in type declaration
+// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
+// }
// As a special case, an explicit type argument may be omitted
// from a type parameter bound if the type bound expects exactly
@@ -275,8 +276,8 @@ func _() {
// Type parameters are never const types, i.e., it's
// not possible to declare a constant of type parameter type.
-// (If a type list contains just a single const type, we could
-// allow it, but such type lists don't make much sense in the
+// (If a type set contains just a single const type, we could
+// allow it, but such type sets don't make much sense in the
// first place.)
func _[T interface{~int|~float64}]() {
// not valid
@@ -304,3 +305,11 @@ func _[P interface{ ~[]E }, E interface{ map[string]P } ]() P {
return P{E{"foo": x}}
return P{{"foo": x}, {}}
}
+
+// This is a degenerate case with a singleton type set, but we can create
+// composite literals even if the structural type is a defined type.
+type MyInts []int
+
+func _[P MyInts]() P {
+ return P{}
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
index 8e6bd974e8..9a98f7f955 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
@@ -37,8 +37,8 @@ func main7() { var _ foo7 = x7[int]{} }
// func main8() {}
// crash 9
-type foo9[A any] interface { foo9 /* ERROR illegal cycle */ [A] }
-func _() { var _ = new(foo9 /* ERROR illegal cycle */ [int]) }
+type foo9 /* ERROR illegal cycle */ [A any] interface { foo9[A] }
+func _() { var _ = new(foo9[int]) }
// crash 12
var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len /* ERROR must be called */ /* ERROR must be called */ )]c /* ERROR undeclared */ /* ERROR undeclared */
@@ -74,9 +74,10 @@ func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) }
type Z21 /* ERROR illegal cycle */ interface{ Z21 }
func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) }
-// crash 24
-type T24[P any] P
-func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // crash 24
+// type T24[P any] P
+// func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
// crash 25
type T25[A any] int
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
index 85eb0a78fe..8f31012354 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
@@ -4,10 +4,10 @@
package p
-// Do not report a duplicate type error for this type list.
+// Do not report a duplicate type error for this term list.
// (Check types after interfaces have been completed.)
type _ interface {
// TODO(gri) Once we have full type sets we can enable this again.
- // Fow now we don't permit interfaces in type lists.
+ // Fow now we don't permit interfaces in term lists.
// type interface{ Error() string }, interface{ String() string }
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
index fb522733e0..696d9d9bee 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
@@ -4,14 +4,15 @@
package p
-type T[P any] P
-type A = T // ERROR cannot use generic type
-var x A[int]
-var _ A
-
-type B = T[int]
-var y B = x
-var _ B /* ERROR not a generic type */ [int]
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type T[P any] P
+// type A = T // ERROR cannot use generic type
+// var x A[int]
+// var _ A
+//
+// type B = T[int]
+// var y B = x
+// var _ B /* ERROR not a generic type */ [int]
// test case from issue
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2
index 0da6e103fd..114646786d 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2
@@ -3,19 +3,21 @@
// license that can be found in the LICENSE file.
// Check "infinite expansion" cycle errors across instantiated types.
+// We can't detect these errors anymore at the moment. See #48962 for
+// details.
package p
-type E0[P any] P
+type E0[P any] []P
type E1[P any] *P
type E2[P any] struct{ _ P }
type E3[P any] struct{ _ *P }
-type T0 /* ERROR illegal cycle */ struct {
+type T0 /* illegal cycle */ struct {
_ E0[T0]
}
-type T0_ /* ERROR illegal cycle */ struct {
+type T0_ /* illegal cycle */ struct {
E0[T0_]
}
@@ -23,7 +25,7 @@ type T1 struct {
_ E1[T1]
}
-type T2 /* ERROR illegal cycle */ struct {
+type T2 /* illegal cycle */ struct {
_ E2[T2]
}
@@ -33,7 +35,7 @@ type T3 struct {
// some more complex cases
-type T4 /* ERROR illegal cycle */ struct {
+type T4 /* illegal cycle */ struct {
_ E0[E2[T4]]
}
@@ -41,7 +43,7 @@ type T5 struct {
_ E0[E2[E0[E1[E2[[10]T5]]]]]
}
-type T6 /* ERROR illegal cycle */ struct {
+type T6 /* illegal cycle */ struct {
_ E0[[10]E2[E0[E2[E2[T6]]]]]
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
index 441fb4cb34..80148fe481 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
@@ -4,9 +4,10 @@
package P
-// It is not permitted to declare a local type whose underlying
-// type is a type parameters not declared by that type declaration.
-func _[T any]() {
- type _ T // ERROR cannot use function type parameter T as RHS in type declaration
- type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// // It is not permitted to declare a local type whose underlying
+// // type is a type parameters not declared by that type declaration.
+// func _[T any]() {
+// type _ T // ERROR cannot use function type parameter T as RHS in type declaration
+// type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
+// }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
index f25b9d2b26..9963d2ee00 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
@@ -5,7 +5,7 @@
package issue45985
// TODO(gri): this error should be on app[int] below.
-func app[S /* ERROR "type S = S does not match" */ interface{ ~[]T }, T any](s S, e T) S {
+func app[S /* ERROR "S does not match" */ interface{ ~[]T }, T any](s S, e T) S {
return append(s, e)
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2
index 77281a19a2..ccf4bcf782 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2
@@ -19,7 +19,7 @@ func _[P comparable,
_ = f[R /* ERROR R has no constraints */ ]
_ = g[int]
- _ = g[P /* ERROR P has no type constraints */ ]
+ _ = g[P /* ERROR P does not satisfy interface{interface{comparable; ~int\|~string} */ ]
_ = g[Q]
_ = g[func( /* ERROR does not satisfy comparable */ )]
_ = g[R /* ERROR R has no constraints */ ]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
index af52056bef..6a2e787bf9 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
@@ -4,15 +4,16 @@
package p
-type T1[P any] P
-
-func (T1[_]) m() {}
-
-func _[P any](x *T1[P]) {
- // x.m exists because x is of type *T1 where T1 is a defined type
- // (even though under(T1) is a type parameter)
- x.m()
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type T1[P any] P
+//
+// func (T1[_]) m() {}
+//
+// func _[P any](x *T1[P]) {
+// // x.m exists because x is of type *T1 where T1 is a defined type
+// // (even though under(T1) is a type parameter)
+// x.m()
+// }
func _[P interface{ m() }](x P) {
@@ -40,29 +41,31 @@ type Barer[t any] interface {
Bar(t)
}
-type Foo1[t any] t
-type Bar[t any] t
-
-func (l Foo1[t]) Foo(v Barer[t]) { v.Bar(t(l)) }
-func (b *Bar[t]) Bar(l t) { *b = Bar[t](l) }
-
-func _[t any](f Fooer1[t]) t {
- var b Bar[t]
- f.Foo(&b)
- return t(b)
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type Foo1[t any] t
+// type Bar[t any] t
+//
+// func (l Foo1[t]) Foo(v Barer[t]) { v.Bar(t(l)) }
+// func (b *Bar[t]) Bar(l t) { *b = Bar[t](l) }
+//
+// func _[t any](f Fooer1[t]) t {
+// var b Bar[t]
+// f.Foo(&b)
+// return t(b)
+// }
// Test case 2 from issue
-type Fooer2[t any] interface {
- Foo()
-}
-
-type Foo2[t any] t
-
-func (f *Foo2[t]) Foo() {}
-
-func _[t any](v t) {
- var f = Foo2[t](v)
- _ = Fooer2[t](&f)
-}
+// For now, a lone type parameter is not permitted as RHS in a type declaration (issue #45639).
+// type Fooer2[t any] interface {
+// Foo()
+// }
+//
+// type Foo2[t any] t
+//
+// func (f *Foo2[t]) Foo() {}
+//
+// func _[t any](v t) {
+// var f = Foo2[t](v)
+// _ = Fooer2[t](&f)
+// }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48018.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48018.go2
new file mode 100644
index 0000000000..e6ccc6b9be
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48018.go2
@@ -0,0 +1,20 @@
+// Copyright 2021 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 Box[A any] struct {
+ value A
+}
+
+func Nest[A /* ERROR instantiation cycle */ any](b Box[A], n int) interface{} {
+ if n == 0 {
+ return b
+ }
+ return Nest(Box[Box[A]]{b}, n-1)
+}
+
+func main() {
+ Nest(Box[int]{0}, 10)
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2
index 5fefcaf22b..2d908f4c8b 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2
@@ -9,3 +9,8 @@ func g() {
var i int
_ = s /* ERROR invalid operation: s \+ i \(mismatched types string and int\) */ + i
}
+
+func f(i int) int {
+ i /* ERROR invalid operation: i \+= "1" \(mismatched types int and untyped string\) */ += "1"
+ return i
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48703.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48703.go2
new file mode 100644
index 0000000000..8a32c1ecf2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48703.go2
@@ -0,0 +1,27 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "unsafe"
+
+// The actual example from the issue.
+type List[P any] struct{}
+
+func (_ List[P]) m() (_ List[List[P]]) { return }
+
+// Other types of recursion through methods.
+type R[P any] int
+
+func (*R[R /* ERROR must be an identifier */ [int]]) m0() {}
+func (R[P]) m1(R[R[P]]) {}
+func (R[P]) m2(R[*P]) {}
+func (R[P]) m3([unsafe.Sizeof(new(R[P]))]int) {}
+func (R[P]) m4([unsafe.Sizeof(new(R[R[P]]))]int) {}
+
+// Mutual recursion
+type M[P any] int
+
+func (R[P]) m5(M[M[P]]) {}
+func (M[P]) m(R[R[P]]) {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48951.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48951.go2
new file mode 100644
index 0000000000..cf02cc130a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48951.go2
@@ -0,0 +1,21 @@
+// Copyright 2020 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 (
+ A1 /* ERROR illegal cycle */ [P any] [10]A1[P]
+ A2 /* ERROR illegal cycle */ [P any] [10]A2[*P]
+ A3[P any] [10]*A3[P]
+
+ L1[P any] []L1[P]
+
+ S1 /* ERROR illegal cycle */ [P any] struct{ f S1[P] }
+ S2 /* ERROR illegal cycle */ [P any] struct{ f S2[*P] } // like example in issue
+ S3[P any] struct{ f *S3[P] }
+
+ I1 /* ERROR illegal cycle */ [P any] interface{ I1[P] }
+ I2 /* ERROR illegal cycle */ [P any] interface{ I2[*P] }
+ I3[P any] interface{ *I3 /* ERROR interface contains type constraints */ [P] }
+)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48974.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48974.go2
new file mode 100644
index 0000000000..d8ff7c8cf4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48974.go2
@@ -0,0 +1,22 @@
+// Copyright 2021 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 Fooer interface {
+ Foo()
+}
+
+type Fooable[F /* ERROR instantiation cycle */ Fooer] struct {
+ ptr F
+}
+
+func (f *Fooable[F]) Adapter() *Fooable[*FooerImpl[F]] {
+ return &Fooable[*FooerImpl[F]]{&FooerImpl[F]{}}
+}
+
+type FooerImpl[F Fooer] struct {
+}
+
+func (fi *FooerImpl[F]) Foo() {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49003.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49003.go
new file mode 100644
index 0000000000..ece1a27bb9
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49003.go
@@ -0,0 +1,10 @@
+// Copyright 2021 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(s string) int {
+ for range s {
+ }
+} // ERROR missing return
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49005.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49005.go
new file mode 100644
index 0000000000..6225e68488
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49005.go
@@ -0,0 +1,34 @@
+// Copyright 2021 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 is tested when running "go test -run Manual"
+// without source arguments. Use for one-off debugging.
+
+package p
+
+type T1 interface{ M() }
+
+func F1() T1
+
+var _ = F1().(*X1 /* ERROR undeclared name: X1 */)
+
+func _() {
+ switch F1().(type) {
+ case *X1 /* ERROR undeclared name: X1 */ :
+ }
+}
+
+type T2 interface{ M() }
+
+func F2() T2
+
+var _ = F2(). /* ERROR impossible type assertion: F2\(\).\(\*X2\)\n\t\*X2 does not implement T2 \(missing M method\) */ (*X2)
+
+type X2 struct{}
+
+func _() {
+ switch F2().(type) {
+ case * /* ERROR impossible type switch case: \*X2\n\tF2\(\) \(value of type T2\) cannot have dynamic type \*X2 \(missing M method\) */ X2:
+ }
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49043.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49043.go2
new file mode 100644
index 0000000000..c37b0f1267
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49043.go2
@@ -0,0 +1,24 @@
+// Copyright 2021 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
+
+// The example from the issue.
+type (
+ N /* ERROR illegal cycle */ [P any] M[P]
+ M[P any] N[P]
+)
+
+// A slightly more complicated case.
+type (
+ A /* ERROR illegal cycle */ [P any] B[P]
+ B[P any] C[P]
+ C[P any] A[P]
+)
+
+// Confusing but valid (note that `type T *T` is valid).
+type (
+ N1[P any] *M1[P]
+ M1[P any] *N1[P]
+)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2
new file mode 100644
index 0000000000..524a0cbae3
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49242.go2
@@ -0,0 +1,27 @@
+// Copyright 2021 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 _[P int](x P) int {
+ return x // ERROR cannot use x .* as int value in return statement
+}
+
+func _[P int]() int {
+ return P /* ERROR cannot use P\(1\) .* as int value in return statement */ (1)
+}
+
+func _[P int](x int) P {
+ return x // ERROR cannot use x .* as P value in return statement
+}
+
+func _[P, Q any](x P) Q {
+ return x // ERROR cannot use x .* as Q value in return statement
+}
+
+// test case from issue
+func F[G interface{ uint }]() int {
+ f := func(uint) int { return 0 }
+ return f(G /* ERROR cannot use G\(1\) .* as uint value in argument to f */ (1))
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2
new file mode 100644
index 0000000000..3f25e0ee35
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49247.go2
@@ -0,0 +1,20 @@
+// Copyright 2021 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 integer interface {
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
+}
+
+func Add1024[T integer](s []T) {
+ for i, v := range s {
+ s[i] = v + 1024 // ERROR cannot convert 1024 \(untyped int constant\) to T
+ }
+}
+
+func f[T interface{ int8 }]() {
+ println(T(1024 /* ERROR cannot convert 1024 \(untyped int value\) to T */))
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49296.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49296.go2
new file mode 100644
index 0000000000..8f52acc8a4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49296.go2
@@ -0,0 +1,20 @@
+// Copyright 2021 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 _[
+ T0 any,
+ T1 []int,
+ T2 ~float64 | ~complex128 | chan int,
+]() {
+ _ = T0(nil /* ERROR cannot convert untyped nil to T0 */ )
+ _ = T1(1 /* ERROR cannot convert 1 .* to T1 */ )
+ _ = T2(2 /* ERROR cannot convert 2 .* to T2 */ )
+}
+
+// test case from issue
+func f[T interface{[]int}]() {
+ _ = T(1 /* ERROR cannot convert */ )
+}
diff --git a/src/cmd/compile/internal/types2/testdata/spec/assignability.go2 b/src/cmd/compile/internal/types2/testdata/spec/assignability.go2
new file mode 100644
index 0000000000..fb28358bbb
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/spec/assignability.go2
@@ -0,0 +1,264 @@
+// Copyright 2021 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 assignability
+
+// See the end of this package for the declarations
+// of the types and variables used in these tests.
+
+// "x's type is identical to T"
+func _[TP any](X TP) {
+ b = b
+ a = a
+ l = l
+ s = s
+ p = p
+ f = f
+ i = i
+ m = m
+ c = c
+ d = d
+
+ B = B
+ A = A
+ L = L
+ S = S
+ P = P
+ F = F
+ I = I
+ M = M
+ C = C
+ D = D
+ X = X
+}
+
+// "x's type V and T have identical underlying types
+// and at least one of V or T is not a named type."
+// (here a named type is a type with a name)
+func _[TP1, TP2 Interface](X1 TP1, X2 TP2) {
+ b = B // ERROR cannot use B .* as int value
+ a = A
+ l = L
+ s = S
+ p = P
+ f = F
+ i = I
+ m = M
+ c = C
+ d = D
+
+ B = b // ERROR cannot use b .* as Basic value
+ A = a
+ L = l
+ S = s
+ P = p
+ F = f
+ I = i
+ M = m
+ C = c
+ D = d
+ X1 = i // ERROR cannot use i .* as TP1 value
+ X1 = X2 // ERROR cannot use X2 .* as TP1 value
+}
+
+// "T is an interface type and x implements T and T is not a type parameter"
+func _[TP Interface](X TP) {
+ i = d // ERROR missing method m
+ i = D
+ i = X
+ X = i // ERROR cannot use i .* as TP value
+}
+
+// "x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type"
+// (here a named type is a type with a name)
+type (
+ _SendChan = chan<- int
+ _RecvChan = <-chan int
+
+ SendChan _SendChan
+ RecvChan _RecvChan
+)
+
+func _[
+ _CC ~_Chan,
+ _SC ~_SendChan,
+ _RC ~_RecvChan,
+
+ CC Chan,
+ SC SendChan,
+ RC RecvChan,
+]() {
+ var (
+ _ _SendChan = c
+ _ _RecvChan = c
+ _ _Chan = c
+
+ _ _SendChan = C
+ _ _RecvChan = C
+ _ _Chan = C
+
+ _ SendChan = c
+ _ RecvChan = c
+ _ Chan = c
+
+ _ SendChan = C // ERROR cannot use C .* as SendChan value
+ _ RecvChan = C // ERROR cannot use C .* as RecvChan value
+ _ Chan = C
+ _ Chan = make /* ERROR cannot use make\(chan Basic\) .* as Chan value */ (chan Basic)
+ )
+
+ var (
+ _ _CC = C // ERROR cannot use C .* as _CC value
+ _ _SC = C // ERROR cannot use C .* as _SC value
+ _ _RC = C // ERROR cannot use C .* as _RC value
+
+ _ CC = _CC /* ERROR cannot use _CC\(nil\) .* as CC value */ (nil)
+ _ SC = _CC /* ERROR cannot use _CC\(nil\) .* as SC value */ (nil)
+ _ RC = _CC /* ERROR cannot use _CC\(nil\) .* as RC value */ (nil)
+
+ _ CC = C // ERROR cannot use C .* as CC value
+ _ SC = C // ERROR cannot use C .* as SC value
+ _ RC = C // ERROR cannot use C .* as RC value
+ )
+}
+
+// "x's type V is not a named type and T is a type parameter, and x is assignable to each specific type in T's type set."
+func _[
+ TP0 any,
+ TP1 ~_Chan,
+ TP2 ~chan int | ~chan byte,
+]() {
+ var (
+ _ TP0 = c // ERROR cannot use c .* as TP0 value
+ _ TP0 = C // ERROR cannot use C .* as TP0 value
+ _ TP1 = c
+ _ TP1 = C // ERROR cannot use C .* as TP1 value
+ _ TP2 = c // ERROR .* cannot assign chan int to chan byte
+ )
+}
+
+// "x's type V is a type parameter and T is not a named type, and values x' of each specific type in V's type set are assignable to T."
+func _[
+ TP0 Interface,
+ TP1 ~_Chan,
+ TP2 ~chan int | ~chan byte,
+](X0 TP0, X1 TP1, X2 TP2) {
+ i = X0
+ I = X0
+ c = X1
+ C = X1 // ERROR cannot use X1 .* as Chan value
+ c = X2 // ERROR .* cannot assign chan byte \(in TP2\) to chan int
+}
+
+// "x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type"
+func _[TP Interface](X TP) {
+ b = nil // ERROR cannot use untyped nil
+ a = nil // ERROR cannot use untyped nil
+ l = nil
+ s = nil // ERROR cannot use untyped nil
+ p = nil
+ f = nil
+ i = nil
+ m = nil
+ c = nil
+ d = nil // ERROR cannot use untyped nil
+
+ B = nil // ERROR cannot use untyped nil
+ A = nil // ERROR cannot use untyped nil
+ L = nil
+ S = nil // ERROR cannot use untyped nil
+ P = nil
+ F = nil
+ I = nil
+ M = nil
+ C = nil
+ D = nil // ERROR cannot use untyped nil
+ X = nil // ERROR cannot use untyped nil
+}
+
+// "x is an untyped constant representable by a value of type T"
+func _[
+ Int8 ~int8,
+ Int16 ~int16,
+ Int32 ~int32,
+ Int64 ~int64,
+ Int8_16 ~int8 | ~int16,
+](
+ i8 Int8,
+ i16 Int16,
+ i32 Int32,
+ i64 Int64,
+ i8_16 Int8_16,
+) {
+ b = 42
+ b = 42.0
+ // etc.
+
+ i8 = -1 << 7
+ i8 = 1<<7 - 1
+ i16 = -1 << 15
+ i16 = 1<<15 - 1
+ i32 = -1 << 31
+ i32 = 1<<31 - 1
+ i64 = -1 << 63
+ i64 = 1<<63 - 1
+
+ i8_16 = -1 << 7
+ i8_16 = 1<<7 - 1
+ i8_16 = - /* ERROR cannot use .* as Int8_16 */ 1 << 15
+ i8_16 = 1 /* ERROR cannot use .* as Int8_16 */ <<15 - 1
+}
+
+// proto-types for tests
+
+type (
+ _Basic = int
+ _Array = [10]int
+ _Slice = []int
+ _Struct = struct{ f int }
+ _Pointer = *int
+ _Func = func(x int) string
+ _Interface = interface{ m() int }
+ _Map = map[string]int
+ _Chan = chan int
+
+ Basic _Basic
+ Array _Array
+ Slice _Slice
+ Struct _Struct
+ Pointer _Pointer
+ Func _Func
+ Interface _Interface
+ Map _Map
+ Chan _Chan
+ Defined _Struct
+)
+
+func (Defined) m() int
+
+// proto-variables for tests
+
+var (
+ b _Basic
+ a _Array
+ l _Slice
+ s _Struct
+ p _Pointer
+ f _Func
+ i _Interface
+ m _Map
+ c _Chan
+ d _Struct
+
+ B Basic
+ A Array
+ L Slice
+ S Struct
+ P Pointer
+ F Func
+ I Interface
+ M Map
+ C Chan
+ D Defined
+)
diff --git a/src/cmd/compile/internal/types2/testdata/spec/conversions.go2 b/src/cmd/compile/internal/types2/testdata/spec/conversions.go2
new file mode 100644
index 0000000000..fde332f34b
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/spec/conversions.go2
@@ -0,0 +1,178 @@
+// Copyright 2021 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 conversions
+
+import "unsafe"
+
+// constant conversions
+
+func _[T ~byte]() T { return 255 }
+func _[T ~byte]() T { return 256 /* ERROR cannot use 256 .* as T value */ }
+
+func _[T ~byte]() {
+ const _ = T /* ERROR T\(0\) .* is not constant */ (0)
+ var _ T = 255
+ var _ T = 256 // ERROR cannot use 256 .* as T value
+}
+
+func _[T ~string]() T { return T('a') }
+func _[T ~int | ~string]() T { return T('a') }
+func _[T ~byte | ~int | ~string]() T { return T(256 /* ERROR cannot convert 256 .* to T */ ) }
+
+// implicit conversions never convert to string
+func _[T ~string]() {
+ var _ string = 0 // ERROR cannot use .* as string value
+ var _ T = 0 // ERROR cannot use .* as T value
+}
+
+// failing const conversions of constants to type parameters report a cause
+func _[
+ T1 any,
+ T2 interface{ m() },
+ T3 ~int | ~float64 | ~bool,
+ T4 ~int | ~string,
+]() {
+ _ = T1(0 /* ERROR cannot convert 0 .* to T1\n\tT1 does not contain specific types */ )
+ _ = T2(1 /* ERROR cannot convert 1 .* to T2\n\tT2 does not contain specific types */ )
+ _ = T3(2 /* ERROR cannot convert 2 .* to T3\n\tcannot convert 2 .* to bool \(in T3\) */ )
+ _ = T4(3.14 /* ERROR cannot convert 3.14 .* to T4\n\tcannot convert 3.14 .* to int \(in T4\) */ )
+}
+
+// "x is assignable to T"
+// - tested via assignability tests
+
+// "x's type and T have identical underlying types if tags are ignored"
+
+func _[X ~int, T ~int](x X) T { return T(x) }
+func _[X struct{f int "foo"}, T struct{f int "bar"}](x X) T { return T(x) }
+
+type Foo struct{f int "foo"}
+type Bar struct{f int "bar"}
+type Far struct{f float64 }
+
+func _[X Foo, T Bar](x X) T { return T(x) }
+func _[X Foo|Bar, T Bar](x X) T { return T(x) }
+func _[X Foo, T Foo|Bar](x X) T { return T(x) }
+func _[X Foo, T Far](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by Foo\) to T\n\tcannot convert Foo \(in X\) to Far \(in T\) */ ) }
+
+// "x's type and T are unnamed pointer types and their pointer base types
+// have identical underlying types if tags are ignored"
+
+func _[X ~*Foo, T ~*Bar](x X) T { return T(x) }
+func _[X ~*Foo|~*Bar, T ~*Bar](x X) T { return T(x) }
+func _[X ~*Foo, T ~*Foo|~*Bar](x X) T { return T(x) }
+func _[X ~*Foo, T ~*Far](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by ~\*Foo\) to T\n\tcannot convert \*Foo \(in X\) to \*Far \(in T\) */ ) }
+
+// Verify that the defined types in constraints are considered for the rule above.
+
+type (
+ B int
+ C int
+ X0 *B
+ T0 *C
+)
+
+func _(x X0) T0 { return T0(x /* ERROR cannot convert */ ) } // non-generic reference
+func _[X X0, T T0](x X) T { return T(x /* ERROR cannot convert */ ) }
+func _[T T0](x X0) T { return T(x /* ERROR cannot convert */ ) }
+func _[X X0](x X) T0 { return T0(x /* ERROR cannot convert */ ) }
+
+// "x's type and T are both integer or floating point types"
+
+func _[X Integer, T Integer](x X) T { return T(x) }
+func _[X Unsigned, T Integer](x X) T { return T(x) }
+func _[X Float, T Integer](x X) T { return T(x) }
+
+func _[X Integer, T Unsigned](x X) T { return T(x) }
+func _[X Unsigned, T Unsigned](x X) T { return T(x) }
+func _[X Float, T Unsigned](x X) T { return T(x) }
+
+func _[X Integer, T Float](x X) T { return T(x) }
+func _[X Unsigned, T Float](x X) T { return T(x) }
+func _[X Float, T Float](x X) T { return T(x) }
+
+func _[X, T Integer|Unsigned|Float](x X) T { return T(x) }
+func _[X, T Integer|~string](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by Integer\|~string\) to T\n\tcannot convert string \(in X\) to int \(in T\) */ ) }
+
+// "x's type and T are both complex types"
+
+func _[X, T Complex](x X) T { return T(x) }
+func _[X, T Float|Complex](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by Float\|Complex\) to T\n\tcannot convert float32 \(in X\) to complex64 \(in T\) */ ) }
+
+// "x is an integer or a slice of bytes or runes and T is a string type"
+
+type myInt int
+type myString string
+
+func _[T ~string](x int) T { return T(x) }
+func _[T ~string](x myInt) T { return T(x) }
+func _[X Integer](x X) string { return string(x) }
+func _[X Integer](x X) myString { return myString(x) }
+func _[X Integer](x X) *string { return (*string)(x /* ERROR cannot convert x \(variable of type X constrained by Integer\) to \*string\n\tcannot convert int \(in X\) to \*string */ ) }
+
+func _[T ~string](x []byte) T { return T(x) }
+func _[T ~string](x []rune) T { return T(x) }
+func _[X ~[]byte, T ~string](x X) T { return T(x) }
+func _[X ~[]rune, T ~string](x X) T { return T(x) }
+func _[X Integer|~[]byte|~[]rune, T ~string](x X) T { return T(x) }
+func _[X Integer|~[]byte|~[]rune, T ~*string](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by Integer\|~\[\]byte\|~\[\]rune\) to T\n\tcannot convert int \(in X\) to \*string \(in T\) */ ) }
+
+// "x is a string and T is a slice of bytes or runes"
+
+func _[T ~[]byte](x string) T { return T(x) }
+func _[T ~[]rune](x string) T { return T(x) }
+func _[T ~[]rune](x *string) T { return T(x /* ERROR cannot convert x \(variable of type \*string\) to T\n\tcannot convert \*string to \[\]rune \(in T\) */ ) }
+
+func _[X ~string, T ~[]byte](x X) T { return T(x) }
+func _[X ~string, T ~[]rune](x X) T { return T(x) }
+func _[X ~string, T ~[]byte|~[]rune](x X) T { return T(x) }
+func _[X ~*string, T ~[]byte|~[]rune](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by ~\*string\) to T\n\tcannot convert \*string \(in X\) to \[\]byte \(in T\) */ ) }
+
+// package unsafe:
+// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
+
+type myUintptr uintptr
+
+func _[X ~uintptr](x X) unsafe.Pointer { return unsafe.Pointer(x) }
+func _[T unsafe.Pointer](x myUintptr) T { return T(x) }
+func _[T unsafe.Pointer](x int64) T { return T(x /* ERROR cannot convert x \(variable of type int64\) to T\n\tcannot convert int64 to unsafe\.Pointer \(in T\) */ ) }
+
+// "and vice versa"
+
+func _[T ~uintptr](x unsafe.Pointer) T { return T(x) }
+func _[X unsafe.Pointer](x X) uintptr { return uintptr(x) }
+func _[X unsafe.Pointer](x X) myUintptr { return myUintptr(x) }
+func _[X unsafe.Pointer](x X) int64 { return int64(x /* ERROR cannot convert x \(variable of type X constrained by unsafe\.Pointer\) to int64\n\tcannot convert unsafe\.Pointer \(in X\) to int64 */ ) }
+
+// "x is a slice, T is a pointer-to-array type,
+// and the slice and array types have identical element types."
+
+func _[X ~[]E, T ~*[10]E, E any](x X) T { return T(x) }
+func _[X ~[]E, T ~[10]E, E any](x X) T { return T(x /* ERROR cannot convert x \(variable of type X constrained by ~\[\]E\) to T\n\tcannot convert \[\]E \(in X\) to \[10\]E \(in T\) */ ) }
+
+// ----------------------------------------------------------------------------
+// The following declarations can be replaced by the exported types of the
+// constraints package once all builders support importing interfaces with
+// type constraints.
+
+type Signed interface {
+ ~int | ~int8 | ~int16 | ~int32 | ~int64
+}
+
+type Unsigned interface {
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
+}
+
+type Integer interface {
+ Signed | Unsigned
+}
+
+type Float interface {
+ ~float32 | ~float64
+}
+
+type Complex interface {
+ ~complex64 | ~complex128
+}
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index 400d6f7128..300c81f5fa 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -9,26 +9,13 @@ package types2
type Type interface {
// Underlying returns the underlying type of a type
// w/o following forwarding chains. Only used by
- // client packages (here for backward-compatibility).
+ // client packages.
Underlying() Type
// String returns a string representation of a type.
String() string
}
-// top represents the top of the type lattice.
-// It is the underlying type of a type parameter that
-// can be satisfied by any type (ignoring methods),
-// because its type constraint contains no restrictions
-// besides methods.
-type top struct{}
-
-// theTop is the singleton top type.
-var theTop = &top{}
-
-func (t *top) Underlying() Type { return t }
-func (t *top) String() string { return TypeString(t, nil) }
-
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
@@ -40,78 +27,47 @@ func under(t Type) Type {
return t
}
-// optype returns a type's operational type. Except for
-// type parameters, the operational type is the same
-// as the underlying type (as returned by under). For
-// Type parameters, the operational type is the structural
-// type, if any; otherwise it's the top type.
-// The result is never the incoming type parameter.
-func optype(typ Type) Type {
- if t := asTypeParam(typ); t != nil {
- // TODO(gri) review accuracy of this comment
- // If the optype is typ, return the top type as we have
- // no information. It also prevents infinite recursion
- // via the asTypeParam converter function. This can happen
- // for a type parameter list of the form:
- // (type T interface { type T }).
- // See also issue #39680.
- if u := t.structuralType(); u != nil {
- assert(u != typ) // "naked" type parameters cannot be embedded
- return u
- }
- return theTop
- }
- return under(typ)
-}
-
-// Converters
-//
-// A converter must only be called when a type is
-// known to be fully set up. A converter returns
-// a type's operational type (see comment for optype)
-// or nil if the type argument is not of the
-// respective type.
+// Convenience converters
func asBasic(t Type) *Basic {
- op, _ := optype(t).(*Basic)
- return op
+ u, _ := under(t).(*Basic)
+ return u
}
func asArray(t Type) *Array {
- op, _ := optype(t).(*Array)
- return op
+ u, _ := under(t).(*Array)
+ return u
}
func asSlice(t Type) *Slice {
- op, _ := optype(t).(*Slice)
- return op
+ u, _ := under(t).(*Slice)
+ return u
}
func asStruct(t Type) *Struct {
- op, _ := optype(t).(*Struct)
- return op
+ u, _ := under(t).(*Struct)
+ return u
}
func asPointer(t Type) *Pointer {
- op, _ := optype(t).(*Pointer)
- return op
+ u, _ := under(t).(*Pointer)
+ return u
}
func asSignature(t Type) *Signature {
- op, _ := optype(t).(*Signature)
- return op
+ u, _ := under(t).(*Signature)
+ return u
}
-// If the argument to asInterface, asNamed, or asTypeParam is of the respective type
-// (possibly after expanding an instance type), these methods return that type.
-// Otherwise the result is nil.
-
-// asInterface does not need to look at optype (type sets don't contain interfaces)
func asInterface(t Type) *Interface {
u, _ := under(t).(*Interface)
return u
}
+// If the argument to asNamed, or asTypeParam is of the respective type
+// (possibly after expanding resolving a *Named type), these methods return that type.
+// Otherwise the result is nil.
+
func asNamed(t Type) *Named {
e, _ := t.(*Named)
if e != nil {
@@ -125,10 +81,32 @@ func asTypeParam(t Type) *TypeParam {
return u
}
-// Exported for the compiler.
+// Helper functions exported for the compiler.
+// These functions assume type checking has completed
+// and Type.Underlying() is returning the fully set up
+// underlying type. Do not use internally.
-func AsPointer(t Type) *Pointer { return asPointer(t) }
-func AsNamed(t Type) *Named { return asNamed(t) }
-func AsSignature(t Type) *Signature { return asSignature(t) }
-func AsInterface(t Type) *Interface { return asInterface(t) }
-func AsTypeParam(t Type) *TypeParam { return asTypeParam(t) }
+func AsPointer(t Type) *Pointer {
+ u, _ := t.Underlying().(*Pointer)
+ return u
+}
+
+func AsNamed(t Type) *Named {
+ u, _ := t.(*Named)
+ return u
+}
+
+func AsSignature(t Type) *Signature {
+ u, _ := t.Underlying().(*Signature)
+ return u
+}
+
+func AsInterface(t Type) *Interface {
+ u, _ := t.Underlying().(*Interface)
+ return u
+}
+
+func AsTypeParam(t Type) *TypeParam {
+ u, _ := t.Underlying().(*TypeParam)
+ return u
+}
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
index f7cdff0180..099bc429c3 100644
--- a/src/cmd/compile/internal/types2/typeparam.go
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -114,15 +114,26 @@ func (t *TypeParam) iface() *Interface {
return ityp
}
-// structuralType returns the structural type of the type parameter's constraint; or nil.
-func (t *TypeParam) structuralType() Type {
- return t.iface().typeSet().structuralType()
+// singleType returns the single type of the type parameter constraint; or nil.
+func (t *TypeParam) singleType() Type {
+ return t.iface().typeSet().singleType()
}
+// hasTerms reports whether the type parameter constraint has specific type terms.
+func (t *TypeParam) hasTerms() bool {
+ return t.iface().typeSet().hasTerms()
+}
+
+// is calls f with the specific type terms of t's constraint and reports whether
+// all calls to f returned true. If there are no specific terms, is
+// returns the result of f(nil).
func (t *TypeParam) is(f func(*term) bool) bool {
return t.iface().typeSet().is(f)
}
+// underIs calls f with the underlying types of the specific type terms
+// of t's constraint and reports whether all calls to f returned true.
+// If there are no specific terms, underIs returns the result of f(nil).
func (t *TypeParam) underIs(f func(Type) bool) bool {
return t.iface().typeSet().underIs(f)
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 8eb43a27e5..445a62f9e0 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -39,7 +39,7 @@ func (s *_TypeSet) IsComparable() bool {
return s.comparable
}
return s.is(func(t *term) bool {
- return Comparable(t.typ)
+ return t != nil && Comparable(t.typ)
})
}
@@ -101,27 +101,29 @@ func (s *_TypeSet) String() string {
// ----------------------------------------------------------------------------
// Implementation
-func (s *_TypeSet) hasTerms() bool { return !s.terms.isAll() }
-func (s *_TypeSet) structuralType() Type { return s.terms.structuralType() }
-func (s *_TypeSet) includes(t Type) bool { return s.terms.includes(t) }
+// hasTerms reports whether the type set has specific type terms.
+func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() }
+
+// singleType returns the single type in s if there is exactly one; otherwise the result is nil.
+func (s *_TypeSet) singleType() Type { return s.terms.singleType() }
+
+// includes reports whether t ∈ s.
+func (s *_TypeSet) includes(t Type) bool { return s.terms.includes(t) }
+
+// subsetOf reports whether s1 ⊆ s2.
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go
-var topTerm = term{false, theTop}
-
+// is calls f with the specific type terms of s and reports whether
+// all calls to f returned true. If there are no specific terms, is
+// returns the result of f(nil).
func (s *_TypeSet) is(f func(*term) bool) bool {
- if len(s.terms) == 0 {
- return false
+ if !s.hasTerms() {
+ return f(nil)
}
for _, t := range s.terms {
- // Terms represent the top term with a nil type.
- // The rest of the type checker uses the top type
- // instead. Convert.
- // TODO(gri) investigate if we can do without this
- if t.typ == nil {
- t = &topTerm
- }
+ assert(t.typ != nil)
if !f(t) {
return false
}
@@ -129,17 +131,17 @@ func (s *_TypeSet) is(f func(*term) bool) bool {
return true
}
+// underIs calls f with the underlying types of the specific type terms
+// of s and reports whether all calls to f returned true. If there are
+// no specific terms, is returns the result of f(nil).
func (s *_TypeSet) underIs(f func(Type) bool) bool {
- if len(s.terms) == 0 {
- return false
+ if !s.hasTerms() {
+ return f(nil)
}
for _, t := range s.terms {
- // see corresponding comment in TypeSet.is
+ assert(t.typ != nil)
+ // x == under(x) for ~x terms
u := t.typ
- if u == nil {
- u = theTop
- }
- // t == under(t) for ~t terms
if !t.tilde {
u = under(u)
}
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index 61c8a9158c..f18a32016f 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -43,8 +43,14 @@ func RelativeTo(pkg *Package) Qualifier {
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func TypeString(typ Type, qf Qualifier) string {
+ return typeString(typ, qf, false)
+}
+
+func typeString(typ Type, qf Qualifier, debug bool) string {
var buf bytes.Buffer
- WriteType(&buf, typ, qf)
+ w := newTypeWriter(&buf, qf)
+ w.debug = debug
+ w.typ(typ)
return buf.String()
}
@@ -64,19 +70,20 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
}
type typeWriter struct {
- buf *bytes.Buffer
- seen map[Type]bool
- qf Qualifier
- ctxt *Context // if non-nil, we are type hashing
+ buf *bytes.Buffer
+ seen map[Type]bool
+ qf Qualifier
+ ctxt *Context // if non-nil, we are type hashing
+ debug bool // if true, write debug annotations
}
func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
- return &typeWriter{buf, make(map[Type]bool), qf, nil}
+ return &typeWriter{buf, make(map[Type]bool), qf, nil, false}
}
func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
assert(ctxt != nil)
- return &typeWriter{buf, make(map[Type]bool), nil, ctxt}
+ return &typeWriter{buf, make(map[Type]bool), nil, ctxt, false}
}
func (w *typeWriter) byte(b byte) {
@@ -266,14 +273,10 @@ func (w *typeWriter) typ(typ Type) {
w.error("unnamed type parameter")
break
}
- // Optionally write out package for typeparams (like Named).
- if t.obj.pkg != nil {
- writePackage(w.buf, t.obj.pkg, w.qf)
+ w.string(t.obj.name)
+ if w.debug || w.ctxt != nil {
+ w.string(subscript(t.id))
}
- w.string(t.obj.name + subscript(t.id))
-
- case *top:
- w.error("⊤")
default:
// For externally defined implementations of Type.
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index 746fe78b38..95893fd1e1 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -144,7 +144,7 @@ func (check *Checker) typ(e syntax.Expr) Type {
func (check *Checker) varType(e syntax.Expr) Type {
typ := check.definedType(e, nil)
- // We don't want to call under() (via asInterface) or complete interfaces while we
+ // We don't want to call under() (via toInterface) or complete interfaces while we
// are in the middle of type-checking parameter declarations that might belong to
// interface methods. Delay this check to the end of type-checking.
check.later(func() {
@@ -214,8 +214,6 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
if T != nil {
// Calling under() here may lead to endless instantiations.
// Test case: type T[P any] *T[P]
- // TODO(gri) investigate if that's a bug or to be expected
- // (see also analogous comment in Checker.instantiate).
under = safeUnderlying(T)
}
if T == under {
@@ -314,6 +312,13 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
typ := new(Pointer)
def.setUnderlying(typ)
typ.base = check.varType(e.X)
+ // If typ.base is invalid, it's unlikely that *base is particularly
+ // useful - even a valid dereferenciation will lead to an invalid
+ // type again, and in some cases we get unexpected follow-on errors
+ // (e.g., see #49005). Return an invalid type instead.
+ if typ.base == Typ[Invalid] {
+ return Typ[Invalid]
+ }
return typ
}
@@ -391,13 +396,24 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
return typ
}
-func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def *Named) Type {
+func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def *Named) (res Type) {
+ if check.conf.Trace {
+ check.trace(x.Pos(), "-- instantiating %s with %s", x, targsx)
+ check.indent++
+ defer func() {
+ check.indent--
+ // Don't format the underlying here. It will always be nil.
+ check.trace(x.Pos(), "=> %s", res)
+ }()
+ }
+
gtyp := check.genericType(x, true)
if gtyp == Typ[Invalid] {
return gtyp // error already reported
}
- base, _ := gtyp.(*Named)
- if base == nil {
+
+ origin, _ := gtyp.(*Named)
+ if origin == nil {
panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp))
}
@@ -411,20 +427,69 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def
// determine argument positions
posList := make([]syntax.Pos, len(targs))
for i, arg := range targsx {
- posList[i] = syntax.StartPos(arg)
+ posList[i] = arg.Pos()
}
- typ := check.instantiate(x.Pos(), base, targs, posList)
- def.setUnderlying(typ)
- check.recordInstance(x, targs, typ)
+ // create the instance
+ h := check.conf.Context.TypeHash(origin, targs)
+ // targs may be incomplete, and require inference. In any case we should de-duplicate.
+ inst := check.conf.Context.typeForHash(h, nil)
+ // If inst is non-nil, we can't just return here. Inst may have been
+ // constructed via recursive substitution, in which case we wouldn't do the
+ // validation below. Ensure that the validation (and resulting errors) runs
+ // for each instantiated type in the source.
+ if inst == nil {
+ tname := NewTypeName(x.Pos(), origin.obj.pkg, origin.obj.name, nil)
+ inst = check.newNamed(tname, origin, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
+ inst.targs = NewTypeList(targs)
+ inst = check.conf.Context.typeForHash(h, inst)
+ }
+ def.setUnderlying(inst)
- // make sure we check instantiation works at least once
- // and that the resulting type is valid
+ inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
+ tparams := origin.TypeParams().list()
+
+ inferred := targs
+ if len(targs) < len(tparams) {
+ // If inference fails, len(inferred) will be 0, and inst.underlying will
+ // be set to Typ[Invalid] in expandNamed.
+ inferred = check.infer(x.Pos(), tparams, targs, nil, nil)
+ if len(inferred) > len(targs) {
+ inst.targs = NewTypeList(inferred)
+ }
+ }
+
+ check.recordInstance(x, inferred, inst)
+ return expandNamed(ctxt, n, x.Pos())
+ }
+
+ // origin.tparams may not be set up, so we need to do expansion later.
check.later(func() {
- check.validType(typ, nil)
+ // This is an instance from the source, not from recursive substitution,
+ // and so it must be resolved during type-checking so that we can report
+ // errors.
+ inst.resolve(check.conf.Context)
+ // Since check is non-nil, we can still mutate inst. Unpinning the resolver
+ // frees some memory.
+ inst.resolver = nil
+
+ if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) {
+ if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil {
+ // best position for error reporting
+ pos := x.Pos()
+ if i < len(posList) {
+ pos = posList[i]
+ }
+ check.softErrorf(pos, err.Error())
+ } else {
+ check.mono.recordInstance(check.pkg, x.Pos(), inst.tparams.list(), inst.targs.list(), posList)
+ }
+ }
+
+ check.validType(inst, nil)
})
- return typ
+ return inst
}
// arrayLength type-checks the array length expression e
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index a252c5e1a5..7f636c30d3 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -235,13 +235,13 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
// If exact unification is known to fail because we attempt to
// match a type name against an unnamed type literal, consider
// the underlying type of the named type.
- // (Subtle: We use isNamed to include any type with a name (incl.
+ // (Subtle: We use hasName to include any type with a name (incl.
// basic types and type parameters. We use asNamed because we only
// want *Named types.)
switch {
- case !isNamed(x) && y != nil && asNamed(y) != nil:
+ case !hasName(x) && y != nil && asNamed(y) != nil:
return u.nify(x, under(y), p)
- case x != nil && asNamed(x) != nil && !isNamed(y):
+ case x != nil && asNamed(x) != nil && !hasName(y):
return u.nify(under(x), y, p)
}
}
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
index 87985dd133..5379bde02c 100644
--- a/src/cmd/compile/internal/types2/union.go
+++ b/src/cmd/compile/internal/types2/union.go
@@ -75,28 +75,16 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
continue
}
- x := tlist[i]
- pos := syntax.StartPos(x)
- // We may not know the position of x if it was a typechecker-
- // introduced ~T term for a type list entry T. Use the position
- // of T instead.
- // TODO(gri) remove this test once we don't support type lists anymore
- if !pos.IsKnown() {
- if op, _ := x.(*syntax.Operation); op != nil {
- pos = syntax.StartPos(op.X)
- }
- }
-
u := under(t.typ)
f, _ := u.(*Interface)
if t.tilde {
if f != nil {
- check.errorf(x, "invalid use of ~ (%s is an interface)", t.typ)
+ check.errorf(tlist[i], "invalid use of ~ (%s is an interface)", t.typ)
continue // don't report another error for t
}
if !Identical(u, t.typ) {
- check.errorf(x, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
+ check.errorf(tlist[i], "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
continue // don't report another error for t
}
}
@@ -105,14 +93,14 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
// in the beginning. Embedded interfaces with tilde are excluded above. If we reach
// here, we must have at least two terms in the union.
if f != nil && !f.typeSet().IsTypeSet() {
- check.errorf(pos, "cannot use %s in union (interface contains methods)", t)
+ check.errorf(tlist[i], "cannot use %s in union (interface contains methods)", t)
continue // don't report another error for t
}
// Report overlapping (non-disjoint) terms such as
// a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a).
if j := overlappingTerm(terms[:i], t); j >= 0 {
- check.softErrorf(pos, "overlapping terms %s and %s", t, terms[j])
+ check.softErrorf(tlist[i], "overlapping terms %s and %s", t, terms[j])
}
}
})
diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go
index 40535afa7a..4d1c5621fe 100644
--- a/src/cmd/compile/internal/walk/closure.go
+++ b/src/cmd/compile/internal/walk/closure.go
@@ -107,7 +107,16 @@ func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
// The closure is not trivial or directly called, so it's going to stay a closure.
ir.ClosureDebugRuntimeCheck(clo)
clofn.SetNeedctxt(true)
- ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
+
+ // The closure expression may be walked more than once if it appeared in composite
+ // literal initialization (e.g, see issue #49029).
+ //
+ // Don't add the closure function to compilation queue more than once, since when
+ // compiling a function twice would lead to an ICE.
+ if !clofn.Walked() {
+ clofn.SetWalked(true)
+ ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
+ }
typ := typecheck.ClosureType(clo)
diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go
index ad54fa7b96..b985b4caeb 100644
--- a/src/cmd/compile/internal/walk/complit.go
+++ b/src/cmd/compile/internal/walk/complit.go
@@ -482,7 +482,7 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) {
loop := ir.NewForStmt(base.Pos, nil, cond, incr, nil)
loop.Body = []ir.Node{body}
- *loop.PtrInit() = []ir.Node{zero}
+ loop.SetInit([]ir.Node{zero})
appendWalkStmt(init, loop)
return
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index c452cecbed..e5bf6cf0b5 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -506,17 +506,7 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
usemethod(n)
}
if n.Op() == ir.OCALLINTER {
- if n.X.(*ir.SelectorExpr).X.Type().HasShape() {
- // There should be an entry in n.KeepAlive to keep the
- // dictionary alive (added in ../noder/transformCall).
- // The dictionary in turn marks the method as used.
- if len(n.KeepAlive) == 0 {
- // TODO(khr): this fails for issue44688.go.
- //base.Fatalf("KeepAlive of dictionary arg missing")
- }
- } else {
- reflectdata.MarkUsedIfaceMethod(n)
- }
+ reflectdata.MarkUsedIfaceMethod(n)
}
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE {
diff --git a/src/cmd/compile/internal/walk/select.go b/src/cmd/compile/internal/walk/select.go
index d2b67ddf55..fde8f50895 100644
--- a/src/cmd/compile/internal/walk/select.go
+++ b/src/cmd/compile/internal/walk/select.go
@@ -105,7 +105,7 @@ func walkSelectCases(cases []*ir.CommClause) []ir.Node {
n := cas.Comm
ir.SetPos(n)
r := ir.NewIfStmt(base.Pos, nil, nil, nil)
- *r.PtrInit() = cas.Init()
+ r.SetInit(cas.Init())
var cond ir.Node
switch n.Op() {
default:
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index 7ee000861b..9c8529f7eb 100644
--- a/src/cmd/cover/cover.go
+++ b/src/cmd/cover/cover.go
@@ -40,8 +40,8 @@ Finally, to generate modified source code with coverage annotations
`
func usage() {
- fmt.Fprintln(os.Stderr, usageMessage)
- fmt.Fprintln(os.Stderr, "Flags:")
+ fmt.Fprint(os.Stderr, usageMessage)
+ fmt.Fprintln(os.Stderr, "\nFlags:")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.")
os.Exit(2)
diff --git a/src/cmd/cover/testdata/test.go b/src/cmd/cover/testdata/test.go
index b794962205..703fba57a4 100644
--- a/src/cmd/cover/testdata/test.go
+++ b/src/cmd/cover/testdata/test.go
@@ -13,6 +13,7 @@ package main
import _ "unsafe" // for go:linkname
//go:linkname some_name some_name
+var some_name int
const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often"
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 39f016e315..d37c3f83ef 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -812,6 +812,9 @@ func runInstall(pkg string, ch chan struct{}) {
importMap := make(map[string]string)
for _, p := range gofiles {
for _, imp := range readimports(p) {
+ if imp == "C" {
+ fatalf("%s imports C", p)
+ }
importMap[imp] = resolveVendor(imp, dir)
}
}
@@ -822,6 +825,9 @@ func runInstall(pkg string, ch chan struct{}) {
sort.Strings(sortedImports)
for _, dep := range importMap {
+ if dep == "C" {
+ fatalf("%s imports C", pkg)
+ }
startInstall(dep)
}
for _, dep := range importMap {
@@ -970,28 +976,8 @@ func packagefile(pkg string) string {
return pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, pkg)
}
-// matchfield reports whether the field (x,y,z) matches this build.
-// all the elements in the field must be satisfied.
-func matchfield(f string) bool {
- for _, tag := range strings.Split(f, ",") {
- if !matchtag(tag) {
- return false
- }
- }
- return true
-}
-
-// matchtag reports whether the tag (x or !x) matches this build.
+// matchtag reports whether the tag matches this build.
func matchtag(tag string) bool {
- if tag == "" {
- return false
- }
- if tag[0] == '!' {
- if len(tag) == 1 || tag[1] == '!' {
- return false
- }
- return !matchtag(tag[1:])
- }
return tag == "gc" || tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" ||
(goos == "android" && tag == "linux") ||
(goos == "illumos" && tag == "solaris") ||
@@ -1032,7 +1018,7 @@ func shouldbuild(file, pkg string) bool {
return false
}
- // Check file contents for // +build lines.
+ // Check file contents for //go:build lines.
for _, p := range strings.Split(readfile(file), "\n") {
p = strings.TrimSpace(p)
if p == "" {
@@ -1052,20 +1038,13 @@ func shouldbuild(file, pkg string) bool {
if !strings.HasPrefix(p, "//") {
break
}
- if !strings.Contains(p, "+build") {
- continue
- }
- fields := strings.Fields(p[2:])
- if len(fields) < 1 || fields[0] != "+build" {
- continue
- }
- for _, p := range fields[1:] {
- if matchfield(p) {
- goto fieldmatch
+ if strings.HasPrefix(p, "//go:build ") {
+ matched, err := matchexpr(p[len("//go:build "):])
+ if err != nil {
+ errprintf("%s: %v", file, err)
}
+ return matched
}
- return false
- fieldmatch:
}
return true
diff --git a/src/cmd/dist/buildtag.go b/src/cmd/dist/buildtag.go
new file mode 100644
index 0000000000..24776a0aaf
--- /dev/null
+++ b/src/cmd/dist/buildtag.go
@@ -0,0 +1,133 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+// exprParser is a //go:build expression parser and evaluator.
+// The parser is a trivial precedence-based parser which is still
+// almost overkill for these very simple expressions.
+type exprParser struct {
+ x string
+ t exprToken // upcoming token
+}
+
+// val is the value type result of parsing.
+// We don't keep a parse tree, just the value of the expression.
+type val bool
+
+// exprToken describes a single token in the input.
+// Prefix operators define a prefix func that parses the
+// upcoming value. Binary operators define an infix func
+// that combines two values according to the operator.
+// In that case, the parsing loop parses the two values.
+type exprToken struct {
+ tok string
+ prec int
+ prefix func(*exprParser) val
+ infix func(val, val) val
+}
+
+var exprTokens []exprToken
+
+func init() { // init to break init cycle
+ exprTokens = []exprToken{
+ {tok: "&&", prec: 1, infix: func(x, y val) val { return x && y }},
+ {tok: "||", prec: 2, infix: func(x, y val) val { return x || y }},
+ {tok: "!", prec: 3, prefix: (*exprParser).not},
+ {tok: "(", prec: 3, prefix: (*exprParser).paren},
+ {tok: ")"},
+ }
+}
+
+// matchexpr parses and evaluates the //go:build expression x.
+func matchexpr(x string) (matched bool, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ matched = false
+ err = fmt.Errorf("parsing //go:build line: %v", e)
+ }
+ }()
+
+ p := &exprParser{x: x}
+ p.next()
+ v := p.parse(0)
+ if p.t.tok != "end of expression" {
+ panic("unexpected " + p.t.tok)
+ }
+ return bool(v), nil
+}
+
+// parse parses an expression, including binary operators at precedence >= prec.
+func (p *exprParser) parse(prec int) val {
+ if p.t.prefix == nil {
+ panic("unexpected " + p.t.tok)
+ }
+ v := p.t.prefix(p)
+ for p.t.prec >= prec && p.t.infix != nil {
+ t := p.t
+ p.next()
+ v = t.infix(v, p.parse(t.prec+1))
+ }
+ return v
+}
+
+// not is the prefix parser for a ! token.
+func (p *exprParser) not() val {
+ p.next()
+ return !p.parse(100)
+}
+
+// paren is the prefix parser for a ( token.
+func (p *exprParser) paren() val {
+ p.next()
+ v := p.parse(0)
+ if p.t.tok != ")" {
+ panic("missing )")
+ }
+ p.next()
+ return v
+}
+
+// next advances the parser to the next token,
+// leaving the token in p.t.
+func (p *exprParser) next() {
+ p.x = strings.TrimSpace(p.x)
+ if p.x == "" {
+ p.t = exprToken{tok: "end of expression"}
+ return
+ }
+ for _, t := range exprTokens {
+ if strings.HasPrefix(p.x, t.tok) {
+ p.x = p.x[len(t.tok):]
+ p.t = t
+ return
+ }
+ }
+
+ i := 0
+ for i < len(p.x) && validtag(p.x[i]) {
+ i++
+ }
+ if i == 0 {
+ panic(fmt.Sprintf("syntax error near %#q", rune(p.x[i])))
+ }
+ tag := p.x[:i]
+ p.x = p.x[i:]
+ p.t = exprToken{
+ tok: "tag",
+ prefix: func(p *exprParser) val {
+ p.next()
+ return val(matchtag(tag))
+ },
+ }
+}
+
+func validtag(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '.' || c == '_'
+}
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 320c62f850..75f04a975c 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -46,8 +46,8 @@ var bootstrapDirs = []string{
"cmd/internal/obj/...",
"cmd/internal/objabi",
"cmd/internal/pkgpath",
+ "cmd/internal/quoted",
"cmd/internal/src",
- "cmd/internal/str",
"cmd/internal/sys",
"cmd/link",
"cmd/link/internal/...",
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 5935011e19..14b48351db 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1031,7 +1031,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
switch pair {
case "aix-ppc64",
"darwin-amd64", "darwin-arm64", "ios-arm64",
- "linux-amd64", "linux-386", "linux-ppc64le", "linux-s390x",
+ "linux-amd64", "linux-386", "linux-ppc64le", "linux-riscv64", "linux-s390x",
"freebsd-amd64",
"windows-amd64", "windows-386":
return true
@@ -1039,7 +1039,7 @@ func (t *tester) supportedBuildmode(mode string) bool {
return false
case "c-shared":
switch pair {
- case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x",
+ case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-riscv64", "linux-s390x",
"darwin-amd64", "darwin-arm64",
"freebsd-amd64",
"android-arm", "android-arm64", "android-386",
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 822c9e16f8..2257c5c0eb 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -323,7 +323,8 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
if n.Assign.IsValid() {
sep = " = "
}
- return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
+ tparams := pkg.formatTypeParams(n.TypeParams, depth)
+ return fmt.Sprintf("type %s%s%s%s", n.Name.Name, tparams, sep, pkg.oneLineNodeDepth(n.Type, depth))
case *ast.FuncType:
var params []string
@@ -342,15 +343,16 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
}
}
+ tparam := pkg.formatTypeParams(n.TypeParams, depth)
param := joinStrings(params)
if len(results) == 0 {
- return fmt.Sprintf("func(%s)", param)
+ return fmt.Sprintf("func%s(%s)", tparam, param)
}
result := joinStrings(results)
if !needParens {
- return fmt.Sprintf("func(%s) %s", param, result)
+ return fmt.Sprintf("func%s(%s) %s", tparam, param, result)
}
- return fmt.Sprintf("func(%s) (%s)", param, result)
+ return fmt.Sprintf("func%s(%s) (%s)", tparam, param, result)
case *ast.StructType:
if n.Fields == nil || len(n.Fields.List) == 0 {
@@ -419,6 +421,17 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
}
}
+func (pkg *Package) formatTypeParams(list *ast.FieldList, depth int) string {
+ if list.NumFields() == 0 {
+ return ""
+ }
+ var tparams []string
+ for _, field := range list.List {
+ tparams = append(tparams, pkg.oneLineField(field, depth))
+ }
+ return "[" + joinStrings(tparams) + "]"
+}
+
// oneLineField returns a one-line summary of the field.
func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
var names []string
diff --git a/src/cmd/fix/buildtag.go b/src/cmd/fix/buildtag.go
new file mode 100644
index 0000000000..5f4fbfef16
--- /dev/null
+++ b/src/cmd/fix/buildtag.go
@@ -0,0 +1,51 @@
+// Copyright 2020 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 (
+ "go/ast"
+ "strings"
+)
+
+func init() {
+ register(buildtagFix)
+}
+
+const buildtagGoVersionCutoff = 1_18
+
+var buildtagFix = fix{
+ name: "buildtag",
+ date: "2021-08-25",
+ f: buildtag,
+ desc: `Remove +build comments from modules using Go 1.18 or later`,
+}
+
+func buildtag(f *ast.File) bool {
+ if goVersion < buildtagGoVersionCutoff {
+ return false
+ }
+
+ // File is already gofmt-ed, so we know that if there are +build lines,
+ // they are in a comment group that starts with a //go:build line followed
+ // by a blank line. While we cannot delete comments from an AST and
+ // expect consistent output in general, this specific case - deleting only
+ // some lines from a comment block - does format correctly.
+ fixed := false
+ for _, g := range f.Comments {
+ sawGoBuild := false
+ for i, c := range g.List {
+ if strings.HasPrefix(c.Text, "//go:build ") {
+ sawGoBuild = true
+ }
+ if sawGoBuild && strings.HasPrefix(c.Text, "// +build ") {
+ g.List = g.List[:i]
+ fixed = true
+ break
+ }
+ }
+ }
+
+ return fixed
+}
diff --git a/src/cmd/fix/buildtag_test.go b/src/cmd/fix/buildtag_test.go
new file mode 100644
index 0000000000..1c6efbe9e0
--- /dev/null
+++ b/src/cmd/fix/buildtag_test.go
@@ -0,0 +1,34 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func init() {
+ addTestCases(buildtagTests, buildtag)
+}
+
+var buildtagTests = []testCase{
+ {
+ Name: "buildtag.oldGo",
+ Version: 1_10,
+ In: `//go:build yes
+// +build yes
+
+package main
+`,
+ },
+ {
+ Name: "buildtag.new",
+ Version: 1_99,
+ In: `//go:build yes
+// +build yes
+
+package main
+`,
+ Out: `//go:build yes
+
+package main
+`,
+ },
+}
diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go
index b47b06682a..3d292bdeba 100644
--- a/src/cmd/fix/cftype.go
+++ b/src/cmd/fix/cftype.go
@@ -63,7 +63,7 @@ func typefix(f *ast.File, badType func(string) bool) bool {
return
}
v := reflect.ValueOf(n)
- if v.Type().Kind() != reflect.Ptr {
+ if v.Type().Kind() != reflect.Pointer {
return
}
if v.IsNil() {
diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go
index b49db37571..b9980c17b9 100644
--- a/src/cmd/fix/fix.go
+++ b/src/cmd/fix/fix.go
@@ -125,6 +125,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
case *ast.IndexExpr:
walkBeforeAfter(&n.X, before, after)
walkBeforeAfter(&n.Index, before, after)
+ case *ast.IndexListExpr:
+ walkBeforeAfter(&n.X, before, after)
+ walkBeforeAfter(&n.Indices, before, after)
case *ast.SliceExpr:
walkBeforeAfter(&n.X, before, after)
if n.Low != nil {
@@ -156,6 +159,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
case *ast.StructType:
walkBeforeAfter(&n.Fields, before, after)
case *ast.FuncType:
+ if n.TypeParams != nil {
+ walkBeforeAfter(&n.TypeParams, before, after)
+ }
walkBeforeAfter(&n.Params, before, after)
if n.Results != nil {
walkBeforeAfter(&n.Results, before, after)
@@ -231,6 +237,9 @@ func walkBeforeAfter(x interface{}, before, after func(interface{})) {
walkBeforeAfter(&n.Values, before, after)
walkBeforeAfter(&n.Names, before, after)
case *ast.TypeSpec:
+ if n.TypeParams != nil {
+ walkBeforeAfter(&n.TypeParams, before, after)
+ }
walkBeforeAfter(&n.Type, before, after)
case *ast.BadDecl:
diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go
index d055929aac..b5f7b901d6 100644
--- a/src/cmd/fix/main.go
+++ b/src/cmd/fix/main.go
@@ -18,6 +18,7 @@ import (
"os"
"path/filepath"
"sort"
+ "strconv"
"strings"
"cmd/internal/diff"
@@ -36,7 +37,12 @@ var forceRewrites = flag.String("force", "",
var allowed, force map[string]bool
-var doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
+var (
+ doDiff = flag.Bool("diff", false, "display diffs instead of rewriting files")
+ goVersionStr = flag.String("go", "", "go language version for files")
+
+ goVersion int // 115 for go1.15
+)
// enable for debugging fix failures
const debug = false // display incorrectly reformatted source and exit
@@ -63,6 +69,26 @@ func main() {
flag.Usage = usage
flag.Parse()
+ if *goVersionStr != "" {
+ if !strings.HasPrefix(*goVersionStr, "go") {
+ report(fmt.Errorf("invalid -go=%s", *goVersionStr))
+ os.Exit(exitCode)
+ }
+ majorStr := (*goVersionStr)[len("go"):]
+ minorStr := "0"
+ if i := strings.Index(majorStr, "."); i >= 0 {
+ majorStr, minorStr = majorStr[:i], majorStr[i+len("."):]
+ }
+ major, err1 := strconv.Atoi(majorStr)
+ minor, err2 := strconv.Atoi(minorStr)
+ if err1 != nil || err2 != nil || major < 0 || major >= 100 || minor < 0 || minor >= 100 {
+ report(fmt.Errorf("invalid -go=%s", *goVersionStr))
+ os.Exit(exitCode)
+ }
+
+ goVersion = major*100 + minor
+ }
+
sort.Sort(byDate(fixes))
if *allowedRewrites != "" {
diff --git a/src/cmd/fix/main_test.go b/src/cmd/fix/main_test.go
index af16bcaa31..1baa95c545 100644
--- a/src/cmd/fix/main_test.go
+++ b/src/cmd/fix/main_test.go
@@ -14,10 +14,11 @@ import (
)
type testCase struct {
- Name string
- Fn func(*ast.File) bool
- In string
- Out string
+ Name string
+ Fn func(*ast.File) bool
+ Version int
+ In string
+ Out string
}
var testCases []testCase
@@ -78,7 +79,16 @@ func TestRewrite(t *testing.T) {
for _, tt := range testCases {
tt := tt
t.Run(tt.Name, func(t *testing.T) {
- t.Parallel()
+ if tt.Version == 0 {
+ t.Parallel()
+ } else {
+ old := goVersion
+ goVersion = tt.Version
+ defer func() {
+ goVersion = old
+ }()
+ }
+
// Apply fix: should get tt.Out.
out, fixed, ok := parseFixPrint(t, tt.Fn, tt.Name, tt.In, true)
if !ok {
@@ -91,6 +101,9 @@ func TestRewrite(t *testing.T) {
return
}
+ if tt.Out == "" {
+ tt.Out = tt.In
+ }
if out != tt.Out {
t.Errorf("incorrect output.\n")
if !strings.HasPrefix(tt.Name, "testdata/") {
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
index 8c4beb4b65..b7ec72e116 100644
--- a/src/cmd/fix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -383,7 +383,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
if n == nil {
return
}
- if false && reflect.TypeOf(n).Kind() == reflect.Ptr { // debugging trace
+ if false && reflect.TypeOf(n).Kind() == reflect.Pointer { // debugging trace
defer func() {
if t := typeof[n]; t != "" {
pos := fset.Position(n.(ast.Node).Pos())
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index b2ec091292..f7802a1675 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -3,17 +3,17 @@ module cmd
go 1.18
require (
- github.com/google/pprof v0.0.0-20211001005136-7fe48b4c820b
+ github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31
golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1
golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
- golang.org/x/tools v0.1.8-0.20211011152358-18fa84021695
+ golang.org/x/tools v0.1.8-0.20211025211149-f916b54a1784
)
require (
- github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
+ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
- golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e // indirect
+ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index ff8df7b749..25c25d81bd 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -1,10 +1,10 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/google/pprof v0.0.0-20211001005136-7fe48b4c820b h1:GX4+fGLMW5XTmDXB3R6UhTwZIYqgAOdA19+Ea0+3CU4=
-github.com/google/pprof v0.0.0-20211001005136-7fe48b4c820b/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31 h1:YvpxjnjGhf/vDEeYOysNbsrtB///PKS8lqkFNSDm1p8=
+github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
+github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d h1:uGg2frlt3IcT7kbV6LEp5ONv4vmoO2FW4qSO+my/aoM=
+github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1 h1:MwxAfiDvuwX8Nnnc6iRDhzyMyyc2tz5tYyCP/pZcPCg=
golang.org/x/arch v0.0.0-20210901143047-ebb09ed340f1/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
@@ -13,12 +13,11 @@ golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a h1:55PVa91KndtPGH2lus5l2gD
golang.org/x/mod v0.5.1-0.20210913215816-37dd6891021a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e h1:XMgFehsDnnLGtjvjOfqWSUzt0alpTR1RSEuznObga2c=
-golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/tools v0.1.8-0.20211011152358-18fa84021695 h1:H+g8CC6T05IOoW6fXaWvb5N3XBYQElWwgDS/7Yxg8dU=
-golang.org/x/tools v0.1.8-0.20211011152358-18fa84021695/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/tools v0.1.8-0.20211025211149-f916b54a1784 h1:+xP+QoP2SEPgbn+07I/yJTzP+gavj0XKGS6+JU5tlck=
+golang.org/x/tools v0.1.8-0.20211025211149-f916b54a1784/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index c3e4179025..c9426801c5 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -114,13 +114,16 @@
// The default is GOMAXPROCS, normally the number of CPUs available.
// -race
// enable data race detection.
-// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
+// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64,
// linux/ppc64le and linux/arm64 (only for 48-bit VMA).
// -msan
// enable interoperation with memory sanitizer.
// Supported only on linux/amd64, linux/arm64
// and only with Clang/LLVM as the host C compiler.
// On linux/arm64, pie build mode will be used.
+// -asan
+// enable interoperation with address sanitizer.
+// Supported only on linux/arm64, linux/amd64.
// -v
// print the names of packages as they are compiled.
// -work
@@ -131,8 +134,19 @@
//
// -asmflags '[pattern=]arg list'
// arguments to pass on each go tool asm invocation.
+// -buildinfo
+// Whether to stamp binaries with build flags. By default, the compiler name
+// (gc or gccgo), toolchain flags (like -gcflags), and environment variables
+// containing flags (like CGO_CFLAGS) are stamped into binaries. Use
+// -buildinfo=false to omit build information. See also -buildvcs.
// -buildmode mode
// build mode to use. See 'go help buildmode' for more.
+// -buildvcs
+// Whether to stamp binaries with version control information. By default,
+// version control information is stamped into a binary if the main package
+// and the main module containing it are in the repository containing the
+// current directory (if there is a repository). Use -buildvcs=false to
+// omit version control information. See also -buildinfo.
// -compiler name
// name of compiler to use, as in runtime.Compiler (gccgo or gc).
// -gccgoflags '[pattern=]arg list'
@@ -144,8 +158,8 @@
// in order to keep output separate from default builds.
// If using the -race flag, the install suffix is automatically set to race
// or, if set explicitly, has _race appended to it. Likewise for the -msan
-// flag. Using a -buildmode option that requires non-default compile flags
-// has a similar effect.
+// and -asan flags. Using a -buildmode option that requires non-default compile
+// flags has a similar effect.
// -ldflags '[pattern=]arg list'
// arguments to pass on each go tool link invocation.
// -linkshared
@@ -453,14 +467,18 @@
//
// Usage:
//
-// go fix [packages]
+// go fix [-fix list] [packages]
//
// Fix runs the Go fix command on the packages named by the import paths.
//
+// The -fix flag sets a comma-separated list of fixes to run.
+// The default is all known fixes.
+// (Its value is passed to 'go tool fix -r'.)
+//
// For more about fix, see 'go doc cmd/fix'.
// For more about specifying packages, see 'go help packages'.
//
-// To run fix with specific options, run 'go tool fix'.
+// To run fix with other options, run 'go tool fix'.
//
// See also: go fmt, go vet.
//
@@ -1056,8 +1074,11 @@
//
// Download downloads the named modules, which can be module patterns selecting
// dependencies of the main module or module queries of the form path@version.
-// With no arguments, download applies to all dependencies of the main module
-// (equivalent to 'go mod download all').
+//
+// With no arguments, download applies to the modules needed to build and test
+// the packages in the main module: the modules explicitly required by the main
+// module if it is at 'go 1.17' or higher, or all transitively-required modules
+// if at 'go 1.16' or lower.
//
// The go command will automatically download modules as needed during ordinary
// execution. The "go mod download" command is useful mainly for pre-filling
@@ -2776,11 +2797,12 @@
//
// -fuzz regexp
// Run the fuzz target matching the regular expression. When specified,
-// the command line argument must match exactly one package, and regexp
-// must match exactly one fuzz target within that package. After tests,
-// benchmarks, seed corpora of other fuzz targets, and examples have
-// completed, the matching target will be fuzzed. See the Fuzzing section
-// of the testing package documentation for details.
+// the command line argument must match exactly one package within the
+// main module, and regexp must match exactly one fuzz target within
+// that package. After tests, benchmarks, seed corpora of other fuzz
+// targets, and examples have completed, the matching target will be
+// fuzzed. See the Fuzzing section of the testing package documentation
+// for details.
//
// -fuzztime t
// Run enough iterations of the fuzz test to take t, specified as a
@@ -2832,11 +2854,11 @@
// exhaustive tests.
//
// -shuffle off,on,N
-// Randomize the execution order of tests and benchmarks.
-// It is off by default. If -shuffle is set to on, then it will seed
-// the randomizer using the system clock. If -shuffle is set to an
-// integer N, then N will be used as the seed value. In both cases,
-// the seed will be reported for reproducibility.
+// Randomize the execution order of tests and benchmarks.
+// It is off by default. If -shuffle is set to on, then it will seed
+// the randomizer using the system clock. If -shuffle is set to an
+// integer N, then N will be used as the seed value. In both cases,
+// the seed will be reported for reproducibility.
//
// -timeout d
// If a test binary runs longer than duration d, panic.
@@ -2932,7 +2954,11 @@
// When 'go test' runs a test binary, it does so from within the
// corresponding package's source code directory. Depending on the test,
// it may be necessary to do the same when invoking a generated test
-// binary directly.
+// binary directly. Because that directory may be located within the
+// module cache, which may be read-only and is verified by checksums, the
+// test must not write to it or any other directory within the module
+// unless explicitly requested by the user (such as with the -fuzz flag,
+// which writes failures to testdata/fuzz).
//
// The command-line package list, if present, must appear before any
// flag not known to the go test command. Continuing the example above,
diff --git a/src/cmd/go/go11.go b/src/cmd/go/go11.go
index a1f2727825..9faa7cba42 100644
--- a/src/cmd/go/go11.go
+++ b/src/cmd/go/go11.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build go1.1
-// +build go1.1
package main
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index 07e9962896..d8bed1dac0 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -13,6 +13,7 @@ import (
"flag"
"fmt"
"go/format"
+ "internal/godebug"
"internal/race"
"internal/testenv"
"io"
@@ -43,9 +44,12 @@ func init() {
}
var (
- canRace = false // whether we can run the race detector
- canCgo = false // whether we can use cgo
- canMSan = false // whether we can run the memory sanitizer
+ canRace = false // whether we can run the race detector
+ canCgo = false // whether we can use cgo
+ canMSan = false // whether we can run the memory sanitizer
+ canASan = false // whether we can run the address sanitizer
+ canFuzz = false // whether we can search for new fuzz failures
+ fuzzInstrumented = false // whether fuzzing uses instrumentation
)
var exeSuffix string = func() string {
@@ -197,6 +201,7 @@ func TestMain(m *testing.M) {
testGOCACHE = strings.TrimSpace(string(out))
canMSan = canCgo && sys.MSanSupported(runtime.GOOS, runtime.GOARCH)
+ canASan = canCgo && sys.ASanSupported(runtime.GOOS, runtime.GOARCH)
canRace = canCgo && sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH)
// The race detector doesn't work on Alpine Linux:
// golang.org/issue/14481
@@ -204,6 +209,8 @@ func TestMain(m *testing.M) {
if isAlpineLinux() || runtime.Compiler == "gccgo" {
canRace = false
}
+ canFuzz = sys.FuzzSupported(runtime.GOOS, runtime.GOARCH)
+ fuzzInstrumented = sys.FuzzInstrumented(runtime.GOOS, runtime.GOARCH)
}
// Don't let these environment variables confuse the test.
os.Setenv("GOENV", "off")
@@ -1380,7 +1387,7 @@ func TestLdFlagsLongArgumentsIssue42295(t *testing.T) {
for buf.Len() < sys.ExecArgLengthLimit+1 {
buf.WriteString(testStr)
}
- tg.run("run", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go"))
+ tg.run("run", "-buildinfo=false", "-ldflags", fmt.Sprintf(`-X "main.extern=%s"`, buf.String()), tg.path("main.go"))
if tg.stderr.String() != buf.String() {
t.Errorf("strings differ")
}
@@ -2275,7 +2282,7 @@ func TestUpxCompression(t *testing.T) {
func TestCacheListStale(t *testing.T) {
tooSlow(t)
- if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
+ if godebug.Get("gocacheverify") == "1" {
t.Skip("GODEBUG gocacheverify")
}
tg := testgo(t)
@@ -2298,7 +2305,7 @@ func TestCacheListStale(t *testing.T) {
func TestCacheCoverage(t *testing.T) {
tooSlow(t)
- if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
+ if godebug.Get("gocacheverify") == "1" {
t.Skip("GODEBUG gocacheverify")
}
@@ -2330,7 +2337,7 @@ func TestIssue22588(t *testing.T) {
func TestIssue22531(t *testing.T) {
tooSlow(t)
- if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
+ if godebug.Get("gocacheverify") == "1" {
t.Skip("GODEBUG gocacheverify")
}
tg := testgo(t)
@@ -2359,7 +2366,7 @@ func TestIssue22531(t *testing.T) {
func TestIssue22596(t *testing.T) {
tooSlow(t)
- if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
+ if godebug.Get("gocacheverify") == "1" {
t.Skip("GODEBUG gocacheverify")
}
tg := testgo(t)
@@ -2389,7 +2396,7 @@ func TestIssue22596(t *testing.T) {
func TestTestCache(t *testing.T) {
tooSlow(t)
- if strings.Contains(os.Getenv("GODEBUG"), "gocacheverify") {
+ if godebug.Get("gocacheverify") == "1" {
t.Skip("GODEBUG gocacheverify")
}
tg := testgo(t)
diff --git a/src/cmd/go/go_unix_test.go b/src/cmd/go/go_unix_test.go
index 7d5ff9bbb7..bab9494401 100644
--- a/src/cmd/go/go_unix_test.go
+++ b/src/cmd/go/go_unix_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main_test
diff --git a/src/cmd/go/internal/base/base.go b/src/cmd/go/internal/base/base.go
index 0144525e30..954ce47a98 100644
--- a/src/cmd/go/internal/base/base.go
+++ b/src/cmd/go/internal/base/base.go
@@ -17,7 +17,7 @@ import (
"sync"
"cmd/go/internal/cfg"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
// A Command is an implementation of a go command
diff --git a/src/cmd/go/internal/base/flag.go b/src/cmd/go/internal/base/flag.go
index 7e5121bffb..2c72c7e562 100644
--- a/src/cmd/go/internal/base/flag.go
+++ b/src/cmd/go/internal/base/flag.go
@@ -9,7 +9,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
- "cmd/internal/str"
+ "cmd/internal/quoted"
)
// A StringsFlag is a command-line flag that interprets its argument
@@ -18,7 +18,7 @@ type StringsFlag []string
func (v *StringsFlag) Set(s string) error {
var err error
- *v, err = str.SplitQuotedFields(s)
+ *v, err = quoted.Split(s)
if *v == nil {
*v = []string{}
}
diff --git a/src/cmd/go/internal/base/signal_notunix.go b/src/cmd/go/internal/base/signal_notunix.go
index 5cc0b0f101..682705f9b2 100644
--- a/src/cmd/go/internal/base/signal_notunix.go
+++ b/src/cmd/go/internal/base/signal_notunix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build plan9 || windows
-// +build plan9 windows
package base
diff --git a/src/cmd/go/internal/base/signal_unix.go b/src/cmd/go/internal/base/signal_unix.go
index cdc2658372..657ff38584 100644
--- a/src/cmd/go/internal/base/signal_unix.go
+++ b/src/cmd/go/internal/base/signal_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || js || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd js linux netbsd openbsd solaris
package base
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index c8747d6c11..351c3ee6a5 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -25,7 +25,9 @@ import (
// These are general "build flags" used by build and other commands.
var (
BuildA bool // -a flag
+ BuildBuildinfo bool // -buildinfo flag
BuildBuildmode string // -buildmode flag
+ BuildBuildvcs bool // -buildvcs flag
BuildContext = defaultContext()
BuildMod string // -mod flag
BuildModExplicit bool // whether -mod was set explicitly
@@ -33,6 +35,7 @@ var (
BuildI bool // -i flag
BuildLinkshared bool // -linkshared flag
BuildMSan bool // -msan flag
+ BuildASan bool // -asan flag
BuildN bool // -n flag
BuildO string // -o flag
BuildP = runtime.GOMAXPROCS(0) // -p flag
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index 1eb773407e..e56dd8223f 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -26,7 +26,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/internal/quoted"
)
var CmdEnv = &base.Command{
@@ -152,8 +152,11 @@ func ExtraEnvVars() []cfg.EnvVar {
} else if modload.Enabled() {
gomod = os.DevNull
}
+ modload.InitWorkfile()
+ gowork := modload.WorkFilePath()
return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod},
+ {Name: "GOWORK", Value: gowork},
}
}
@@ -431,7 +434,7 @@ func getOrigEnv(key string) string {
func checkEnvWrite(key, val string) error {
switch key {
- case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOTOOLDIR", "GOVERSION":
+ case "GOEXE", "GOGCCFLAGS", "GOHOSTARCH", "GOHOSTOS", "GOMOD", "GOWORK", "GOTOOLDIR", "GOVERSION":
return fmt.Errorf("%s cannot be modified", key)
case "GOENV":
return fmt.Errorf("%s can only be set using the OS environment", key)
@@ -467,7 +470,7 @@ func checkEnvWrite(key, val string) error {
if val == "" {
break
}
- args, err := str.SplitQuotedFields(val)
+ args, err := quoted.Split(val)
if err != nil {
return fmt.Errorf("invalid %s: %v", key, err)
}
diff --git a/src/cmd/go/internal/fix/fix.go b/src/cmd/go/internal/fix/fix.go
index cc5940fccd..d8ba353de6 100644
--- a/src/cmd/go/internal/fix/fix.go
+++ b/src/cmd/go/internal/fix/fix.go
@@ -10,28 +10,40 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
- "cmd/internal/str"
+ "cmd/go/internal/str"
+ "cmd/go/internal/work"
"context"
"fmt"
+ "go/build"
"os"
)
var CmdFix = &base.Command{
- Run: runFix,
- UsageLine: "go fix [packages]",
+ UsageLine: "go fix [-fix list] [packages]",
Short: "update packages to use new APIs",
Long: `
Fix runs the Go fix command on the packages named by the import paths.
+The -fix flag sets a comma-separated list of fixes to run.
+The default is all known fixes.
+(Its value is passed to 'go tool fix -r'.)
+
For more about fix, see 'go doc cmd/fix'.
For more about specifying packages, see 'go help packages'.
-To run fix with specific options, run 'go tool fix'.
+To run fix with other options, run 'go tool fix'.
See also: go fmt, go vet.
`,
}
+var fixes = CmdFix.Flag.String("fix", "", "comma-separated list of fixes to apply")
+
+func init() {
+ work.AddBuildFlags(CmdFix, work.DefaultBuildFlags)
+ CmdFix.Run = runFix // fix cycle
+}
+
func runFix(ctx context.Context, cmd *base.Command, args []string) {
pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{}, args)
w := 0
@@ -58,6 +70,16 @@ func runFix(ctx context.Context, cmd *base.Command, args []string) {
// the command only applies to this package,
// not to packages in subdirectories.
files := base.RelPaths(pkg.InternalAllGoFiles())
- base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), files))
+ goVersion := ""
+ if pkg.Module != nil {
+ goVersion = "go" + pkg.Module.GoVersion
+ } else if pkg.Standard {
+ goVersion = build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1]
+ }
+ var fixArg []string
+ if *fixes != "" {
+ fixArg = []string{"-r=" + *fixes}
+ }
+ base.Run(str.StringList(cfg.BuildToolexec, base.Tool("fix"), "-go="+goVersion, fixArg, files))
}
}
diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go
index 5981e5ecdb..a3873d1138 100644
--- a/src/cmd/go/internal/generate/generate.go
+++ b/src/cmd/go/internal/generate/generate.go
@@ -26,7 +26,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
var CmdGenerate = &base.Command{
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index b79d3ba86f..f46313dcff 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -20,7 +20,7 @@ import (
"cmd/go/internal/vcs"
"cmd/go/internal/web"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
)
@@ -417,10 +417,10 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// to make the first copy of or update a copy of the given package.
func downloadPackage(p *load.Package) error {
var (
- vcsCmd *vcs.Cmd
- repo, rootPath string
- err error
- blindRepo bool // set if the repo has unusual configuration
+ vcsCmd *vcs.Cmd
+ repo, rootPath, repoDir string
+ err error
+ blindRepo bool // set if the repo has unusual configuration
)
// p can be either a real package, or a pseudo-package whose “import path” is
@@ -446,10 +446,19 @@ func downloadPackage(p *load.Package) error {
if p.Internal.Build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src.
- vcsCmd, rootPath, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot)
+ const allowNesting = false
+ repoDir, vcsCmd, err = vcs.FromDir(p.Dir, p.Internal.Build.SrcRoot, allowNesting)
if err != nil {
return err
}
+ if !str.HasFilePathPrefix(repoDir, p.Internal.Build.SrcRoot) {
+ panic(fmt.Sprintf("repository %q not in source root %q", repo, p.Internal.Build.SrcRoot))
+ }
+ rootPath = str.TrimFilePathPrefix(repoDir, p.Internal.Build.SrcRoot)
+ if err := vcs.CheckGOVCS(vcsCmd, rootPath); err != nil {
+ return err
+ }
+
repo = "" // should be unused; make distinctive
// Double-check where it came from.
diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go
index 50aeabc578..ff6bea6777 100644
--- a/src/cmd/go/internal/imports/build.go
+++ b/src/cmd/go/internal/imports/build.go
@@ -2,17 +2,51 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Copied from Go distribution src/go/build/build.go, syslist.go
+// Copied from Go distribution src/go/build/build.go, syslist.go.
+// That package does not export the ability to process raw file data,
+// although we could fake it with an appropriate build.Context
+// and a lot of unwrapping.
+// More importantly, that package does not implement the tags["*"]
+// special case, in which both tag and !tag are considered to be true
+// for essentially all tags (except "ignore").
+//
+// If we added this API to go/build directly, we wouldn't need this
+// file anymore, but this API is not terribly general-purpose and we
+// don't really want to commit to any public form of it, nor do we
+// want to move the core parts of go/build into a top-level internal package.
+// These details change very infrequently, so the copy is fine.
package imports
import (
"bytes"
+ "errors"
+ "fmt"
+ "go/build/constraint"
"strings"
"unicode"
)
-var slashslash = []byte("//")
+var (
+ bSlashSlash = []byte("//")
+ bStarSlash = []byte("*/")
+ bSlashStar = []byte("/*")
+ bPlusBuild = []byte("+build")
+
+ goBuildComment = []byte("//go:build")
+
+ errGoBuildWithoutBuild = errors.New("//go:build comment without // +build comment")
+ errMultipleGoBuild = errors.New("multiple //go:build comments")
+)
+
+func isGoBuildComment(line []byte) bool {
+ if !bytes.HasPrefix(line, goBuildComment) {
+ return false
+ }
+ line = bytes.TrimSpace(line)
+ rest := line[len(goBuildComment):]
+ return len(rest) == 0 || len(bytes.TrimSpace(rest)) < len(rest)
+}
// ShouldBuild reports whether it is okay to use this file,
// The rule is that in the file's leading run of // comments
@@ -34,90 +68,124 @@ var slashslash = []byte("//")
// in any build.
//
func ShouldBuild(content []byte, tags map[string]bool) bool {
- // Pass 1. Identify leading run of // comments and blank lines,
+ // Identify leading run of // comments and blank lines,
// which must be followed by a blank line.
- end := 0
- p := content
- for len(p) > 0 {
- line := p
- if i := bytes.IndexByte(line, '\n'); i >= 0 {
- line, p = line[:i], p[i+1:]
- } else {
- p = p[len(p):]
- }
- line = bytes.TrimSpace(line)
- if len(line) == 0 { // Blank line
- end = len(content) - len(p)
- continue
- }
- if !bytes.HasPrefix(line, slashslash) { // Not comment line
- break
- }
+ // Also identify any //go:build comments.
+ content, goBuild, _, err := parseFileHeader(content)
+ if err != nil {
+ return false
}
- content = content[:end]
- // Pass 2. Process each line in the run.
- p = content
- allok := true
- for len(p) > 0 {
- line := p
- if i := bytes.IndexByte(line, '\n'); i >= 0 {
- line, p = line[:i], p[i+1:]
- } else {
- p = p[len(p):]
+ // If //go:build line is present, it controls.
+ // Otherwise fall back to +build processing.
+ var shouldBuild bool
+ switch {
+ case goBuild != nil:
+ x, err := constraint.Parse(string(goBuild))
+ if err != nil {
+ return false
}
- line = bytes.TrimSpace(line)
- if !bytes.HasPrefix(line, slashslash) {
- continue
- }
- line = bytes.TrimSpace(line[len(slashslash):])
- if len(line) > 0 && line[0] == '+' {
- // Looks like a comment +line.
- f := strings.Fields(string(line))
- if f[0] == "+build" {
- ok := false
- for _, tok := range f[1:] {
- if matchTags(tok, tags) {
- ok = true
- }
- }
- if !ok {
- allok = false
+ shouldBuild = eval(x, tags, true)
+
+ default:
+ shouldBuild = true
+ p := content
+ for len(p) > 0 {
+ line := p
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, p = line[:i], p[i+1:]
+ } else {
+ p = p[len(p):]
+ }
+ line = bytes.TrimSpace(line)
+ if !bytes.HasPrefix(line, bSlashSlash) || !bytes.Contains(line, bPlusBuild) {
+ continue
+ }
+ text := string(line)
+ if !constraint.IsPlusBuild(text) {
+ continue
+ }
+ if x, err := constraint.Parse(text); err == nil {
+ if !eval(x, tags, true) {
+ shouldBuild = false
}
}
}
}
- return allok
+ return shouldBuild
}
-// matchTags reports whether the name is one of:
-//
-// tag (if tags[tag] is true)
-// !tag (if tags[tag] is false)
-// a comma-separated list of any of these
-//
-func matchTags(name string, tags map[string]bool) bool {
- if name == "" {
- return false
+func parseFileHeader(content []byte) (trimmed, goBuild []byte, sawBinaryOnly bool, err error) {
+ end := 0
+ p := content
+ ended := false // found non-blank, non-// line, so stopped accepting // +build lines
+ inSlashStar := false // in /* */ comment
+
+Lines:
+ for len(p) > 0 {
+ line := p
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, p = line[:i], p[i+1:]
+ } else {
+ p = p[len(p):]
+ }
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 && !ended { // Blank line
+ // Remember position of most recent blank line.
+ // When we find the first non-blank, non-// line,
+ // this "end" position marks the latest file position
+ // where a // +build line can appear.
+ // (It must appear _before_ a blank line before the non-blank, non-// line.
+ // Yes, that's confusing, which is part of why we moved to //go:build lines.)
+ // Note that ended==false here means that inSlashStar==false,
+ // since seeing a /* would have set ended==true.
+ end = len(content) - len(p)
+ continue Lines
+ }
+ if !bytes.HasPrefix(line, bSlashSlash) { // Not comment line
+ ended = true
+ }
+
+ if !inSlashStar && isGoBuildComment(line) {
+ if goBuild != nil {
+ return nil, nil, false, errMultipleGoBuild
+ }
+ goBuild = line
+ }
+
+ Comments:
+ for len(line) > 0 {
+ if inSlashStar {
+ if i := bytes.Index(line, bStarSlash); i >= 0 {
+ inSlashStar = false
+ line = bytes.TrimSpace(line[i+len(bStarSlash):])
+ continue Comments
+ }
+ continue Lines
+ }
+ if bytes.HasPrefix(line, bSlashSlash) {
+ continue Lines
+ }
+ if bytes.HasPrefix(line, bSlashStar) {
+ inSlashStar = true
+ line = bytes.TrimSpace(line[len(bSlashStar):])
+ continue Comments
+ }
+ // Found non-comment text.
+ break Lines
+ }
}
- if i := strings.Index(name, ","); i >= 0 {
- // comma-separated list
- ok1 := matchTags(name[:i], tags)
- ok2 := matchTags(name[i+1:], tags)
- return ok1 && ok2
- }
- if strings.HasPrefix(name, "!!") { // bad syntax, reject always
- return false
- }
- if strings.HasPrefix(name, "!") { // negation
- return len(name) > 1 && matchTag(name[1:], tags, false)
- }
- return matchTag(name, tags, true)
+
+ return content[:end], goBuild, sawBinaryOnly, nil
}
-// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
-func matchTag(name string, tags map[string]bool, want bool) bool {
+// matchTag reports whether the tag name is valid and tags[name] is true.
+// As a special case, if tags["*"] is true and name is not empty or ignore,
+// then matchTag will return prefer instead of the actual answer,
+// which allows the caller to pretend in that case that most tags are
+// both true and false.
+func matchTag(name string, tags map[string]bool, prefer bool) bool {
// Tags must be letters, digits, underscores or dots.
// Unlike in Go identifiers, all digits are fine (e.g., "386").
for _, c := range name {
@@ -131,7 +199,7 @@ func matchTag(name string, tags map[string]bool, want bool) bool {
// if we put * in the tags map then all tags
// except "ignore" are considered both present and not
// (so we return true no matter how 'want' is set).
- return true
+ return prefer
}
have := tags[name]
@@ -144,7 +212,25 @@ func matchTag(name string, tags map[string]bool, want bool) bool {
if name == "darwin" {
have = have || tags["ios"]
}
- return have == want
+ return have
+}
+
+// eval is like
+// x.Eval(func(tag string) bool { return matchTag(tag, tags) })
+// except that it implements the special case for tags["*"] meaning
+// all tags are both true and false at the same time.
+func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
+ switch x := x.(type) {
+ case *constraint.TagExpr:
+ return matchTag(x.Tag, tags, prefer)
+ case *constraint.NotExpr:
+ return !eval(x.X, tags, !prefer)
+ case *constraint.AndExpr:
+ return eval(x.X, tags, prefer) && eval(x.Y, tags, prefer)
+ case *constraint.OrExpr:
+ return eval(x.X, tags, prefer) || eval(x.Y, tags, prefer)
+ }
+ panic(fmt.Sprintf("unexpected constraint expression %T", x))
}
// MatchFile returns false if the name contains a $GOOS or $GOARCH
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 821e622abb..8c85ddcf21 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -24,7 +24,7 @@ import (
"cmd/go/internal/modinfo"
"cmd/go/internal/modload"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
var CmdList = &base.Command{
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go
index 4e0cb5bc19..de079decdf 100644
--- a/src/cmd/go/internal/load/flag.go
+++ b/src/cmd/go/internal/load/flag.go
@@ -6,7 +6,7 @@ package load
import (
"cmd/go/internal/base"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"fmt"
"strings"
)
@@ -22,6 +22,7 @@ var (
// that allows specifying different effective flags for different packages.
// See 'go help build' for more details about per-package flags.
type PerPackageFlag struct {
+ raw string
present bool
values []ppfValue
}
@@ -39,6 +40,7 @@ func (f *PerPackageFlag) Set(v string) error {
// set is the implementation of Set, taking a cwd (current working directory) for easier testing.
func (f *PerPackageFlag) set(v, cwd string) error {
+ f.raw = v
f.present = true
match := func(p *Package) bool { return p.Internal.CmdlinePkg || p.Internal.CmdlineFiles } // default predicate with no pattern
// For backwards compatibility with earlier flag splitting, ignore spaces around flags.
@@ -61,7 +63,7 @@ func (f *PerPackageFlag) set(v, cwd string) error {
match = MatchPackage(pattern, cwd)
v = v[i+1:]
}
- flags, err := str.SplitQuotedFields(v)
+ flags, err := quoted.Split(v)
if err != nil {
return err
}
@@ -72,9 +74,7 @@ func (f *PerPackageFlag) set(v, cwd string) error {
return nil
}
-// String is required to implement flag.Value.
-// It is not used, because cmd/go never calls flag.PrintDefaults.
-func (f *PerPackageFlag) String() string { return "" }
+func (f *PerPackageFlag) String() string { return f.raw }
// Present reports whether the flag appeared on the command line.
func (f *PerPackageFlag) Present() bool {
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index a3a8de81c9..c6c5fb00a8 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -21,9 +21,11 @@ import (
pathpkg "path"
"path/filepath"
"runtime"
+ "runtime/debug"
"sort"
"strconv"
"strings"
+ "time"
"unicode"
"unicode/utf8"
@@ -36,8 +38,9 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/par"
"cmd/go/internal/search"
+ "cmd/go/internal/str"
"cmd/go/internal/trace"
- "cmd/internal/str"
+ "cmd/go/internal/vcs"
"cmd/internal/sys"
"golang.org/x/mod/modfile"
@@ -1623,6 +1626,7 @@ var cgoSyscallExclude = map[string]bool{
"runtime/cgo": true,
"runtime/race": true,
"runtime/msan": true,
+ "runtime/asan": true,
}
var foldPath = make(map[string]string)
@@ -1921,9 +1925,8 @@ func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *
}
p.Internal.Imports = imports
p.collectDeps()
-
- if cfg.ModulesEnabled && p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 {
- p.Internal.BuildInfo = modload.PackageBuildInfo(pkgPath, p.Deps)
+ if p.Error == nil && p.Name == "main" && len(p.DepsErrors) == 0 {
+ p.setBuildInfo()
}
// unsafe is a fake package.
@@ -2195,6 +2198,191 @@ func (p *Package) collectDeps() {
}
}
+// setBuildInfo gathers build information, formats it as a string to be
+// embedded in the binary, then sets p.Internal.BuildInfo to that string.
+// setBuildInfo should only be called on a main package with no errors.
+//
+// This information can be retrieved using debug.ReadBuildInfo.
+//
+// Note that the GoVersion field is not set here to avoid encoding it twice.
+// It is stored separately in the binary, mostly for historical reasons.
+func (p *Package) setBuildInfo() {
+ // TODO: build and vcs information is not embedded for executables in GOROOT.
+ // cmd/dist uses -gcflags=all= -ldflags=all= by default, which means these
+ // executables always appear stale unless the user sets the same flags.
+ // Perhaps it's safe to omit those flags when GO_GCFLAGS and GO_LDFLAGS
+ // are not set?
+ setPkgErrorf := func(format string, args ...interface{}) {
+ if p.Error == nil {
+ p.Error = &PackageError{Err: fmt.Errorf(format, args...)}
+ }
+ }
+
+ var debugModFromModinfo func(*modinfo.ModulePublic) *debug.Module
+ debugModFromModinfo = func(mi *modinfo.ModulePublic) *debug.Module {
+ dm := &debug.Module{
+ Path: mi.Path,
+ Version: mi.Version,
+ }
+ if mi.Replace != nil {
+ dm.Replace = debugModFromModinfo(mi.Replace)
+ } else {
+ dm.Sum = modfetch.Sum(module.Version{Path: mi.Path, Version: mi.Version})
+ }
+ return dm
+ }
+
+ var main debug.Module
+ if p.Module != nil {
+ main = *debugModFromModinfo(p.Module)
+ }
+
+ visited := make(map[*Package]bool)
+ mdeps := make(map[module.Version]*debug.Module)
+ var q []*Package
+ q = append(q, p.Internal.Imports...)
+ for len(q) > 0 {
+ p1 := q[0]
+ q = q[1:]
+ if visited[p1] {
+ continue
+ }
+ visited[p1] = true
+ if p1.Module != nil {
+ m := module.Version{Path: p1.Module.Path, Version: p1.Module.Version}
+ if p1.Module.Path != main.Path && mdeps[m] == nil {
+ mdeps[m] = debugModFromModinfo(p1.Module)
+ }
+ }
+ q = append(q, p1.Internal.Imports...)
+ }
+ sortedMods := make([]module.Version, 0, len(mdeps))
+ for mod := range mdeps {
+ sortedMods = append(sortedMods, mod)
+ }
+ module.Sort(sortedMods)
+ deps := make([]*debug.Module, len(sortedMods))
+ for i, mod := range sortedMods {
+ deps[i] = mdeps[mod]
+ }
+
+ pkgPath := p.ImportPath
+ if p.Internal.CmdlineFiles {
+ pkgPath = "command-line-arguments"
+ }
+ info := &debug.BuildInfo{
+ Path: pkgPath,
+ Main: main,
+ Deps: deps,
+ }
+ appendSetting := func(key, value string) {
+ info.Settings = append(info.Settings, debug.BuildSetting{Key: key, Value: value})
+ }
+
+ // Add command-line flags relevant to the build.
+ // This is informational, not an exhaustive list.
+ if cfg.BuildBuildinfo && !p.Standard {
+ appendSetting("compiler", cfg.BuildContext.Compiler)
+ if BuildAsmflags.present {
+ appendSetting("asmflags", BuildAsmflags.String())
+ }
+ if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" {
+ appendSetting("gcflags", BuildGcflags.String())
+ }
+ if BuildGccgoflags.present && cfg.BuildContext.Compiler == "gccgo" {
+ appendSetting("gccgoflags", BuildGccgoflags.String())
+ }
+ if BuildLdflags.present {
+ appendSetting("ldflags", BuildLdflags.String())
+ }
+ tags := append(cfg.BuildContext.BuildTags, cfg.BuildContext.ToolTags...)
+ appendSetting("tags", strings.Join(tags, ","))
+ appendSetting("CGO_ENABLED", strconv.FormatBool(cfg.BuildContext.CgoEnabled))
+ if cfg.BuildContext.CgoEnabled {
+ for _, name := range []string{"CGO_CPPFLAGS", "CGO_CFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} {
+ appendSetting(name, cfg.Getenv(name))
+ }
+ }
+ }
+
+ // Add VCS status if all conditions are true:
+ //
+ // - -buildvcs is enabled.
+ // - p is contained within a main module (there may be multiple main modules
+ // in a workspace, but local replacements don't count).
+ // - Both the current directory and p's module's root directory are contained
+ // in the same local repository.
+ // - We know the VCS commands needed to get the status.
+ setVCSError := func(err error) {
+ setPkgErrorf("error obtaining VCS status: %v\n\tUse -buildvcs=false to disable VCS stamping.", err)
+ }
+
+ var repoDir string
+ var vcsCmd *vcs.Cmd
+ var err error
+ const allowNesting = true
+ if cfg.BuildBuildvcs && p.Module != nil && p.Module.Version == "" && !p.Standard {
+ repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting)
+ if err != nil && !errors.Is(err, os.ErrNotExist) {
+ setVCSError(err)
+ return
+ }
+ if !str.HasFilePathPrefix(p.Module.Dir, repoDir) &&
+ !str.HasFilePathPrefix(repoDir, p.Module.Dir) {
+ // The module containing the main package does not overlap with the
+ // repository containing the working directory. Don't include VCS info.
+ // If the repo contains the module or vice versa, but they are not
+ // the same directory, it's likely an error (see below).
+ repoDir, vcsCmd = "", nil
+ }
+ }
+ if repoDir != "" && vcsCmd.Status != nil {
+ // Check that the current directory, package, and module are in the same
+ // repository. vcs.FromDir allows nested Git repositories, but nesting
+ // is not allowed for other VCS tools. The current directory may be outside
+ // p.Module.Dir when a workspace is used.
+ pkgRepoDir, _, err := vcs.FromDir(p.Dir, "", allowNesting)
+ if err != nil {
+ setVCSError(err)
+ return
+ }
+ if pkgRepoDir != repoDir {
+ setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir))
+ return
+ }
+ modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting)
+ if err != nil {
+ setVCSError(err)
+ return
+ }
+ if modRepoDir != repoDir {
+ setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir))
+ return
+ }
+
+ st, err := vcsCmd.Status(vcsCmd, repoDir)
+ if err != nil {
+ setVCSError(err)
+ return
+ }
+ if st.Revision != "" {
+ appendSetting(vcsCmd.Cmd+"revision", st.Revision)
+ }
+ if !st.CommitTime.IsZero() {
+ stamp := st.CommitTime.UTC().Format(time.RFC3339Nano)
+ appendSetting(vcsCmd.Cmd+"committime", stamp)
+ }
+ appendSetting(vcsCmd.Cmd+"uncommitted", strconv.FormatBool(st.Uncommitted))
+ }
+
+ text, err := info.MarshalText()
+ if err != nil {
+ setPkgErrorf("error formatting build info: %v", err)
+ return
+ }
+ p.Internal.BuildInfo = string(text)
+}
+
// SafeArg reports whether arg is a "safe" command-line argument,
// meaning that when it appears in a command-line, it probably
// doesn't have some special meaning other than its own name.
@@ -2233,6 +2421,10 @@ func LinkerDeps(p *Package) []string {
if cfg.BuildMSan {
deps = append(deps, "runtime/msan")
}
+ // Using address sanitizer forces an import of runtime/asan.
+ if cfg.BuildASan {
+ deps = append(deps, "runtime/asan")
+ }
return deps
}
diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go
index da6d1cb21d..8a18dfbe93 100644
--- a/src/cmd/go/internal/load/test.go
+++ b/src/cmd/go/internal/load/test.go
@@ -23,7 +23,7 @@ import (
"cmd/go/internal/fsys"
"cmd/go/internal/trace"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
var TestMainDeps = []string{
@@ -678,10 +678,16 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
}
func checkTestFunc(fn *ast.FuncDecl, arg string) error {
+ var why string
if !isTestFunc(fn, arg) {
- name := fn.Name.String()
+ why = fmt.Sprintf("must be: func %s(%s *testing.%s)", fn.Name.String(), strings.ToLower(arg), arg)
+ }
+ if fn.Type.TypeParams.NumFields() > 0 {
+ why = "test functions cannot have type parameters"
+ }
+ if why != "" {
pos := testFileSet.Position(fn.Pos())
- return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
+ return fmt.Errorf("%s: wrong signature for %s, %s", pos, fn.Name.String(), why)
}
return nil
}
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go
index a37b2ad6d1..09354d2306 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || (solaris && !illumos)
-// +build aix solaris,!illumos
// This code implements the filelock API using POSIX 'fcntl' locks, which attach
// to an (inode, process) pair rather than a file descriptor. To avoid unlocking
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go
index 70f5d7a688..491bec39af 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_other.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris && !windows
-// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris,!windows
package filelock
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go
index 908afb6c8c..54b2c946e0 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_plan9.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build plan9
-// +build plan9
package filelock
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go
index 640d4406f4..7bd7bd28f5 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !js && !plan9
-// +build !js,!plan9
package filelock_test
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go
index 878a1e770d..d7778d05de 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd
-// +build darwin dragonfly freebsd illumos linux netbsd openbsd
package filelock
diff --git a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go
index dd27ce92bd..e2ca538304 100644
--- a/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go
+++ b/src/cmd/go/internal/lockedfile/internal/filelock/filelock_windows.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build windows
-// +build windows
package filelock
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
index e4923f6876..1a677a7fe4 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_filelock.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !plan9
-// +build !plan9
package lockedfile
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
index 979118b10a..35669388e0 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build plan9
-// +build plan9
package lockedfile
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_test.go b/src/cmd/go/internal/lockedfile/lockedfile_test.go
index 3acc6695a7..c9907db46c 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_test.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_test.go
@@ -4,7 +4,6 @@
// js does not support inter-process file locking.
//go:build !js
-// +build !js
package lockedfile_test
diff --git a/src/cmd/go/internal/lockedfile/transform_test.go b/src/cmd/go/internal/lockedfile/transform_test.go
index b753346e7d..3c1caa334e 100644
--- a/src/cmd/go/internal/lockedfile/transform_test.go
+++ b/src/cmd/go/internal/lockedfile/transform_test.go
@@ -4,7 +4,6 @@
// js does not support inter-process file locking.
//go:build !js
-// +build !js
package lockedfile_test
diff --git a/src/cmd/go/internal/modcmd/download.go b/src/cmd/go/internal/modcmd/download.go
index 5ea56c34bd..f252133762 100644
--- a/src/cmd/go/internal/modcmd/download.go
+++ b/src/cmd/go/internal/modcmd/download.go
@@ -16,6 +16,7 @@ import (
"cmd/go/internal/modload"
"golang.org/x/mod/module"
+ "golang.org/x/mod/semver"
)
var cmdDownload = &base.Command{
@@ -24,8 +25,11 @@ var cmdDownload = &base.Command{
Long: `
Download downloads the named modules, which can be module patterns selecting
dependencies of the main module or module queries of the form path@version.
-With no arguments, download applies to all dependencies of the main module
-(equivalent to 'go mod download all').
+
+With no arguments, download applies to the modules needed to build and test
+the packages in the main module: the modules explicitly required by the main
+module if it is at 'go 1.17' or higher, or all transitively-required modules
+if at 'go 1.16' or lower.
The go command will automatically download modules as needed during ordinary
execution. The "go mod download" command is useful mainly for pre-filling
@@ -87,13 +91,8 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
// Check whether modules are enabled and whether we're in a module.
modload.ForceUseModules = true
modload.ExplicitWriteGoMod = true
- if !modload.HasModRoot() && len(args) == 0 {
- base.Fatalf("go: no modules specified (see 'go help mod download')")
- }
haveExplicitArgs := len(args) > 0
- if !haveExplicitArgs {
- args = []string{"all"}
- }
+
if modload.HasModRoot() {
modload.LoadModFile(ctx) // to fill MainModules
@@ -102,16 +101,50 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
}
mainModule := modload.MainModules.Versions()[0]
- targetAtUpgrade := mainModule.Path + "@upgrade"
- targetAtPatch := mainModule.Path + "@patch"
- for _, arg := range args {
- switch arg {
- case mainModule.Path, targetAtUpgrade, targetAtPatch:
- os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
+ if haveExplicitArgs {
+ targetAtUpgrade := mainModule.Path + "@upgrade"
+ targetAtPatch := mainModule.Path + "@patch"
+ for _, arg := range args {
+ switch arg {
+ case mainModule.Path, targetAtUpgrade, targetAtPatch:
+ os.Stderr.WriteString("go: skipping download of " + arg + " that resolves to the main module\n")
+ }
+ }
+ } else {
+ modFile := modload.MainModules.ModFile(mainModule)
+ if modFile.Go == nil || semver.Compare("v"+modFile.Go.Version, modload.ExplicitIndirectVersionV) < 0 {
+ if len(modFile.Require) > 0 {
+ args = []string{"all"}
+ }
+ } else {
+ // As of Go 1.17, the go.mod file explicitly requires every module
+ // that provides any package imported by the main module.
+ // 'go mod download' is typically run before testing packages in the
+ // main module, so by default we shouldn't download the others
+ // (which are presumed irrelevant to the packages in the main module).
+ // See https://golang.org/issue/44435.
+ //
+ // However, we also need to load the full module graph, to ensure that
+ // we have downloaded enough of the module graph to run 'go list all',
+ // 'go mod graph', and similar commands.
+ _ = modload.LoadModGraph(ctx, "")
+
+ for _, m := range modFile.Require {
+ args = append(args, m.Mod.Path)
+ }
}
}
}
+ if len(args) == 0 {
+ if modload.HasModRoot() {
+ os.Stderr.WriteString("go: no module dependencies to download\n")
+ } else {
+ base.Errorf("go: no modules specified (see 'go help mod download')")
+ }
+ base.Exit()
+ }
+
downloadModule := func(m *moduleJSON) {
var err error
m.Info, err = modfetch.InfoFile(m.Path, m.Version)
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index 92348b8897..484e095cc7 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -24,7 +24,7 @@ import (
"cmd/go/internal/imports"
"cmd/go/internal/load"
"cmd/go/internal/modload"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@@ -128,7 +128,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
for _, m := range vendorMods {
- replacement, _ := modload.Replacement(m)
+ replacement := modload.Replacement(m)
line := moduleLine(m, replacement)
io.WriteString(w, line)
diff --git a/src/cmd/go/internal/modfetch/bootstrap.go b/src/cmd/go/internal/modfetch/bootstrap.go
index ed694581a7..e23669fb00 100644
--- a/src/cmd/go/internal/modfetch/bootstrap.go
+++ b/src/cmd/go/internal/modfetch/bootstrap.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build cmd_go_bootstrap
-// +build cmd_go_bootstrap
package modfetch
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index efb4b1516a..378fbae34f 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -21,7 +21,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/lockedfile"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
// Downloaded size limits.
diff --git a/src/cmd/go/internal/modfetch/codehost/shell.go b/src/cmd/go/internal/modfetch/codehost/shell.go
index 0e9f381966..eaa01950b9 100644
--- a/src/cmd/go/internal/modfetch/codehost/shell.go
+++ b/src/cmd/go/internal/modfetch/codehost/shell.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
// Interactive debugging shell for codehost.Repo implementations.
diff --git a/src/cmd/go/internal/modfetch/codehost/vcs.go b/src/cmd/go/internal/modfetch/codehost/vcs.go
index 5d810d2621..c2cca084e3 100644
--- a/src/cmd/go/internal/modfetch/codehost/vcs.go
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -20,7 +20,7 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/par"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
// A VCSError indicates an error using a version control system.
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index dfef9f73c2..df835c3d7e 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -567,11 +567,11 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
if rev != info.Short {
switch {
case strings.HasPrefix(rev, info.Short):
- return fmt.Errorf("revision is longer than canonical (%s)", info.Short)
+ return fmt.Errorf("revision is longer than canonical (expected %s)", info.Short)
case strings.HasPrefix(info.Short, rev):
- return fmt.Errorf("revision is shorter than canonical (%s)", info.Short)
+ return fmt.Errorf("revision is shorter than canonical (expected %s)", info.Short)
default:
- return fmt.Errorf("does not match short name of revision (%s)", info.Short)
+ return fmt.Errorf("does not match short name of revision (expected %s)", info.Short)
}
}
diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go
index 79961b4324..492b03bd84 100644
--- a/src/cmd/go/internal/modfetch/sumdb.go
+++ b/src/cmd/go/internal/modfetch/sumdb.go
@@ -5,7 +5,6 @@
// Go checksum database lookup
//go:build !cmd_go_bootstrap
-// +build !cmd_go_bootstrap
package modfetch
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index c634512072..2c48c3c444 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -1575,7 +1575,7 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
i := i
m := r.buildList[i]
mActual := m
- if mRepl, _ := modload.Replacement(m); mRepl.Path != "" {
+ if mRepl := modload.Replacement(m); mRepl.Path != "" {
mActual = mRepl
}
old := module.Version{Path: m.Path, Version: r.initialVersion[m.Path]}
@@ -1583,7 +1583,7 @@ func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []strin
continue
}
oldActual := old
- if oldRepl, _ := modload.Replacement(old); oldRepl.Path != "" {
+ if oldRepl := modload.Replacement(old); oldRepl.Path != "" {
oldActual = oldRepl
}
if mActual == oldActual || mActual.Version == "" || !modfetch.HaveSum(oldActual) {
diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go
index d7341e7813..887cb51b31 100644
--- a/src/cmd/go/internal/modget/query.go
+++ b/src/cmd/go/internal/modget/query.go
@@ -14,7 +14,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/search"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
)
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index 8a9792089b..0e0292ec15 100644
--- a/src/cmd/go/internal/modload/build.go
+++ b/src/cmd/go/internal/modload/build.go
@@ -5,7 +5,6 @@
package modload
import (
- "bytes"
"context"
"encoding/hex"
"errors"
@@ -240,7 +239,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
}
// completeFromModCache fills in the extra fields in m using the module cache.
- completeFromModCache := func(m *modinfo.ModulePublic, replacedFrom string) {
+ completeFromModCache := func(m *modinfo.ModulePublic) {
checksumOk := func(suffix string) bool {
return rs == nil || m.Version == "" || cfg.BuildMod == "mod" ||
modfetch.HaveSum(module.Version{Path: m.Path, Version: m.Version + suffix})
@@ -259,7 +258,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
if m.GoVersion == "" && checksumOk("/go.mod") {
// Load the go.mod file to determine the Go version, since it hasn't
// already been populated from rawGoVersion.
- if summary, err := rawGoModSummary(mod, replacedFrom); err == nil && summary.goVersion != "" {
+ if summary, err := rawGoModSummary(mod); err == nil && summary.goVersion != "" {
m.GoVersion = summary.goVersion
}
}
@@ -289,11 +288,11 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
if rs == nil {
// If this was an explicitly-versioned argument to 'go mod download' or
// 'go list -m', report the actual requested version, not its replacement.
- completeFromModCache(info, "") // Will set m.Error in vendor mode.
+ completeFromModCache(info) // Will set m.Error in vendor mode.
return info
}
- r, replacedFrom := Replacement(m)
+ r := Replacement(m)
if r.Path == "" {
if cfg.BuildMod == "vendor" {
// It's tempting to fill in the "Dir" field to point within the vendor
@@ -302,7 +301,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
// interleave packages from different modules if one module path is a
// prefix of the other.
} else {
- completeFromModCache(info, "")
+ completeFromModCache(info)
}
return info
}
@@ -322,12 +321,12 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
if filepath.IsAbs(r.Path) {
info.Replace.Dir = r.Path
} else {
- info.Replace.Dir = filepath.Join(replacedFrom, r.Path)
+ info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path)
}
info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
}
if cfg.BuildMod != "vendor" {
- completeFromModCache(info.Replace, replacedFrom)
+ completeFromModCache(info.Replace)
info.Dir = info.Replace.Dir
info.GoMod = info.Replace.GoMod
info.Retracted = info.Replace.Retracted
@@ -336,53 +335,6 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
return info
}
-// PackageBuildInfo returns a string containing module version information
-// for modules providing packages named by path and deps. path and deps must
-// name packages that were resolved successfully with LoadPackages.
-func PackageBuildInfo(path string, deps []string) string {
- if !Enabled() {
- return ""
- }
- target, _ := findModule(loaded, path)
- mdeps := make(map[module.Version]bool)
- for _, dep := range deps {
- if m, ok := findModule(loaded, dep); ok {
- mdeps[m] = true
- }
- }
- var mods []module.Version
- delete(mdeps, target)
- for mod := range mdeps {
- mods = append(mods, mod)
- }
- module.Sort(mods)
-
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "path\t%s\n", path)
-
- writeEntry := func(token string, m module.Version) {
- mv := m.Version
- if mv == "" {
- mv = "(devel)"
- }
- fmt.Fprintf(&buf, "%s\t%s\t%s", token, m.Path, mv)
- if r, _ := Replacement(m); r.Path == "" {
- fmt.Fprintf(&buf, "\t%s\n", modfetch.Sum(m))
- } else {
- fmt.Fprintf(&buf, "\n=>\t%s\t%s\t%s\n", r.Path, r.Version, modfetch.Sum(r))
- }
- }
-
- if target.Path != "" {
- writeEntry("mod", target)
- }
- for _, mod := range mods {
- writeEntry("dep", mod)
- }
-
- return buf.String()
-}
-
// findModule searches for the module that contains the package at path.
// If the package was loaded, its containing module and true are returned.
// Otherwise, module.Version{} and false are returned.
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index 4634ad009d..27cab0b9c8 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -236,7 +236,7 @@ func (rs *Requirements) IsDirect(path string) bool {
// A ModuleGraph represents the complete graph of module dependencies
// of a main module.
//
-// If the main module is lazily loaded, the graph does not include
+// If the main module supports module graph pruning, the graph does not include
// transitive dependencies of non-root (implicit) dependencies.
type ModuleGraph struct {
g *mvs.Graph
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index e64677acd0..bc2b0a0230 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -426,35 +426,33 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver
// To avoid spurious remote fetches, try the latest replacement for each
// module (golang.org/issue/26241).
var mods []module.Version
- for _, v := range MainModules.Versions() {
- if index := MainModules.Index(v); index != nil {
- for mp, mv := range index.highestReplaced {
- if !maybeInModule(path, mp) {
- continue
- }
- if mv == "" {
- // The only replacement is a wildcard that doesn't specify a version, so
- // synthesize a pseudo-version with an appropriate major version and a
- // timestamp below any real timestamp. That way, if the main module is
- // used from within some other module, the user will be able to upgrade
- // the requirement to any real version they choose.
- if _, pathMajor, ok := module.SplitPathVersion(mp); ok && len(pathMajor) > 0 {
- mv = module.ZeroPseudoVersion(pathMajor[1:])
- } else {
- mv = module.ZeroPseudoVersion("v0")
- }
- }
- mg, err := rs.Graph(ctx)
- if err != nil {
- return module.Version{}, err
- }
- if cmpVersion(mg.Selected(mp), mv) >= 0 {
- // We can't resolve the import by adding mp@mv to the module graph,
- // because the selected version of mp is already at least mv.
- continue
- }
- mods = append(mods, module.Version{Path: mp, Version: mv})
+ if MainModules != nil { // TODO(#48912): Ensure MainModules exists at this point, and remove the check.
+ for mp, mv := range MainModules.HighestReplaced() {
+ if !maybeInModule(path, mp) {
+ continue
}
+ if mv == "" {
+ // The only replacement is a wildcard that doesn't specify a version, so
+ // synthesize a pseudo-version with an appropriate major version and a
+ // timestamp below any real timestamp. That way, if the main module is
+ // used from within some other module, the user will be able to upgrade
+ // the requirement to any real version they choose.
+ if _, pathMajor, ok := module.SplitPathVersion(mp); ok && len(pathMajor) > 0 {
+ mv = module.ZeroPseudoVersion(pathMajor[1:])
+ } else {
+ mv = module.ZeroPseudoVersion("v0")
+ }
+ }
+ mg, err := rs.Graph(ctx)
+ if err != nil {
+ return module.Version{}, err
+ }
+ if cmpVersion(mg.Selected(mp), mv) >= 0 {
+ // We can't resolve the import by adding mp@mv to the module graph,
+ // because the selected version of mp is already at least mv.
+ continue
+ }
+ mods = append(mods, module.Version{Path: mp, Version: mv})
}
}
@@ -485,7 +483,7 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver
// The package path is not valid to fetch remotely,
// so it can only exist in a replaced module,
// and we know from the above loop that it is not.
- replacement, _ := Replacement(mods[0])
+ replacement := Replacement(mods[0])
return module.Version{}, &PackageNotInModuleError{
Mod: mods[0],
Query: "latest",
@@ -659,11 +657,11 @@ func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, i
if modRoot := MainModules.ModRoot(mod); modRoot != "" {
return modRoot, true, nil
}
- if r, replacedFrom := Replacement(mod); r.Path != "" {
+ if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
- dir = filepath.Join(replacedFrom, dir)
+ dir = filepath.Join(replaceRelativeTo(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 621099beb3..0602aee0cc 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -69,9 +69,8 @@ var (
// roots are required but MainModules hasn't been initialized yet. Set to
// the modRoots of the main modules.
// modRoots != nil implies len(modRoots) > 0
- modRoots []string
- gopath string
- workFileGoVersion string
+ modRoots []string
+ gopath string
)
// Variable set in InitWorkfile
@@ -104,6 +103,10 @@ type MainModuleSet struct {
workFileGoVersion string
+ workFileReplaceMap map[module.Version]module.Version
+ // highest replaced version of each module path; empty string for wildcard-only replacements
+ highestReplaced map[string]string
+
indexMu sync.Mutex
indices map[module.Version]*modFileIndex
}
@@ -203,6 +206,10 @@ func (mms *MainModuleSet) ModContainingCWD() module.Version {
return mms.modContainingCWD
}
+func (mms *MainModuleSet) HighestReplaced() map[string]string {
+ return mms.highestReplaced
+}
+
// GoVersion returns the go version set on the single module, in module mode,
// or the go.work file in workspace mode.
func (mms *MainModuleSet) GoVersion() string {
@@ -217,6 +224,10 @@ func (mms *MainModuleSet) GoVersion() string {
return v
}
+func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {
+ return mms.workFileReplaceMap
+}
+
var MainModules *MainModuleSet
type Root int
@@ -275,6 +286,9 @@ func InitWorkfile() {
case "", "auto":
workFilePath = findWorkspaceFile(base.Cwd())
default:
+ if !filepath.IsAbs(cfg.WorkFile) {
+ base.Errorf("the path provided to -workfile must be an absolute path")
+ }
workFilePath = cfg.WorkFile
}
}
@@ -403,37 +417,6 @@ func Init() {
base.Fatalf("$GOPATH/go.mod exists but should not")
}
}
-
- if inWorkspaceMode() {
- var err error
- workFileGoVersion, modRoots, err = loadWorkFile(workFilePath)
- if err != nil {
- base.Fatalf("reading go.work: %v", err)
- }
- _ = TODOWorkspaces("Support falling back to individual module go.sum " +
- "files for sums not in the workspace sum file.")
- modfetch.GoSumFile = workFilePath + ".sum"
- } else if modRoots == nil {
- // We're in module mode, but not inside a module.
- //
- // Commands like 'go build', 'go run', 'go list' have no go.mod file to
- // read or write. They would need to find and download the latest versions
- // of a potentially large number of modules with no way to save version
- // information. We can succeed slowly (but not reproducibly), but that's
- // not usually a good experience.
- //
- // Instead, we forbid resolving import paths to modules other than std and
- // cmd. Users may still build packages specified with .go files on the
- // command line, but they'll see an error if those files import anything
- // outside std.
- //
- // This can be overridden by calling AllowMissingModuleImports.
- // For example, 'go get' does this, since it is expected to resolve paths.
- //
- // See golang.org/issue/32027.
- } else {
- modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum"
- }
}
// WillBeEnabled checks whether modules should be enabled but does not
@@ -568,16 +551,16 @@ func (goModDirtyError) Error() string {
var errGoModDirty error = goModDirtyError{}
-func loadWorkFile(path string) (goVersion string, modRoots []string, err error) {
+func loadWorkFile(path string) (goVersion string, modRoots []string, replaces []*modfile.Replace, err error) {
_ = TODOWorkspaces("Clean up and write back the go.work file: add module paths for workspace modules.")
workDir := filepath.Dir(path)
workData, err := lockedfile.Read(path)
if err != nil {
- return "", nil, err
+ return "", nil, nil, err
}
wf, err := modfile.ParseWork(path, workData, nil)
if err != nil {
- return "", nil, err
+ return "", nil, nil, err
}
if wf.Go != nil {
goVersion = wf.Go.Version
@@ -589,12 +572,12 @@ func loadWorkFile(path string) (goVersion string, modRoots []string, err error)
modRoot = filepath.Join(workDir, modRoot)
}
if seen[modRoot] {
- return "", nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot)
+ return "", nil, nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot)
}
seen[modRoot] = true
modRoots = append(modRoots, modRoot)
}
- return goVersion, modRoots, nil
+ return goVersion, modRoots, wf.Replace, nil
}
// LoadModFile sets Target and, if there is a main module, parses the initial
@@ -621,10 +604,44 @@ func LoadModFile(ctx context.Context) *Requirements {
}
Init()
+ var (
+ workFileGoVersion string
+ workFileReplaces []*modfile.Replace
+ )
+ if inWorkspaceMode() {
+ var err error
+ workFileGoVersion, modRoots, workFileReplaces, err = loadWorkFile(workFilePath)
+ if err != nil {
+ base.Fatalf("reading go.work: %v", err)
+ }
+ _ = TODOWorkspaces("Support falling back to individual module go.sum " +
+ "files for sums not in the workspace sum file.")
+ modfetch.GoSumFile = workFilePath + ".sum"
+ } else if modRoots == nil {
+ // We're in module mode, but not inside a module.
+ //
+ // Commands like 'go build', 'go run', 'go list' have no go.mod file to
+ // read or write. They would need to find and download the latest versions
+ // of a potentially large number of modules with no way to save version
+ // information. We can succeed slowly (but not reproducibly), but that's
+ // not usually a good experience.
+ //
+ // Instead, we forbid resolving import paths to modules other than std and
+ // cmd. Users may still build packages specified with .go files on the
+ // command line, but they'll see an error if those files import anything
+ // outside std.
+ //
+ // This can be overridden by calling AllowMissingModuleImports.
+ // For example, 'go get' does this, since it is expected to resolve paths.
+ //
+ // See golang.org/issue/32027.
+ } else {
+ modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum"
+ }
if len(modRoots) == 0 {
_ = TODOWorkspaces("Instead of creating a fake module with an empty modroot, make MainModules.Len() == 0 mean that we're in module mode but not inside any module.")
mainModule := module.Version{Path: "command-line-arguments"}
- MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "")
+ MainModules = makeMainModules([]module.Version{mainModule}, []string{""}, []*modfile.File{nil}, []*modFileIndex{nil}, "", nil)
goVersion := LatestGoVersion()
rawGoVersion.Store(mainModule, goVersion)
requirements = newRequirements(pruningForGoVersion(goVersion), nil, nil)
@@ -655,7 +672,7 @@ func LoadModFile(ctx context.Context) *Requirements {
}
}
- MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion)
+ MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion, workFileReplaces)
setDefaultBuildMod() // possibly enable automatic vendoring
rs := requirementsFromModFiles(ctx, modFiles)
@@ -758,7 +775,7 @@ func CreateModFile(ctx context.Context, modPath string) {
fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
modFile := new(modfile.File)
modFile.AddModuleStmt(modPath)
- MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "")
+ MainModules = makeMainModules([]module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, "", nil)
addGoStmt(modFile, modFile.Module.Mod, LatestGoVersion()) // Add the go directive before converted module requirements.
convertedFrom, err := convertLegacyConfig(modFile, modRoot)
@@ -893,7 +910,7 @@ func AllowMissingModuleImports() {
// makeMainModules creates a MainModuleSet and associated variables according to
// the given main modules.
-func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string) *MainModuleSet {
+func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile.File, indices []*modFileIndex, workFileGoVersion string, workFileReplaces []*modfile.Replace) *MainModuleSet {
for _, m := range ms {
if m.Version != "" {
panic("mainModulesCalled with module.Version with non empty Version field: " + fmt.Sprintf("%#v", m))
@@ -901,13 +918,25 @@ func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile
}
modRootContainingCWD := findModuleRoot(base.Cwd())
mainModules := &MainModuleSet{
- versions: ms[:len(ms):len(ms)],
- inGorootSrc: map[module.Version]bool{},
- pathPrefix: map[module.Version]string{},
- modRoot: map[module.Version]string{},
- modFiles: map[module.Version]*modfile.File{},
- indices: map[module.Version]*modFileIndex{},
- workFileGoVersion: workFileGoVersion,
+ versions: ms[:len(ms):len(ms)],
+ inGorootSrc: map[module.Version]bool{},
+ pathPrefix: map[module.Version]string{},
+ modRoot: map[module.Version]string{},
+ modFiles: map[module.Version]*modfile.File{},
+ indices: map[module.Version]*modFileIndex{},
+ workFileGoVersion: workFileGoVersion,
+ workFileReplaceMap: toReplaceMap(workFileReplaces),
+ highestReplaced: map[string]string{},
+ }
+ replacedByWorkFile := make(map[string]bool)
+ replacements := make(map[module.Version]module.Version)
+ for _, r := range workFileReplaces {
+ replacedByWorkFile[r.Old.Path] = true
+ v, ok := mainModules.highestReplaced[r.Old.Path]
+ if !ok || semver.Compare(r.Old.Version, v) > 0 {
+ mainModules.highestReplaced[r.Old.Path] = r.Old.Version
+ }
+ replacements[r.Old] = r.New
}
for i, m := range ms {
mainModules.pathPrefix[m] = m.Path
@@ -933,6 +962,24 @@ func makeMainModules(ms []module.Version, rootDirs []string, modFiles []*modfile
mainModules.pathPrefix[m] = ""
}
}
+
+ if modFiles[i] != nil {
+ curModuleReplaces := make(map[module.Version]bool)
+ for _, r := range modFiles[i].Replace {
+ if replacedByWorkFile[r.Old.Path] {
+ continue
+ } else if prev, ok := replacements[r.Old]; ok && !curModuleReplaces[r.Old] {
+ base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go mod editwork -replace %v=[override]\" to resolve", r.Old, prev, r.New, r.Old)
+ }
+ curModuleReplaces[r.Old] = true
+ replacements[r.Old] = r.New
+
+ v, ok := mainModules.highestReplaced[r.Old.Path]
+ if !ok || semver.Compare(r.Old.Version, v) > 0 {
+ mainModules.highestReplaced[r.Old.Path] = r.Old.Version
+ }
+ }
+ }
}
return mainModules
}
@@ -1471,7 +1518,7 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v, ok := rs.rootSelected(prefix); ok && v != "none" {
m := module.Version{Path: prefix, Version: v}
- r, _ := resolveReplacement(m)
+ r := resolveReplacement(m)
keep[r] = true
}
}
@@ -1483,7 +1530,7 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
for prefix := pkg.path; prefix != "."; prefix = path.Dir(prefix) {
if v := mg.Selected(prefix); v != "none" {
m := module.Version{Path: prefix, Version: v}
- r, _ := resolveReplacement(m)
+ r := resolveReplacement(m)
keep[r] = true
}
}
@@ -1495,7 +1542,7 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
// Save sums for the root modules (or their replacements), but don't
// incur the cost of loading the graph just to find and retain the sums.
for _, m := range rs.rootModules {
- r, _ := resolveReplacement(m)
+ r := resolveReplacement(m)
keep[modkey(r)] = true
if which == addBuildListZipSums {
keep[r] = true
@@ -1508,14 +1555,14 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
// The requirements from m's go.mod file are present in the module graph,
// so they are relevant to the MVS result regardless of whether m was
// actually selected.
- r, _ := resolveReplacement(m)
+ r := resolveReplacement(m)
keep[modkey(r)] = true
}
})
if which == addBuildListZipSums {
for _, m := range mg.BuildList() {
- r, _ := resolveReplacement(m)
+ r := resolveReplacement(m)
keep[r] = true
}
}
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 3498c662f3..845bf2f8a2 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -119,7 +119,7 @@ import (
"cmd/go/internal/mvs"
"cmd/go/internal/par"
"cmd/go/internal/search"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@@ -607,10 +607,10 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string
tryMod := func(m module.Version) (string, bool) {
var root string
var err error
- if repl, replModRoot := Replacement(m); repl.Path != "" && repl.Version == "" {
+ if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
root = repl.Path
if !filepath.IsAbs(root) {
- root = filepath.Join(replModRoot, root)
+ root = filepath.Join(replaceRelativeTo(), root)
}
} else if repl.Path != "" {
root, err = modfetch.DownloadDir(repl)
@@ -1834,7 +1834,7 @@ func (ld *loader) checkMultiplePaths() {
firstPath := map[module.Version]string{}
for _, mod := range mods {
- src, _ := resolveReplacement(mod)
+ src := resolveReplacement(mod)
if prev, ok := firstPath[src]; !ok {
firstPath[src] = mod.Path
} else if prev != mod.Path {
diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go
index bf05e92ba2..1672d563b7 100644
--- a/src/cmd/go/internal/modload/modfile.go
+++ b/src/cmd/go/internal/modload/modfile.go
@@ -33,13 +33,13 @@ const (
// tests outside of the main module.
narrowAllVersionV = "v1.16"
- // explicitIndirectVersionV is the Go version (plus leading "v") at which a
+ // ExplicitIndirectVersionV is the Go version (plus leading "v") at which a
// module's go.mod file is expected to list explicit requirements on every
// module that provides any package transitively imported by that module.
//
// Other indirect dependencies of such a module can be safely pruned out of
// the module graph; see https://golang.org/ref/mod#graph-pruning.
- explicitIndirectVersionV = "v1.17"
+ ExplicitIndirectVersionV = "v1.17"
// separateIndirectVersionV is the Go version (plus leading "v") at which
// "// indirect" dependencies are added in a block separate from the direct
@@ -99,14 +99,13 @@ func modFileGoVersion(modFile *modfile.File) string {
// A modFileIndex is an index of data corresponding to a modFile
// at a specific point in time.
type modFileIndex struct {
- data []byte
- dataNeedsFix bool // true if fixVersion applied a change while parsing data
- module module.Version
- goVersionV string // GoVersion with "v" prefix
- require map[module.Version]requireMeta
- replace map[module.Version]module.Version
- highestReplaced map[string]string // highest replaced version of each module path; empty string for wildcard-only replacements
- exclude map[module.Version]bool
+ data []byte
+ dataNeedsFix bool // true if fixVersion applied a change while parsing data
+ module module.Version
+ goVersionV string // GoVersion with "v" prefix
+ require map[module.Version]requireMeta
+ replace map[module.Version]module.Version
+ exclude map[module.Version]bool
}
type requireMeta struct {
@@ -124,7 +123,7 @@ const (
)
func pruningForGoVersion(goVersion string) modPruning {
- if semver.Compare("v"+goVersion, explicitIndirectVersionV) < 0 {
+ if semver.Compare("v"+goVersion, ExplicitIndirectVersionV) < 0 {
// The go.mod file does not duplicate relevant information about transitive
// dependencies, so they cannot be pruned out.
return unpruned
@@ -187,7 +186,7 @@ func CheckRetractions(ctx context.Context, m module.Version) (err error) {
// Cannot be retracted.
return nil
}
- if repl, _ := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
+ if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
// All versions of the module were replaced.
// Don't load retractions, since we'd just load the replacement.
return nil
@@ -204,11 +203,11 @@ func CheckRetractions(ctx context.Context, m module.Version) (err error) {
// We load the raw file here: the go.mod file may have a different module
// path that we expect if the module or its repository was renamed.
// We still want to apply retractions to other aliases of the module.
- rm, replacedFrom, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
+ rm, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
if err != nil {
return err
}
- summary, err := rawGoModSummary(rm, replacedFrom)
+ summary, err := rawGoModSummary(rm)
if err != nil {
return err
}
@@ -306,66 +305,107 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string
// Don't look up deprecation.
return "", nil
}
- if repl, _ := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
+ if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
// All versions of the module were replaced.
// We'll look up deprecation separately for the replacement.
return "", nil
}
- latest, replacedFrom, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
+ latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
if err != nil {
return "", err
}
- summary, err := rawGoModSummary(latest, replacedFrom)
+ summary, err := rawGoModSummary(latest)
if err != nil {
return "", err
}
return summary.deprecated, nil
}
-func replacement(mod module.Version, index *modFileIndex) (fromVersion string, to module.Version, ok bool) {
- if r, ok := index.replace[mod]; ok {
+func replacement(mod module.Version, replace map[module.Version]module.Version) (fromVersion string, to module.Version, ok bool) {
+ if r, ok := replace[mod]; ok {
return mod.Version, r, true
}
- if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
+ if r, ok := replace[module.Version{Path: mod.Path}]; ok {
return "", r, true
}
return "", module.Version{}, false
}
-// Replacement returns the replacement for mod, if any, and and the module root
-// directory of the main module containing the replace directive.
-// If there is no replacement for mod, Replacement returns
-// a module.Version with Path == "".
-func Replacement(mod module.Version) (module.Version, string) {
- _ = TODOWorkspaces("Support replaces in the go.work file.")
+// Replacement returns the replacement for mod, if any. If the path in the
+// module.Version is relative it's relative to the single main module outside
+// workspace mode, or the workspace's directory in workspace mode.
+func Replacement(mod module.Version) module.Version {
foundFrom, found, foundModRoot := "", module.Version{}, ""
+ if MainModules == nil {
+ return module.Version{}
+ }
+ if _, r, ok := replacement(mod, MainModules.WorkFileReplaceMap()); ok {
+ return r
+ }
for _, v := range MainModules.Versions() {
if index := MainModules.Index(v); index != nil {
- if from, r, ok := replacement(mod, index); ok {
+ if from, r, ok := replacement(mod, index.replace); ok {
modRoot := MainModules.ModRoot(v)
if foundModRoot != "" && foundFrom != from && found != r {
- _ = TODOWorkspaces("once the go.work file supports replaces, recommend them as a way to override conflicts")
base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v",
mod, modFilePath(foundModRoot), modFilePath(modRoot))
- return found, foundModRoot
+ return canonicalizeReplacePath(found, foundModRoot)
}
found, foundModRoot = r, modRoot
}
}
}
- return found, foundModRoot
+ return canonicalizeReplacePath(found, foundModRoot)
+}
+
+func replaceRelativeTo() string {
+ if workFilePath := WorkFilePath(); workFilePath != "" {
+ return filepath.Dir(workFilePath)
+ }
+ return MainModules.ModRoot(MainModules.mustGetSingleMainModule())
+}
+
+// canonicalizeReplacePath ensures that relative, on-disk, replaced module paths
+// are relative to the workspace directory (in workspace mode) or to the module's
+// directory (in module mode, as they already are).
+func canonicalizeReplacePath(r module.Version, modRoot string) module.Version {
+ if filepath.IsAbs(r.Path) || r.Version != "" {
+ return r
+ }
+ workFilePath := WorkFilePath()
+ if workFilePath == "" {
+ return r
+ }
+ abs := filepath.Join(modRoot, r.Path)
+ if rel, err := filepath.Rel(workFilePath, abs); err == nil {
+ return module.Version{Path: rel, Version: r.Version}
+ }
+ // We couldn't make the version's path relative to the workspace's path,
+ // so just return the absolute path. It's the best we can do.
+ return module.Version{Path: abs, Version: r.Version}
}
// resolveReplacement returns the module actually used to load the source code
// for m: either m itself, or the replacement for m (iff m is replaced).
// It also returns the modroot of the module providing the replacement if
// one was found.
-func resolveReplacement(m module.Version) (module.Version, string) {
- if r, replacedFrom := Replacement(m); r.Path != "" {
- return r, replacedFrom
+func resolveReplacement(m module.Version) module.Version {
+ if r := Replacement(m); r.Path != "" {
+ return r
}
- return m, ""
+ return m
+}
+
+func toReplaceMap(replacements []*modfile.Replace) map[module.Version]module.Version {
+ replaceMap := make(map[module.Version]module.Version, len(replacements))
+ for _, r := range replacements {
+ if prev, dup := replaceMap[r.Old]; dup && prev != r.New {
+ base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
+ }
+ replaceMap[r.Old] = r.New
+ }
+ return replaceMap
}
// indexModFile rebuilds the index of modFile.
@@ -396,21 +436,7 @@ func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsF
i.require[r.Mod] = requireMeta{indirect: r.Indirect}
}
- i.replace = make(map[module.Version]module.Version, len(modFile.Replace))
- for _, r := range modFile.Replace {
- if prev, dup := i.replace[r.Old]; dup && prev != r.New {
- base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v", r.Old, prev, r.New)
- }
- i.replace[r.Old] = r.New
- }
-
- i.highestReplaced = make(map[string]string)
- for _, r := range modFile.Replace {
- v, ok := i.highestReplaced[r.Old.Path]
- if !ok || semver.Compare(r.Old.Version, v) > 0 {
- i.highestReplaced[r.Old.Path] = r.Old.Version
- }
- }
+ i.replace = toReplaceMap(modFile.Replace)
i.exclude = make(map[module.Version]bool, len(modFile.Exclude))
for _, x := range modFile.Exclude {
@@ -552,7 +578,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
return summary, nil
}
- actual, replacedFrom := resolveReplacement(m)
+ actual := resolveReplacement(m)
if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" {
key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
if !modfetch.HaveSum(key) {
@@ -560,7 +586,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
return nil, module.VersionError(actual, &sumMissingError{suggestion: suggestion})
}
}
- summary, err := rawGoModSummary(actual, replacedFrom)
+ summary, err := rawGoModSummary(actual)
if err != nil {
return nil, err
}
@@ -625,22 +651,21 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
//
// rawGoModSummary cannot be used on the Target module.
-func rawGoModSummary(m module.Version, replacedFrom string) (*modFileSummary, error) {
+func rawGoModSummary(m module.Version) (*modFileSummary, error) {
if m.Path == "" && MainModules.Contains(m.Path) {
panic("internal error: rawGoModSummary called on the Target module")
}
type key struct {
- m module.Version
- replacedFrom string
+ m module.Version
}
type cached struct {
summary *modFileSummary
err error
}
- c := rawGoModSummaryCache.Do(key{m, replacedFrom}, func() interface{} {
+ c := rawGoModSummaryCache.Do(key{m}, func() interface{} {
summary := new(modFileSummary)
- name, data, err := rawGoModData(m, replacedFrom)
+ name, data, err := rawGoModData(m)
if err != nil {
return cached{nil, err}
}
@@ -690,15 +715,12 @@ var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
//
// Unlike rawGoModSummary, rawGoModData does not cache its results in memory.
// Use rawGoModSummary instead unless you specifically need these bytes.
-func rawGoModData(m module.Version, replacedFrom string) (name string, data []byte, err error) {
+func rawGoModData(m module.Version) (name string, data []byte, err error) {
if m.Version == "" {
// m is a replacement module with only a file path.
dir := m.Path
if !filepath.IsAbs(dir) {
- if replacedFrom == "" {
- panic(fmt.Errorf("missing module root of main module providing replacement with relative path: %v", dir))
- }
- dir = filepath.Join(replacedFrom, dir)
+ dir = filepath.Join(replaceRelativeTo(), dir)
}
name = filepath.Join(dir, "go.mod")
if gomodActual, ok := fsys.OverlayPath(name); ok {
@@ -733,20 +755,19 @@ func rawGoModData(m module.Version, replacedFrom string) (name string, data []by
//
// If the queried latest version is replaced,
// queryLatestVersionIgnoringRetractions returns the replacement.
-func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, replacedFrom string, err error) {
+func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (latest module.Version, err error) {
type entry struct {
- latest module.Version
- replacedFrom string // if latest is a replacement
- err error
+ latest module.Version
+ err error
}
e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
defer span.Done()
- if repl, replFrom := Replacement(module.Version{Path: path}); repl.Path != "" {
+ if repl := Replacement(module.Version{Path: path}); repl.Path != "" {
// All versions of the module were replaced.
// No need to query.
- return &entry{latest: repl, replacedFrom: replFrom}
+ return &entry{latest: repl}
}
// Find the latest version of the module.
@@ -758,12 +779,12 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la
return &entry{err: err}
}
latest := module.Version{Path: path, Version: rev.Version}
- if repl, replFrom := resolveReplacement(latest); repl.Path != "" {
- latest, replacedFrom = repl, replFrom
+ if repl := resolveReplacement(latest); repl.Path != "" {
+ latest = repl
}
- return &entry{latest: latest, replacedFrom: replacedFrom}
+ return &entry{latest: latest}
}).(*entry)
- return e.latest, e.replacedFrom, e.err
+ return e.latest, e.err
}
var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index 82979fbda1..1eb484de9d 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -22,7 +22,7 @@ import (
"cmd/go/internal/modfetch"
"cmd/go/internal/search"
"cmd/go/internal/trace"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
@@ -513,7 +513,7 @@ func QueryPackages(ctx context.Context, pattern, query string, current func(stri
pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
if len(pkgMods) == 0 && err == nil {
- replacement, _ := Replacement(modOnly.Mod)
+ replacement := Replacement(modOnly.Mod)
return nil, &PackageNotInModuleError{
Mod: modOnly.Mod,
Replacement: replacement,
@@ -669,7 +669,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
if err := firstError(m); err != nil {
return r, err
}
- replacement, _ := Replacement(r.Mod)
+ replacement := Replacement(r.Mod)
return r, &PackageNotInModuleError{
Mod: r.Mod,
Replacement: replacement,
@@ -969,7 +969,7 @@ func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
// we don't need to verify it in go.sum. This makes 'go list -m -u' faster
// and simpler.
func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
- _, data, err := rawGoModData(m, "")
+ _, data, err := rawGoModData(m)
if err != nil {
return false, err
}
@@ -996,15 +996,10 @@ func lookupRepo(proxy, path string) (repo versionRepo, err error) {
repo = emptyRepo{path: path, err: err}
}
- // TODO(#45713): Join all the highestReplaced fields into a single value.
- for _, mm := range MainModules.Versions() {
- index := MainModules.Index(mm)
- if index == nil {
- continue
- }
- if _, ok := index.highestReplaced[path]; ok {
- return &replacementRepo{repo: repo}, nil
- }
+ if MainModules == nil {
+ return repo, err
+ } else if _, ok := MainModules.HighestReplaced()[path]; ok {
+ return &replacementRepo{repo: repo}, nil
}
return repo, err
@@ -1098,7 +1093,7 @@ func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
}
}
- if r, _ := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
+ if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
return info, err
}
return rr.replacementStat(v)
@@ -1108,24 +1103,7 @@ func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
info, err := rr.repo.Latest()
path := rr.ModulePath()
- highestReplaced, found := "", false
- for _, mm := range MainModules.Versions() {
- if index := MainModules.Index(mm); index != nil {
- if v, ok := index.highestReplaced[path]; ok {
- if !found {
- highestReplaced, found = v, true
- continue
- }
- if semver.Compare(v, highestReplaced) > 0 {
- highestReplaced = v
- }
- }
- }
- }
-
- if found {
- v := highestReplaced
-
+ if v, ok := MainModules.HighestReplaced()[path]; ok {
if v == "" {
// The only replacement is a wildcard that doesn't specify a version, so
// synthesize a pseudo-version with an appropriate major version and a
diff --git a/src/cmd/go/internal/modload/stat_openfile.go b/src/cmd/go/internal/modload/stat_openfile.go
index 368f893198..ff7c124af5 100644
--- a/src/cmd/go/internal/modload/stat_openfile.go
+++ b/src/cmd/go/internal/modload/stat_openfile.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build (js && wasm) || plan9
-// +build js,wasm plan9
// On plan9, per http://9p.io/magic/man2html/2/access: “Since file permissions
// are checked by the server and group information is not known to the client,
diff --git a/src/cmd/go/internal/modload/stat_unix.go b/src/cmd/go/internal/modload/stat_unix.go
index e079d73990..8a3653ba80 100644
--- a/src/cmd/go/internal/modload/stat_unix.go
+++ b/src/cmd/go/internal/modload/stat_unix.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package modload
diff --git a/src/cmd/go/internal/modload/stat_windows.go b/src/cmd/go/internal/modload/stat_windows.go
index 825e60b27a..f29a99165e 100644
--- a/src/cmd/go/internal/modload/stat_windows.go
+++ b/src/cmd/go/internal/modload/stat_windows.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build windows
-// +build windows
package modload
diff --git a/src/cmd/go/internal/modload/vendor.go b/src/cmd/go/internal/modload/vendor.go
index daa5888075..a735cad905 100644
--- a/src/cmd/go/internal/modload/vendor.go
+++ b/src/cmd/go/internal/modload/vendor.go
@@ -209,7 +209,7 @@ func checkVendorConsistency(index *modFileIndex, modFile *modfile.File) {
}
for _, mod := range vendorReplaced {
- r, _ := Replacement(mod)
+ r := Replacement(mod)
if r == (module.Version{}) {
vendErrorf(mod, "is marked as replaced in vendor/modules.txt, but not replaced in go.mod")
continue
diff --git a/src/cmd/go/internal/robustio/robustio_flaky.go b/src/cmd/go/internal/robustio/robustio_flaky.go
index d5c241857b..c56e36ca62 100644
--- a/src/cmd/go/internal/robustio/robustio_flaky.go
+++ b/src/cmd/go/internal/robustio/robustio_flaky.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build windows || darwin
-// +build windows darwin
package robustio
diff --git a/src/cmd/go/internal/robustio/robustio_other.go b/src/cmd/go/internal/robustio/robustio_other.go
index 3a20cac6cf..da9a46e4fa 100644
--- a/src/cmd/go/internal/robustio/robustio_other.go
+++ b/src/cmd/go/internal/robustio/robustio_other.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !windows && !darwin
-// +build !windows,!darwin
package robustio
diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go
index 11e2c81b9a..03895d27eb 100644
--- a/src/cmd/go/internal/run/run.go
+++ b/src/cmd/go/internal/run/run.go
@@ -19,7 +19,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
var CmdRun = &base.Command{
diff --git a/src/cmd/internal/str/path.go b/src/cmd/go/internal/str/path.go
similarity index 74%
rename from src/cmd/internal/str/path.go
rename to src/cmd/go/internal/str/path.go
index 51ab2af82b..0c8aaeaca1 100644
--- a/src/cmd/internal/str/path.go
+++ b/src/cmd/go/internal/str/path.go
@@ -49,3 +49,17 @@ func HasFilePathPrefix(s, prefix string) bool {
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}
+
+// TrimFilePathPrefix returns s without the leading path elements in prefix.
+// If s does not start with prefix (HasFilePathPrefix with the same arguments
+// returns false), TrimFilePathPrefix returns s. If s equals prefix,
+// TrimFilePathPrefix returns "".
+func TrimFilePathPrefix(s, prefix string) string {
+ if !HasFilePathPrefix(s, prefix) {
+ return s
+ }
+ if len(s) == len(prefix) {
+ return ""
+ }
+ return s[len(prefix)+1:]
+}
diff --git a/src/cmd/go/internal/str/str.go b/src/cmd/go/internal/str/str.go
new file mode 100644
index 0000000000..5bc521b9df
--- /dev/null
+++ b/src/cmd/go/internal/str/str.go
@@ -0,0 +1,111 @@
+// Copyright 2017 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 str provides string manipulation utilities.
+package str
+
+import (
+ "bytes"
+ "fmt"
+ "unicode"
+ "unicode/utf8"
+)
+
+// StringList flattens its arguments into a single []string.
+// Each argument in args must have type string or []string.
+func StringList(args ...interface{}) []string {
+ var x []string
+ for _, arg := range args {
+ switch arg := arg.(type) {
+ case []string:
+ x = append(x, arg...)
+ case string:
+ x = append(x, arg)
+ default:
+ panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
+ }
+ }
+ return x
+}
+
+// ToFold returns a string with the property that
+// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
+// This lets us test a large set of strings for fold-equivalent
+// duplicates without making a quadratic number of calls
+// to EqualFold. Note that strings.ToUpper and strings.ToLower
+// do not have the desired property in some corner cases.
+func ToFold(s string) string {
+ // Fast path: all ASCII, no upper case.
+ // Most paths look like this already.
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
+ goto Slow
+ }
+ }
+ return s
+
+Slow:
+ var buf bytes.Buffer
+ for _, r := range s {
+ // SimpleFold(x) cycles to the next equivalent rune > x
+ // or wraps around to smaller values. Iterate until it wraps,
+ // and we've found the minimum value.
+ for {
+ r0 := r
+ r = unicode.SimpleFold(r0)
+ if r <= r0 {
+ break
+ }
+ }
+ // Exception to allow fast path above: A-Z => a-z
+ if 'A' <= r && r <= 'Z' {
+ r += 'a' - 'A'
+ }
+ buf.WriteRune(r)
+ }
+ return buf.String()
+}
+
+// FoldDup reports a pair of strings from the list that are
+// equal according to strings.EqualFold.
+// It returns "", "" if there are no such strings.
+func FoldDup(list []string) (string, string) {
+ clash := map[string]string{}
+ for _, s := range list {
+ fold := ToFold(s)
+ if t := clash[fold]; t != "" {
+ if s > t {
+ s, t = t, s
+ }
+ return s, t
+ }
+ clash[fold] = s
+ }
+ return "", ""
+}
+
+// Contains reports whether x contains s.
+func Contains(x []string, s string) bool {
+ for _, t := range x {
+ if t == s {
+ return true
+ }
+ }
+ return false
+}
+
+// Uniq removes consecutive duplicate strings from ss.
+func Uniq(ss *[]string) {
+ if len(*ss) <= 1 {
+ return
+ }
+ uniq := (*ss)[:1]
+ for _, s := range *ss {
+ if s != uniq[len(uniq)-1] {
+ uniq = append(uniq, s)
+ }
+ }
+ *ss = uniq
+}
diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go
new file mode 100644
index 0000000000..8ea758e0a8
--- /dev/null
+++ b/src/cmd/go/internal/str/str_test.go
@@ -0,0 +1,29 @@
+// Copyright 2020 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 str
+
+import (
+ "testing"
+)
+
+var foldDupTests = []struct {
+ list []string
+ f1, f2 string
+}{
+ {StringList("math/rand", "math/big"), "", ""},
+ {StringList("math", "strings"), "", ""},
+ {StringList("strings"), "", ""},
+ {StringList("strings", "strings"), "strings", "strings"},
+ {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
+}
+
+func TestFoldDup(t *testing.T) {
+ for _, tt := range foldDupTests {
+ f1, f2 := FoldDup(tt.list)
+ if f1 != tt.f1 || f2 != tt.f2 {
+ t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
+ }
+ }
+}
diff --git a/src/cmd/go/internal/test/genflags.go b/src/cmd/go/internal/test/genflags.go
index cba366062f..10f290090c 100644
--- a/src/cmd/go/internal/test/genflags.go
+++ b/src/cmd/go/internal/test/genflags.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build ignore
-// +build ignore
package main
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index dc1bea505b..7361c11786 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -31,10 +31,13 @@ import (
"cmd/go/internal/lockedfile"
"cmd/go/internal/modload"
"cmd/go/internal/search"
+ "cmd/go/internal/str"
"cmd/go/internal/trace"
"cmd/go/internal/work"
- "cmd/internal/str"
+ "cmd/internal/sys"
"cmd/internal/test2json"
+
+ "golang.org/x/mod/module"
)
// Break init loop.
@@ -247,11 +250,12 @@ control the execution of any test:
-fuzz regexp
Run the fuzz target matching the regular expression. When specified,
- the command line argument must match exactly one package, and regexp
- must match exactly one fuzz target within that package. After tests,
- benchmarks, seed corpora of other fuzz targets, and examples have
- completed, the matching target will be fuzzed. See the Fuzzing section
- of the testing package documentation for details.
+ the command line argument must match exactly one package within the
+ main module, and regexp must match exactly one fuzz target within
+ that package. After tests, benchmarks, seed corpora of other fuzz
+ targets, and examples have completed, the matching target will be
+ fuzzed. See the Fuzzing section of the testing package documentation
+ for details.
-fuzztime t
Run enough iterations of the fuzz test to take t, specified as a
@@ -303,11 +307,11 @@ control the execution of any test:
exhaustive tests.
-shuffle off,on,N
- Randomize the execution order of tests and benchmarks.
- It is off by default. If -shuffle is set to on, then it will seed
- the randomizer using the system clock. If -shuffle is set to an
- integer N, then N will be used as the seed value. In both cases,
- the seed will be reported for reproducibility.
+ Randomize the execution order of tests and benchmarks.
+ It is off by default. If -shuffle is set to on, then it will seed
+ the randomizer using the system clock. If -shuffle is set to an
+ integer N, then N will be used as the seed value. In both cases,
+ the seed will be reported for reproducibility.
-timeout d
If a test binary runs longer than duration d, panic.
@@ -403,7 +407,11 @@ leave the test binary in pkg.test for use when analyzing the profiles.
When 'go test' runs a test binary, it does so from within the
corresponding package's source code directory. Depending on the test,
it may be necessary to do the same when invoking a generated test
-binary directly.
+binary directly. Because that directory may be located within the
+module cache, which may be read-only and is verified by checksums, the
+test must not write to it or any other directory within the module
+unless explicitly requested by the user (such as with the -fuzz flag,
+which writes failures to testdata/fuzz).
The command-line package list, if present, must appear before any
flag not known to the go test command. Continuing the example above,
@@ -651,8 +659,51 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
if testO != "" && len(pkgs) != 1 {
base.Fatalf("cannot use -o flag with multiple packages")
}
- if testFuzz != "" && len(pkgs) != 1 {
- base.Fatalf("cannot use -fuzz flag with multiple packages")
+ if testFuzz != "" {
+ if !sys.FuzzSupported(cfg.Goos, cfg.Goarch) {
+ base.Fatalf("-fuzz flag is not supported on %s/%s", cfg.Goos, cfg.Goarch)
+ }
+ if len(pkgs) != 1 {
+ base.Fatalf("cannot use -fuzz flag with multiple packages")
+ }
+ if testCoverProfile != "" {
+ base.Fatalf("cannot use -coverprofile flag with -fuzz flag")
+ }
+ if profileFlag := testProfile(); profileFlag != "" {
+ base.Fatalf("cannot use %s flag with -fuzz flag", profileFlag)
+ }
+
+ // Reject the '-fuzz' flag if the package is outside the main module.
+ // Otherwise, if fuzzing identifies a failure it could corrupt checksums in
+ // the module cache (or permanently alter the behavior of std tests for all
+ // users) by writing the failing input to the package's testdata directory.
+ // (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.)
+ mainMods := modload.MainModules
+ if m := pkgs[0].Module; m != nil && m.Path != "" {
+ if !mainMods.Contains(m.Path) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ } else if pkgs[0].Standard && modload.Enabled() {
+ // Because packages in 'std' and 'cmd' are part of the standard library,
+ // they are only treated as part of a module in 'go mod' subcommands and
+ // 'go get'. However, we still don't want to accidentally corrupt their
+ // testdata during fuzzing, nor do we want to fail with surprising errors
+ // if GOROOT isn't writable (as is often the case for Go toolchains
+ // installed through package managers).
+ //
+ // If the user is requesting to fuzz a standard-library package, ensure
+ // that they are in the same module as that package (just like when
+ // fuzzing any other package).
+ if strings.HasPrefix(pkgs[0].ImportPath, "cmd/") {
+ if !mainMods.Contains("cmd") || !mainMods.InGorootSrc(module.Version{Path: "cmd"}) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ } else {
+ if !mainMods.Contains("std") || !mainMods.InGorootSrc(module.Version{Path: "std"}) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ }
+ }
}
if testProfile() != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile())
@@ -1354,15 +1405,25 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
if bytes.HasPrefix(out, tooManyTargetsToFuzz[1:]) || bytes.Contains(out, tooManyTargetsToFuzz) {
norun = " [will not fuzz, -fuzz matches more than one target]"
}
+ if len(out) > 0 && !bytes.HasSuffix(out, []byte("\n")) {
+ // Ensure that the output ends with a newline before the "ok"
+ // line we're about to print (https://golang.org/issue/49317).
+ cmd.Stdout.Write([]byte("\n"))
+ }
fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
c.saveOutput(a)
} else {
base.SetExitStatus(1)
- // If there was test output, assume we don't need to print the exit status.
- // Buf there's no test output, do print the exit status.
if len(out) == 0 {
+ // If there was no test output, print the exit status so that the reason
+ // for failure is clear.
fmt.Fprintf(cmd.Stdout, "%s\n", err)
+ } else if !bytes.HasSuffix(out, []byte("\n")) {
+ // Otherwise, ensure that the output ends with a newline before the FAIL
+ // line we're about to print (https://golang.org/issue/49317).
+ cmd.Stdout.Write([]byte("\n"))
}
+
// NOTE(golang.org/issue/37555): test2json reports that a test passes
// unless "FAIL" is printed at the beginning of a line. The test may not
// actually print that if it panics, exits, or terminates abnormally,
diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go
index 97b2a631ae..b2ce80325a 100644
--- a/src/cmd/go/internal/vcs/vcs.go
+++ b/src/cmd/go/internal/vcs/vcs.go
@@ -5,6 +5,7 @@
package vcs
import (
+ "bytes"
"encoding/json"
"errors"
"fmt"
@@ -17,23 +18,26 @@ import (
"os"
"path/filepath"
"regexp"
+ "strconv"
"strings"
"sync"
+ "time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/search"
"cmd/go/internal/web"
- "cmd/internal/str"
+ "cmd/go/internal/str"
"golang.org/x/mod/module"
)
-// A vcsCmd describes how to use a version control system
+// A Cmd describes how to use a version control system
// like Mercurial, Git, or Subversion.
type Cmd struct {
- Name string
- Cmd string // name of binary to invoke command
+ Name string
+ Cmd string // name of binary to invoke command
+ RootNames []string // filename indicating the root of a checkout directory
CreateCmd []string // commands to download a fresh copy of a repository
DownloadCmd []string // commands to download updates into an existing repository
@@ -48,6 +52,14 @@ type Cmd struct {
RemoteRepo func(v *Cmd, rootDir string) (remoteRepo string, err error)
ResolveRepo func(v *Cmd, rootDir, remoteRepo string) (realRepo string, err error)
+ Status func(v *Cmd, rootDir string) (Status, error)
+}
+
+// Status is the current state of a local repository.
+type Status struct {
+ Revision string // Optional.
+ CommitTime time.Time // Optional.
+ Uncommitted bool // Required.
}
var defaultSecureScheme = map[string]bool{
@@ -118,8 +130,9 @@ func vcsByCmd(cmd string) *Cmd {
// vcsHg describes how to use Mercurial.
var vcsHg = &Cmd{
- Name: "Mercurial",
- Cmd: "hg",
+ Name: "Mercurial",
+ Cmd: "hg",
+ RootNames: []string{".hg"},
CreateCmd: []string{"clone -U -- {repo} {dir}"},
DownloadCmd: []string{"pull"},
@@ -139,6 +152,7 @@ var vcsHg = &Cmd{
Scheme: []string{"https", "http", "ssh"},
PingCmd: "identify -- {scheme}://{repo}",
RemoteRepo: hgRemoteRepo,
+ Status: hgStatus,
}
func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) {
@@ -149,10 +163,60 @@ func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) {
return strings.TrimSpace(string(out)), nil
}
+func hgStatus(vcsHg *Cmd, rootDir string) (Status, error) {
+ // Output changeset ID and seconds since epoch.
+ out, err := vcsHg.runOutputVerboseOnly(rootDir, `log -l1 -T {node}:{date(date,"%s")}`)
+ if err != nil {
+ return Status{}, err
+ }
+
+ // Successful execution without output indicates an empty repo (no commits).
+ var rev string
+ var commitTime time.Time
+ if len(out) > 0 {
+ rev, commitTime, err = parseRevTime(out)
+ if err != nil {
+ return Status{}, err
+ }
+ }
+
+ // Also look for untracked files.
+ out, err = vcsHg.runOutputVerboseOnly(rootDir, "status")
+ if err != nil {
+ return Status{}, err
+ }
+ uncommitted := len(out) > 0
+
+ return Status{
+ Revision: rev,
+ CommitTime: commitTime,
+ Uncommitted: uncommitted,
+ }, nil
+}
+
+// parseRevTime parses commit details in "revision:seconds" format.
+func parseRevTime(out []byte) (string, time.Time, error) {
+ buf := string(bytes.TrimSpace(out))
+
+ i := strings.IndexByte(buf, ':')
+ if i < 1 {
+ return "", time.Time{}, errors.New("unrecognized VCS tool output")
+ }
+ rev := buf[:i]
+
+ secs, err := strconv.ParseInt(string(buf[i+1:]), 10, 64)
+ if err != nil {
+ return "", time.Time{}, fmt.Errorf("unrecognized VCS tool output: %v", err)
+ }
+
+ return rev, time.Unix(secs, 0), nil
+}
+
// vcsGit describes how to use Git.
var vcsGit = &Cmd{
- Name: "Git",
- Cmd: "git",
+ Name: "Git",
+ Cmd: "git",
+ RootNames: []string{".git"},
CreateCmd: []string{"clone -- {repo} {dir}", "-go-internal-cd {dir} submodule update --init --recursive"},
DownloadCmd: []string{"pull --ff-only", "submodule update --init --recursive"},
@@ -182,6 +246,7 @@ var vcsGit = &Cmd{
PingCmd: "ls-remote {scheme}://{repo}",
RemoteRepo: gitRemoteRepo,
+ Status: gitStatus,
}
// scpSyntaxRe matches the SCP-like addresses used by Git to access
@@ -232,10 +297,40 @@ func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) {
return "", errParse
}
+func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) {
+ out, err := vcsGit.runOutputVerboseOnly(rootDir, "status --porcelain")
+ if err != nil {
+ return Status{}, err
+ }
+ uncommitted := len(out) > 0
+
+ // "git status" works for empty repositories, but "git show" does not.
+ // Assume there are no commits in the repo when "git show" fails with
+ // uncommitted files and skip tagging revision / committime.
+ var rev string
+ var commitTime time.Time
+ out, err = vcsGit.runOutputVerboseOnly(rootDir, "show -s --format=%H:%ct")
+ if err != nil && !uncommitted {
+ return Status{}, err
+ } else if err == nil {
+ rev, commitTime, err = parseRevTime(out)
+ if err != nil {
+ return Status{}, err
+ }
+ }
+
+ return Status{
+ Revision: rev,
+ CommitTime: commitTime,
+ Uncommitted: uncommitted,
+ }, nil
+}
+
// vcsBzr describes how to use Bazaar.
var vcsBzr = &Cmd{
- Name: "Bazaar",
- Cmd: "bzr",
+ Name: "Bazaar",
+ Cmd: "bzr",
+ RootNames: []string{".bzr"},
CreateCmd: []string{"branch -- {repo} {dir}"},
@@ -251,6 +346,7 @@ var vcsBzr = &Cmd{
PingCmd: "info -- {scheme}://{repo}",
RemoteRepo: bzrRemoteRepo,
ResolveRepo: bzrResolveRepo,
+ Status: bzrStatus,
}
func bzrRemoteRepo(vcsBzr *Cmd, rootDir string) (remoteRepo string, err error) {
@@ -294,10 +390,68 @@ func bzrResolveRepo(vcsBzr *Cmd, rootDir, remoteRepo string) (realRepo string, e
return strings.TrimSpace(out), nil
}
+func bzrStatus(vcsBzr *Cmd, rootDir string) (Status, error) {
+ outb, err := vcsBzr.runOutputVerboseOnly(rootDir, "version-info")
+ if err != nil {
+ return Status{}, err
+ }
+ out := string(outb)
+
+ // Expect (non-empty repositories only):
+ //
+ // revision-id: gopher@gopher.net-20211021072330-qshok76wfypw9lpm
+ // date: 2021-09-21 12:00:00 +1000
+ // ...
+ var rev string
+ var commitTime time.Time
+
+ for _, line := range strings.Split(out, "\n") {
+ i := strings.IndexByte(line, ':')
+ if i < 0 {
+ continue
+ }
+ key := line[:i]
+ value := strings.TrimSpace(line[i+1:])
+
+ switch key {
+ case "revision-id":
+ rev = value
+ case "date":
+ var err error
+ commitTime, err = time.Parse("2006-01-02 15:04:05 -0700", value)
+ if err != nil {
+ return Status{}, errors.New("unable to parse output of bzr version-info")
+ }
+ }
+ }
+
+ outb, err = vcsBzr.runOutputVerboseOnly(rootDir, "status")
+ if err != nil {
+ return Status{}, err
+ }
+
+ // Skip warning when working directory is set to an older revision.
+ if bytes.HasPrefix(outb, []byte("working tree is out of date")) {
+ i := bytes.IndexByte(outb, '\n')
+ if i < 0 {
+ i = len(outb)
+ }
+ outb = outb[:i]
+ }
+ uncommitted := len(outb) > 0
+
+ return Status{
+ Revision: rev,
+ CommitTime: commitTime,
+ Uncommitted: uncommitted,
+ }, nil
+}
+
// vcsSvn describes how to use Subversion.
var vcsSvn = &Cmd{
- Name: "Subversion",
- Cmd: "svn",
+ Name: "Subversion",
+ Cmd: "svn",
+ RootNames: []string{".svn"},
CreateCmd: []string{"checkout -- {repo} {dir}"},
DownloadCmd: []string{"update"},
@@ -346,8 +500,9 @@ const fossilRepoName = ".fossil"
// vcsFossil describes how to use Fossil (fossil-scm.org)
var vcsFossil = &Cmd{
- Name: "Fossil",
- Cmd: "fossil",
+ Name: "Fossil",
+ Cmd: "fossil",
+ RootNames: []string{".fslckout", "_FOSSIL_"},
CreateCmd: []string{"-go-internal-mkdir {dir} clone -- {repo} " + filepath.Join("{dir}", fossilRepoName), "-go-internal-cd {dir} open .fossil"},
DownloadCmd: []string{"up"},
@@ -358,6 +513,7 @@ var vcsFossil = &Cmd{
Scheme: []string{"https", "http"},
RemoteRepo: fossilRemoteRepo,
+ Status: fossilStatus,
}
func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err error) {
@@ -368,6 +524,60 @@ func fossilRemoteRepo(vcsFossil *Cmd, rootDir string) (remoteRepo string, err er
return strings.TrimSpace(string(out)), nil
}
+var errFossilInfo = errors.New("unable to parse output of fossil info")
+
+func fossilStatus(vcsFossil *Cmd, rootDir string) (Status, error) {
+ outb, err := vcsFossil.runOutputVerboseOnly(rootDir, "info")
+ if err != nil {
+ return Status{}, err
+ }
+ out := string(outb)
+
+ // Expect:
+ // ...
+ // checkout: 91ed71f22c77be0c3e250920f47bfd4e1f9024d2 2021-09-21 12:00:00 UTC
+ // ...
+
+ // Extract revision and commit time.
+ // Ensure line ends with UTC (known timezone offset).
+ const prefix = "\ncheckout:"
+ const suffix = " UTC"
+ i := strings.Index(out, prefix)
+ if i < 0 {
+ return Status{}, errFossilInfo
+ }
+ checkout := out[i+len(prefix):]
+ i = strings.Index(checkout, suffix)
+ if i < 0 {
+ return Status{}, errFossilInfo
+ }
+ checkout = strings.TrimSpace(checkout[:i])
+
+ i = strings.IndexByte(checkout, ' ')
+ if i < 0 {
+ return Status{}, errFossilInfo
+ }
+ rev := checkout[:i]
+
+ commitTime, err := time.ParseInLocation("2006-01-02 15:04:05", checkout[i+1:], time.UTC)
+ if err != nil {
+ return Status{}, fmt.Errorf("%v: %v", errFossilInfo, err)
+ }
+
+ // Also look for untracked changes.
+ outb, err = vcsFossil.runOutputVerboseOnly(rootDir, "changes --differ")
+ if err != nil {
+ return Status{}, err
+ }
+ uncommitted := len(outb) > 0
+
+ return Status{
+ Revision: rev,
+ CommitTime: commitTime,
+ Uncommitted: uncommitted,
+ }, nil
+}
+
func (v *Cmd) String() string {
return v.Name
}
@@ -395,6 +605,12 @@ func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error
return v.run1(dir, cmd, keyval, true)
}
+// runOutputVerboseOnly is like runOutput but only generates error output to
+// standard error in verbose mode.
+func (v *Cmd) runOutputVerboseOnly(dir string, cmd string, keyval ...string) ([]byte, error) {
+ return v.run1(dir, cmd, keyval, false)
+}
+
// run1 is the generalized implementation of run and runOutput.
func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
m := make(map[string]string)
@@ -550,58 +766,86 @@ type vcsPath struct {
// FromDir inspects dir and its parents to determine the
// version control system and code repository to use.
-// On return, root is the import path
-// corresponding to the root of the repository.
-func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) {
+// If no repository is found, FromDir returns an error
+// equivalent to os.ErrNotExist.
+func FromDir(dir, srcRoot string, allowNesting bool) (repoDir string, vcsCmd *Cmd, err error) {
// Clean and double-check that dir is in (a subdirectory of) srcRoot.
dir = filepath.Clean(dir)
- srcRoot = filepath.Clean(srcRoot)
- if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
- return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
+ if srcRoot != "" {
+ srcRoot = filepath.Clean(srcRoot)
+ if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
+ return "", nil, fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
+ }
}
- var vcsRet *Cmd
- var rootRet string
-
origDir := dir
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
- if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil {
- root := filepath.ToSlash(dir[len(srcRoot)+1:])
- // Record first VCS we find, but keep looking,
- // to detect mistakes like one kind of VCS inside another.
- if vcsRet == nil {
- vcsRet = vcs
- rootRet = root
+ if _, err := statAny(dir, vcs.RootNames); err == nil {
+ // Record first VCS we find.
+ // If allowNesting is false (as it is in GOPATH), keep looking for
+ // repositories in parent directories and report an error if one is
+ // found to mitigate VCS injection attacks.
+ if vcsCmd == nil {
+ vcsCmd = vcs
+ repoDir = dir
+ if allowNesting {
+ return repoDir, vcsCmd, nil
+ }
continue
}
// Allow .git inside .git, which can arise due to submodules.
- if vcsRet == vcs && vcs.Cmd == "git" {
+ if vcsCmd == vcs && vcs.Cmd == "git" {
continue
}
// Otherwise, we have one VCS inside a different VCS.
- return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s",
- filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd)
+ return "", nil, fmt.Errorf("directory %q uses %s, but parent %q uses %s",
+ repoDir, vcsCmd.Cmd, dir, vcs.Cmd)
}
}
// Move to parent.
ndir := filepath.Dir(dir)
if len(ndir) >= len(dir) {
- // Shouldn't happen, but just in case, stop.
break
}
dir = ndir
}
+ if vcsCmd == nil {
+ return "", nil, &vcsNotFoundError{dir: origDir}
+ }
+ return repoDir, vcsCmd, nil
+}
- if vcsRet != nil {
- if err := checkGOVCS(vcsRet, rootRet); err != nil {
- return nil, "", err
- }
- return vcsRet, rootRet, nil
+// statAny provides FileInfo for the first filename found in the directory.
+// Otherwise, it returns the last error seen.
+func statAny(dir string, filenames []string) (os.FileInfo, error) {
+ if len(filenames) == 0 {
+ return nil, errors.New("invalid argument: no filenames provided")
}
- return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
+ var err error
+ var fi os.FileInfo
+ for _, name := range filenames {
+ fi, err = os.Stat(filepath.Join(dir, name))
+ if err == nil {
+ return fi, nil
+ }
+ }
+
+ return nil, err
+}
+
+type vcsNotFoundError struct {
+ dir string
+}
+
+func (e *vcsNotFoundError) Error() string {
+ return fmt.Sprintf("directory %q is not using a known version control system", e.dir)
+}
+
+func (e *vcsNotFoundError) Is(err error) bool {
+ return err == os.ErrNotExist
}
// A govcsRule is a single GOVCS rule like private:hg|svn.
@@ -707,7 +951,11 @@ var defaultGOVCS = govcsConfig{
{"public", []string{"git", "hg"}},
}
-func checkGOVCS(vcs *Cmd, root string) error {
+// CheckGOVCS checks whether the policy defined by the environment variable
+// GOVCS allows the given vcs command to be used with the given repository
+// root path. Note that root may not be a real package or module path; it's
+// the same as the root path in the go-import meta tag.
+func CheckGOVCS(vcs *Cmd, root string) error {
if vcs == vcsMod {
// Direct module (proxy protocol) fetches don't
// involve an external version control system
@@ -745,7 +993,7 @@ func CheckNested(vcs *Cmd, dir, srcRoot string) error {
otherDir := dir
for len(otherDir) > len(srcRoot) {
for _, otherVCS := range vcsList {
- if _, err := os.Stat(filepath.Join(otherDir, "."+otherVCS.Cmd)); err == nil {
+ if _, err := statAny(otherDir, otherVCS.RootNames); err == nil {
// Allow expected vcs in original dir.
if otherDir == dir && otherVCS == vcs {
continue
@@ -885,7 +1133,7 @@ func repoRootFromVCSPaths(importPath string, security web.SecurityMode, vcsPaths
if vcs == nil {
return nil, fmt.Errorf("unknown version control system %q", match["vcs"])
}
- if err := checkGOVCS(vcs, match["root"]); err != nil {
+ if err := CheckGOVCS(vcs, match["root"]); err != nil {
return nil, err
}
var repoURL string
@@ -1012,7 +1260,7 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
}
}
- if err := checkGOVCS(vcs, mmi.Prefix); err != nil {
+ if err := CheckGOVCS(vcs, mmi.Prefix); err != nil {
return nil, err
}
diff --git a/src/cmd/go/internal/vcs/vcs_test.go b/src/cmd/go/internal/vcs/vcs_test.go
index c5c7a3283b..c4e4f4d3c6 100644
--- a/src/cmd/go/internal/vcs/vcs_test.go
+++ b/src/cmd/go/internal/vcs/vcs_test.go
@@ -6,9 +6,9 @@ package vcs
import (
"errors"
+ "fmt"
"internal/testenv"
"os"
- "path"
"path/filepath"
"strings"
"testing"
@@ -205,7 +205,8 @@ func TestRepoRootForImportPath(t *testing.T) {
}
}
-// Test that vcsFromDir correctly inspects a given directory and returns the right VCS and root.
+// Test that vcs.FromDir correctly inspects a given directory and returns the
+// right VCS and repo directory.
func TestFromDir(t *testing.T) {
tempDir, err := os.MkdirTemp("", "vcstest")
if err != nil {
@@ -214,36 +215,35 @@ func TestFromDir(t *testing.T) {
defer os.RemoveAll(tempDir)
for j, vcs := range vcsList {
- dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd)
- if j&1 == 0 {
- err := os.MkdirAll(dir, 0755)
- if err != nil {
- t.Fatal(err)
+ for r, rootName := range vcs.RootNames {
+ vcsName := fmt.Sprint(vcs.Name, r)
+ dir := filepath.Join(tempDir, "example.com", vcsName, rootName)
+ if j&1 == 0 {
+ err := os.MkdirAll(dir, 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ err := os.MkdirAll(filepath.Dir(dir), 0755)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f, err := os.Create(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
}
- } else {
- err := os.MkdirAll(filepath.Dir(dir), 0755)
- if err != nil {
- t.Fatal(err)
- }
- f, err := os.Create(dir)
- if err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
- want := RepoRoot{
- VCS: vcs,
- Root: path.Join("example.com", vcs.Name),
- }
- var got RepoRoot
- got.VCS, got.Root, err = FromDir(dir, tempDir)
- if err != nil {
- t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
- continue
- }
- if got.VCS.Name != want.VCS.Name || got.Root != want.Root {
- t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root)
+ wantRepoDir := filepath.Dir(dir)
+ gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
+ if err != nil {
+ t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
+ continue
+ }
+ if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name {
+ t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name)
+ }
}
}
}
diff --git a/src/cmd/go/internal/version/exe.go b/src/cmd/go/internal/version/exe.go
deleted file mode 100644
index 0e7deef149..0000000000
--- a/src/cmd/go/internal/version/exe.go
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2019 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 version
-
-import (
- "bytes"
- "debug/elf"
- "debug/macho"
- "debug/pe"
- "fmt"
- "internal/xcoff"
- "io"
- "os"
-)
-
-// An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF).
-type exe interface {
- // Close closes the underlying file.
- Close() error
-
- // ReadData reads and returns up to size byte starting at virtual address addr.
- ReadData(addr, size uint64) ([]byte, error)
-
- // DataStart returns the writable data segment start address.
- DataStart() uint64
-}
-
-// openExe opens file and returns it as an exe.
-func openExe(file string) (exe, error) {
- f, err := os.Open(file)
- if err != nil {
- return nil, err
- }
- data := make([]byte, 16)
- if _, err := io.ReadFull(f, data); err != nil {
- return nil, err
- }
- f.Seek(0, 0)
- if bytes.HasPrefix(data, []byte("\x7FELF")) {
- e, err := elf.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &elfExe{f, e}, nil
- }
- if bytes.HasPrefix(data, []byte("MZ")) {
- e, err := pe.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &peExe{f, e}, nil
- }
- if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) {
- e, err := macho.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &machoExe{f, e}, nil
- }
- if bytes.HasPrefix(data, []byte{0x01, 0xDF}) || bytes.HasPrefix(data, []byte{0x01, 0xF7}) {
- e, err := xcoff.NewFile(f)
- if err != nil {
- f.Close()
- return nil, err
- }
- return &xcoffExe{f, e}, nil
-
- }
- return nil, fmt.Errorf("unrecognized executable format")
-}
-
-// elfExe is the ELF implementation of the exe interface.
-type elfExe struct {
- os *os.File
- f *elf.File
-}
-
-func (x *elfExe) Close() error {
- return x.os.Close()
-}
-
-func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
- for _, prog := range x.f.Progs {
- if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 {
- n := prog.Vaddr + prog.Filesz - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *elfExe) DataStart() uint64 {
- for _, s := range x.f.Sections {
- if s.Name == ".go.buildinfo" {
- return s.Addr
- }
- }
- for _, p := range x.f.Progs {
- if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
- return p.Vaddr
- }
- }
- return 0
-}
-
-// peExe is the PE (Windows Portable Executable) implementation of the exe interface.
-type peExe struct {
- os *os.File
- f *pe.File
-}
-
-func (x *peExe) Close() error {
- return x.os.Close()
-}
-
-func (x *peExe) imageBase() uint64 {
- switch oh := x.f.OptionalHeader.(type) {
- case *pe.OptionalHeader32:
- return uint64(oh.ImageBase)
- case *pe.OptionalHeader64:
- return oh.ImageBase
- }
- return 0
-}
-
-func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
- addr -= x.imageBase()
- for _, sect := range x.f.Sections {
- if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
- n := uint64(sect.VirtualAddress+sect.Size) - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *peExe) DataStart() uint64 {
- // Assume data is first writable section.
- const (
- IMAGE_SCN_CNT_CODE = 0x00000020
- IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
- IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
- IMAGE_SCN_MEM_EXECUTE = 0x20000000
- IMAGE_SCN_MEM_READ = 0x40000000
- IMAGE_SCN_MEM_WRITE = 0x80000000
- IMAGE_SCN_MEM_DISCARDABLE = 0x2000000
- IMAGE_SCN_LNK_NRELOC_OVFL = 0x1000000
- IMAGE_SCN_ALIGN_32BYTES = 0x600000
- )
- for _, sect := range x.f.Sections {
- if sect.VirtualAddress != 0 && sect.Size != 0 &&
- sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
- return uint64(sect.VirtualAddress) + x.imageBase()
- }
- }
- return 0
-}
-
-// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
-type machoExe struct {
- os *os.File
- f *macho.File
-}
-
-func (x *machoExe) Close() error {
- return x.os.Close()
-}
-
-func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
- for _, load := range x.f.Loads {
- seg, ok := load.(*macho.Segment)
- if !ok {
- continue
- }
- if seg.Addr <= addr && addr <= seg.Addr+seg.Filesz-1 {
- if seg.Name == "__PAGEZERO" {
- continue
- }
- n := seg.Addr + seg.Filesz - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := seg.ReadAt(data, int64(addr-seg.Addr))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *machoExe) DataStart() uint64 {
- // Look for section named "__go_buildinfo".
- for _, sec := range x.f.Sections {
- if sec.Name == "__go_buildinfo" {
- return sec.Addr
- }
- }
- // Try the first non-empty writable segment.
- const RW = 3
- for _, load := range x.f.Loads {
- seg, ok := load.(*macho.Segment)
- if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
- return seg.Addr
- }
- }
- return 0
-}
-
-// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
-type xcoffExe struct {
- os *os.File
- f *xcoff.File
-}
-
-func (x *xcoffExe) Close() error {
- return x.os.Close()
-}
-
-func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
- for _, sect := range x.f.Sections {
- if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
- n := uint64(sect.VirtualAddress+sect.Size) - addr
- if n > size {
- n = size
- }
- data := make([]byte, n)
- _, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- }
- return nil, fmt.Errorf("address not mapped")
-}
-
-func (x *xcoffExe) DataStart() uint64 {
- return x.f.SectionByType(xcoff.STYP_DATA).VirtualAddress
-}
diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go
index e885933ac3..febc7c638a 100644
--- a/src/cmd/go/internal/version/version.go
+++ b/src/cmd/go/internal/version/version.go
@@ -8,7 +8,8 @@ package version
import (
"bytes"
"context"
- "encoding/binary"
+ "debug/buildinfo"
+ "errors"
"fmt"
"io/fs"
"os"
@@ -141,90 +142,25 @@ func scanFile(file string, info fs.FileInfo, mustPrint bool) {
return
}
- x, err := openExe(file)
+ bi, err := buildinfo.ReadFile(file)
if err != nil {
if mustPrint {
- fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
+ if pathErr := (*os.PathError)(nil); errors.As(err, &pathErr) && filepath.Clean(pathErr.Path) == filepath.Clean(file) {
+ fmt.Fprintf(os.Stderr, "%v\n", file)
+ } else {
+ fmt.Fprintf(os.Stderr, "%s: %v\n", file, err)
+ }
}
- return
- }
- defer x.Close()
-
- vers, mod := findVers(x)
- if vers == "" {
- if mustPrint {
- fmt.Fprintf(os.Stderr, "%s: go version not found\n", file)
- }
- return
}
- fmt.Printf("%s: %s\n", file, vers)
- if *versionM && mod != "" {
- fmt.Printf("\t%s\n", strings.ReplaceAll(mod[:len(mod)-1], "\n", "\n\t"))
- }
-}
-
-// The build info blob left by the linker is identified by
-// a 16-byte header, consisting of buildInfoMagic (14 bytes),
-// the binary's pointer size (1 byte),
-// and whether the binary is big endian (1 byte).
-var buildInfoMagic = []byte("\xff Go buildinf:")
-
-// findVers finds and returns the Go version and module version information
-// in the executable x.
-func findVers(x exe) (vers, mod string) {
- // Read the first 64kB of text to find the build info blob.
- text := x.DataStart()
- data, err := x.ReadData(text, 64*1024)
+ fmt.Printf("%s: %s\n", file, bi.GoVersion)
+ bi.GoVersion = "" // suppress printing go version again
+ mod, err := bi.MarshalText()
if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: formatting build info: %v\n", file, err)
return
}
- for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
- if len(data) < 32 {
- return
- }
+ if *versionM && len(mod) > 0 {
+ fmt.Printf("\t%s\n", bytes.ReplaceAll(mod[:len(mod)-1], []byte("\n"), []byte("\n\t")))
}
-
- // Decode the blob.
- ptrSize := int(data[14])
- bigEndian := data[15] != 0
- var bo binary.ByteOrder
- if bigEndian {
- bo = binary.BigEndian
- } else {
- bo = binary.LittleEndian
- }
- var readPtr func([]byte) uint64
- if ptrSize == 4 {
- readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
- } else {
- readPtr = bo.Uint64
- }
- vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
- if vers == "" {
- return
- }
- mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
- if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
- // Strip module framing.
- mod = mod[16 : len(mod)-16]
- } else {
- mod = ""
- }
- return
-}
-
-// readString returns the string at address addr in the executable x.
-func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
- hdr, err := x.ReadData(addr, uint64(2*ptrSize))
- if err != nil || len(hdr) < 2*ptrSize {
- return ""
- }
- dataAddr := readPtr(hdr)
- dataLen := readPtr(hdr[ptrSize:])
- data, err := x.ReadData(dataAddr, dataLen)
- if err != nil || uint64(len(data)) < dataLen {
- return ""
- }
- return string(data)
}
diff --git a/src/cmd/go/internal/web/bootstrap.go b/src/cmd/go/internal/web/bootstrap.go
index 08686cdfcf..ab88e9e478 100644
--- a/src/cmd/go/internal/web/bootstrap.go
+++ b/src/cmd/go/internal/web/bootstrap.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build cmd_go_bootstrap
-// +build cmd_go_bootstrap
// This code is compiled only into the bootstrap 'go' binary.
// These stubs avoid importing packages with large dependency
diff --git a/src/cmd/go/internal/web/http.go b/src/cmd/go/internal/web/http.go
index 81f841d2c6..a92326db01 100644
--- a/src/cmd/go/internal/web/http.go
+++ b/src/cmd/go/internal/web/http.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !cmd_go_bootstrap
-// +build !cmd_go_bootstrap
// This code is compiled into the real 'go' binary, but it is not
// compiled into the binary that is built during all.bash, so as
diff --git a/src/cmd/go/internal/web/url_other.go b/src/cmd/go/internal/web/url_other.go
index 453af402b4..84bbd72820 100644
--- a/src/cmd/go/internal/web/url_other.go
+++ b/src/cmd/go/internal/web/url_other.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !windows
-// +build !windows
package web
diff --git a/src/cmd/go/internal/web/url_other_test.go b/src/cmd/go/internal/web/url_other_test.go
index 4d6ed2ec7f..5c197de800 100644
--- a/src/cmd/go/internal/web/url_other_test.go
+++ b/src/cmd/go/internal/web/url_other_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !windows
-// +build !windows
package web
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 55e4954eee..9d0ad27f0d 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -68,13 +68,16 @@ and test commands:
The default is GOMAXPROCS, normally the number of CPUs available.
-race
enable data race detection.
- Supported only on linux/amd64, freebsd/amd64, darwin/amd64, windows/amd64,
+ Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64,
linux/ppc64le and linux/arm64 (only for 48-bit VMA).
-msan
enable interoperation with memory sanitizer.
Supported only on linux/amd64, linux/arm64
and only with Clang/LLVM as the host C compiler.
On linux/arm64, pie build mode will be used.
+ -asan
+ enable interoperation with address sanitizer.
+ Supported only on linux/arm64, linux/amd64.
-v
print the names of packages as they are compiled.
-work
@@ -85,8 +88,19 @@ and test commands:
-asmflags '[pattern=]arg list'
arguments to pass on each go tool asm invocation.
+ -buildinfo
+ Whether to stamp binaries with build flags. By default, the compiler name
+ (gc or gccgo), toolchain flags (like -gcflags), and environment variables
+ containing flags (like CGO_CFLAGS) are stamped into binaries. Use
+ -buildinfo=false to omit build information. See also -buildvcs.
-buildmode mode
build mode to use. See 'go help buildmode' for more.
+ -buildvcs
+ Whether to stamp binaries with version control information. By default,
+ version control information is stamped into a binary if the main package
+ and the main module containing it are in the repository containing the
+ current directory (if there is a repository). Use -buildvcs=false to
+ omit version control information. See also -buildinfo.
-compiler name
name of compiler to use, as in runtime.Compiler (gccgo or gc).
-gccgoflags '[pattern=]arg list'
@@ -98,8 +112,8 @@ and test commands:
in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race
or, if set explicitly, has _race appended to it. Likewise for the -msan
- flag. Using a -buildmode option that requires non-default compile flags
- has a similar effect.
+ and -asan flags. Using a -buildmode option that requires non-default compile
+ flags has a similar effect.
-ldflags '[pattern=]arg list'
arguments to pass on each go tool link invocation.
-linkshared
@@ -298,10 +312,13 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "")
cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "")
cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "")
+ cmd.Flag.BoolVar(&cfg.BuildASan, "asan", false, "")
cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "")
cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
+ cmd.Flag.BoolVar(&cfg.BuildBuildinfo, "buildinfo", true, "")
+ cmd.Flag.BoolVar(&cfg.BuildBuildvcs, "buildvcs", true, "")
// Undocumented, unstable debugging flags.
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go
index 15f944d2af..d4f2a716d7 100644
--- a/src/cmd/go/internal/work/buildid.go
+++ b/src/cmd/go/internal/work/buildid.go
@@ -16,7 +16,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/internal/buildid"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
// Build IDs
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index d4e24d4cd1..03f8866cf2 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -34,8 +34,9 @@ import (
"cmd/go/internal/fsys"
"cmd/go/internal/load"
"cmd/go/internal/modload"
+ "cmd/go/internal/str"
"cmd/go/internal/trace"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"cmd/internal/sys"
)
@@ -2666,7 +2667,7 @@ func envList(key, def string) []string {
if v == "" {
v = def
}
- args, err := str.SplitQuotedFields(v)
+ args, err := quoted.Split(v)
if err != nil {
panic(fmt.Sprintf("could not parse environment variable %s with value %q: %v", key, v, err))
}
@@ -2736,6 +2737,10 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
}
+ if cfg.BuildASan {
+ cgoCFLAGS = append([]string{"-fsanitize=address"}, cgoCFLAGS...)
+ cgoLDFLAGS = append([]string{"-fsanitize=address"}, cgoLDFLAGS...)
+ }
// Allows including _cgo_export.h, as well as the user's .h files,
// from .[ch] files in the package.
@@ -2757,7 +2762,7 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
- if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") {
+ if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo" || p.ImportPath == "runtime/asan") {
cgoflags = append(cgoflags, "-import_syscall=false")
}
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 3eb9b35f40..e3b4a817e7 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -20,8 +20,9 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/load"
+ "cmd/go/internal/str"
"cmd/internal/objabi"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"cmd/internal/sys"
"crypto/sha1"
)
@@ -565,7 +566,7 @@ func setextld(ldflags []string, compiler []string) ([]string, error) {
return ldflags, nil
}
}
- joined, err := str.JoinAndQuoteFields(compiler)
+ joined, err := quoted.Join(compiler)
if err != nil {
return nil, err
}
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index 3cb7b64183..60181b99e4 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -17,7 +17,7 @@ import (
"cmd/go/internal/fsys"
"cmd/go/internal/load"
"cmd/internal/pkgpath"
- "cmd/internal/str"
+ "cmd/go/internal/str"
)
// The Gccgo toolchain.
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 6a29abb03b..dc368de1c1 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -11,7 +11,7 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/modload"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"cmd/internal/sys"
"fmt"
"os"
@@ -46,7 +46,7 @@ func BuildInit() {
// Make sure CC, CXX, and FC are absolute paths.
for _, key := range []string{"CC", "CXX", "FC"} {
value := cfg.Getenv(key)
- args, err := str.SplitQuotedFields(value)
+ args, err := quoted.Split(value)
if err != nil {
base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
}
@@ -67,27 +67,14 @@ func BuildInit() {
// instrumentation is added. 'go test -fuzz' still works without coverage,
// but it generates random inputs without guidance, so it's much less effective.
func fuzzInstrumentFlags() []string {
- // TODO: expand the set of supported platforms, with testing. Nothing about
- // the instrumentation is OS specific, but only amd64 and arm64 are
- // supported in the runtime. See src/runtime/libfuzzer*.
- //
- // Keep in sync with build constraints in
- // internal/fuzz/counters_{un,}supported.go
- switch cfg.Goos {
- case "darwin", "freebsd", "linux", "windows":
- default:
- return nil
- }
- switch cfg.Goarch {
- case "amd64", "arm64":
- default:
+ if !sys.FuzzInstrumented(cfg.Goos, cfg.Goarch) {
return nil
}
return []string{"-d=libfuzzer"}
}
func instrumentInit() {
- if !cfg.BuildRace && !cfg.BuildMSan {
+ if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan {
return
}
if cfg.BuildRace && cfg.BuildMSan {
@@ -95,17 +82,30 @@ func instrumentInit() {
base.SetExitStatus(2)
base.Exit()
}
+ if cfg.BuildRace && cfg.BuildASan {
+ fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n")
+ base.SetExitStatus(2)
+ base.Exit()
+ }
+ if cfg.BuildMSan && cfg.BuildASan {
+ fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n")
+ base.SetExitStatus(2)
+ base.Exit()
+ }
if cfg.BuildMSan && !sys.MSanSupported(cfg.Goos, cfg.Goarch) {
fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
base.SetExitStatus(2)
base.Exit()
}
- if cfg.BuildRace {
- if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
- fmt.Fprintf(os.Stderr, "go: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n")
- base.SetExitStatus(2)
- base.Exit()
- }
+ if cfg.BuildRace && !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
+ fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
+ base.SetExitStatus(2)
+ base.Exit()
+ }
+ if cfg.BuildASan && !sys.ASanSupported(cfg.Goos, cfg.Goarch) {
+ fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
+ base.SetExitStatus(2)
+ base.Exit()
}
mode := "race"
if cfg.BuildMSan {
@@ -116,6 +116,9 @@ func instrumentInit() {
cfg.BuildBuildmode = "pie"
}
}
+ if cfg.BuildASan {
+ mode = "asan"
+ }
modeFlag := "-" + mode
if !cfg.BuildContext.CgoEnabled {
diff --git a/src/cmd/go/internal/work/testgo.go b/src/cmd/go/internal/work/testgo.go
index 8b77871b23..a09b65a23c 100644
--- a/src/cmd/go/internal/work/testgo.go
+++ b/src/cmd/go/internal/work/testgo.go
@@ -5,7 +5,6 @@
// This file contains extra hooks for testing the go command.
//go:build testgo
-// +build testgo
package work
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 17782420c7..98c1b68ed9 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -353,8 +353,14 @@ Script:
ok = canCgo
case "msan":
ok = canMSan
+ case "asan":
+ ok = canASan
case "race":
ok = canRace
+ case "fuzz":
+ ok = canFuzz
+ case "fuzz-instrumented":
+ ok = fuzzInstrumented
case "net":
ok = testenv.HasExternalNetwork()
case "link":
@@ -1130,6 +1136,17 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args ..
done: done,
}
+ // Use the script's PATH to look up the command if it contains a separator
+ // instead of the test process's PATH (see lookPath).
+ // Don't use filepath.Clean, since that changes "./foo" to "foo".
+ command = filepath.FromSlash(command)
+ if !strings.Contains(command, string(filepath.Separator)) {
+ var err error
+ command, err = ts.lookPath(command)
+ if err != nil {
+ return nil, err
+ }
+ }
cmd := exec.Command(command, args...)
cmd.Dir = ts.cd
cmd.Env = append(ts.env, "PWD="+ts.cd)
@@ -1146,6 +1163,73 @@ func (ts *testScript) startBackground(want simpleStatus, command string, args ..
return bg, nil
}
+// lookPath is (roughly) like exec.LookPath, but it uses the test script's PATH
+// instead of the test process's PATH to find the executable. We don't change
+// the test process's PATH since it may run scripts in parallel.
+func (ts *testScript) lookPath(command string) (string, error) {
+ var strEqual func(string, string) bool
+ if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
+ // Using GOOS as a proxy for case-insensitive file system.
+ strEqual = strings.EqualFold
+ } else {
+ strEqual = func(a, b string) bool { return a == b }
+ }
+
+ var pathExt []string
+ var searchExt bool
+ var isExecutable func(os.FileInfo) bool
+ if runtime.GOOS == "windows" {
+ // Use the test process's PathExt instead of the script's.
+ // If PathExt is set in the command's environment, cmd.Start fails with
+ // "parameter is invalid". Not sure why.
+ // If the command already has an extension in PathExt (like "cmd.exe")
+ // don't search for other extensions (not "cmd.bat.exe").
+ pathExt = strings.Split(os.Getenv("PathExt"), string(filepath.ListSeparator))
+ searchExt = true
+ cmdExt := filepath.Ext(command)
+ for _, ext := range pathExt {
+ if strEqual(cmdExt, ext) {
+ searchExt = false
+ break
+ }
+ }
+ isExecutable = func(fi os.FileInfo) bool {
+ return fi.Mode().IsRegular()
+ }
+ } else {
+ isExecutable = func(fi os.FileInfo) bool {
+ return fi.Mode().IsRegular() && fi.Mode().Perm()&0111 != 0
+ }
+ }
+
+ pathName := "PATH"
+ if runtime.GOOS == "plan9" {
+ pathName = "path"
+ }
+
+ for _, dir := range strings.Split(ts.envMap[pathName], string(filepath.ListSeparator)) {
+ if searchExt {
+ ents, err := os.ReadDir(dir)
+ if err != nil {
+ continue
+ }
+ for _, ent := range ents {
+ for _, ext := range pathExt {
+ if !ent.IsDir() && strEqual(ent.Name(), command+ext) {
+ return dir + string(filepath.Separator) + ent.Name(), nil
+ }
+ }
+ }
+ } else {
+ path := dir + string(filepath.Separator) + command
+ if fi, err := os.Stat(path); err == nil && isExecutable(fi) {
+ return path, nil
+ }
+ }
+ }
+ return "", &exec.Error{Name: command, Err: exec.ErrNotFound}
+}
+
// waitOrStop waits for the already-started command cmd by calling its Wait method.
//
// If cmd does not return before ctx is done, waitOrStop sends it the given interrupt signal.
diff --git a/src/cmd/go/stop_other_test.go b/src/cmd/go/stop_other_test.go
index e1cc6cf8ba..35c12858c1 100644
--- a/src/cmd/go/stop_other_test.go
+++ b/src/cmd/go/stop_other_test.go
@@ -3,15 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !(aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris)
-// +build !aix
-// +build !darwin
-// +build !dragonfly
-// +build !freebsd
-// +build !js !wasm
-// +build !linux
-// +build !netbsd
-// +build !openbsd
-// +build !solaris
package main_test
diff --git a/src/cmd/go/stop_unix_test.go b/src/cmd/go/stop_unix_test.go
index ac35b240f0..5939f0d40d 100644
--- a/src/cmd/go/stop_unix_test.go
+++ b/src/cmd/go/stop_unix_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris
-// +build aix darwin dragonfly freebsd js,wasm linux netbsd openbsd solaris
package main_test
diff --git a/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt
new file mode 100644
index 0000000000..af005ffb41
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.1.0.txt
@@ -0,0 +1,20 @@
+-- .mod --
+module example.com/fuzzfail
+
+go 1.18
+-- .info --
+{"Version":"v0.1.0"}
+-- go.mod --
+module example.com/fuzzfail
+
+go 1.18
+-- fuzzfail_test.go --
+package fuzzfail
+
+import "testing"
+
+func FuzzFail(f *testing.F) {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ t.Fatalf("oops: %q", b)
+ })
+}
diff --git a/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt
new file mode 100644
index 0000000000..ea599aa611
--- /dev/null
+++ b/src/cmd/go/testdata/mod/example.com_fuzzfail_v0.2.0.txt
@@ -0,0 +1,23 @@
+-- .mod --
+module example.com/fuzzfail
+
+go 1.18
+-- .info --
+{"Version":"v0.2.0"}
+-- go.mod --
+module example.com/fuzzfail
+
+go 1.18
+-- fuzzfail_test.go --
+package fuzzfail
+
+import "testing"
+
+func FuzzFail(f *testing.F) {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ t.Fatalf("oops: %q", b)
+ })
+}
+-- testdata/fuzz/FuzzFail/bbb0c2d22aa1a24617301566dc7486f8b625d38024603ba62757c1124013b49a --
+go test fuzz v1
+[]byte("\x05")
diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README
index 48e4055b0b..2b55fa8977 100644
--- a/src/cmd/go/testdata/script/README
+++ b/src/cmd/go/testdata/script/README
@@ -79,7 +79,9 @@ should only run when the condition is satisfied. The available conditions are:
- Compiler names, like [gccgo], [gc].
- Test environment details:
- [short] for testing.Short()
- - [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used
+ - [cgo], [msan], [asan], [race] for whether cgo, msan, asan, and the race detector can be used
+ - [fuzz] for whether 'go test -fuzz' can be used at all
+ - [fuzz-instrumented] for whether 'go test -fuzz' uses coverage-instrumented binaries
- [net] for whether the external network can be used
- [link] for testenv.HasLink()
- [root] for os.Geteuid() == 0
diff --git a/src/cmd/go/testdata/script/gcflags_patterns.txt b/src/cmd/go/testdata/script/gcflags_patterns.txt
index e9521c2fb2..5b81e9481a 100644
--- a/src/cmd/go/testdata/script/gcflags_patterns.txt
+++ b/src/cmd/go/testdata/script/gcflags_patterns.txt
@@ -7,24 +7,24 @@ env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache
# -gcflags=-e applies to named packages, not dependencies
go build -n -v -gcflags=-e z1 z2
-stderr 'compile.* -p z1.* -e'
-stderr 'compile.* -p z2.* -e'
+stderr 'compile.* -p z1.* -e '
+stderr 'compile.* -p z2.* -e '
stderr 'compile.* -p y'
-! stderr 'compile.* -p [^z].* -e'
+! stderr 'compile.* -p [^z].* -e '
# -gcflags can specify package=flags, and can be repeated; last match wins
go build -n -v -gcflags=-e -gcflags=z1=-N z1 z2
-stderr 'compile.* -p z1.* -N'
-! stderr 'compile.* -p z1.* -e'
-! stderr 'compile.* -p z2.* -N'
-stderr 'compile.* -p z2.* -e'
+stderr 'compile.* -p z1.* -N '
+! stderr 'compile.* -p z1.* -e '
+! stderr 'compile.* -p z2.* -N '
+stderr 'compile.* -p z2.* -e '
stderr 'compile.* -p y'
-! stderr 'compile.* -p [^z].* -e'
-! stderr 'compile.* -p [^z].* -N'
+! stderr 'compile.* -p [^z].* -e '
+! stderr 'compile.* -p [^z].* -N '
# -gcflags can have arbitrary spaces around the flags
go build -n -v -gcflags=' z1 = -e ' z1
-stderr 'compile.* -p z1.* -e'
+stderr 'compile.* -p z1.* -e '
# -gcflags='all=-e' should apply to all packages, even with go test
go test -c -n -gcflags='all=-e' z1
diff --git a/src/cmd/go/testdata/script/goflags.txt b/src/cmd/go/testdata/script/goflags.txt
index 686d1138b8..f4872ffd35 100644
--- a/src/cmd/go/testdata/script/goflags.txt
+++ b/src/cmd/go/testdata/script/goflags.txt
@@ -9,7 +9,7 @@ stdout '[\\/]runtime$'
env GOFLAGS=-race OLDGOARCH=$GOARCH OLDGOOS=$GOOS GOARCH=386 GOOS=linux
! go list runtime
-stderr 'race is only supported on'
+stderr 'race is not supported on linux/386'
env GOARCH=$OLDGOARCH GOOS=$OLDGOOS
diff --git a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt b/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
index 7985cd2ab2..5e88f7b8db 100644
--- a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
+++ b/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
@@ -1,7 +1,5 @@
# Tests Issue #21895
-[!msan] [!race] skip 'skipping because both msan and the race detector are not supported'
-
env CGO_ENABLED=0
[race] ! go install -race triv.go
@@ -12,6 +10,10 @@ env CGO_ENABLED=0
[msan] stderr '-msan requires cgo'
[msan] ! stderr '-race'
+[asan] ! go install -asan triv.go
+[asan] stderr '-asan requires cgo'
+[asan] ! stderr '-msan'
+
-- triv.go --
package main
diff --git a/src/cmd/go/testdata/script/list_all_gobuild.txt b/src/cmd/go/testdata/script/list_all_gobuild.txt
new file mode 100644
index 0000000000..e0a47398bb
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_all_gobuild.txt
@@ -0,0 +1,41 @@
+# go list all should work with GOOS=linux because all packages build on Linux
+env GOOS=linux
+go list all
+
+# go list all should work with GOOS=darwin, but it used to fail because
+# in the absence of //go:build support, p looked like it needed q
+# (p_test.go was not properly excluded), and q was Linux-only.
+#
+# Also testing with r and s that +build lines keep working.
+env GOOS=darwin
+go list all
+
+-- go.mod --
+go 1.17
+module m
+
+-- p/p.go --
+package p
+
+-- p/p_test.go --
+//go:build linux
+
+package p
+
+import "m/q"
+
+-- q/q_linux.go --
+package q
+
+-- r/r.go --
+package r
+
+-- r/r_test.go --
+// +build linux
+
+package r
+
+import "m/s"
+
+-- s/s_linux.go --
+package s
diff --git a/src/cmd/go/testdata/script/list_reserved.txt b/src/cmd/go/testdata/script/list_reserved.txt
new file mode 100644
index 0000000000..b9c5361492
--- /dev/null
+++ b/src/cmd/go/testdata/script/list_reserved.txt
@@ -0,0 +1,7 @@
+# https://golang.org/issue/37641: the paths "example" and "test" are reserved
+# for end users, and must never exist in the standard library.
+
+go list example/... test/...
+stderr 'go: warning: "example/..." matched no packages$'
+stderr 'go: warning: "test/..." matched no packages$'
+! stdout .
diff --git a/src/cmd/go/testdata/script/list_test_err.txt b/src/cmd/go/testdata/script/list_test_err.txt
index c6f1ecf400..25dbb969b0 100644
--- a/src/cmd/go/testdata/script/list_test_err.txt
+++ b/src/cmd/go/testdata/script/list_test_err.txt
@@ -44,6 +44,10 @@ stdout 'testdep_b '
stdout 'nameerr\.test "[^"]*wrong signature for TestBad'
! stderr 'wrong signature for TestBad'
+# go list prints a useful error for generic test functions
+! go list -test -deps genericerr
+stderr 'wrong signature for TestGeneric, test functions cannot have type parameters'
+
# go list prints partial information with error if test has cyclic import
! go list -test -deps cycleerr
stdout cycleerr
@@ -106,6 +110,16 @@ import (
func TestBad(t *testing.B) {}
+-- genericerr/genericerr.go --
+package genericerr
+
+-- genericerr/genericerr_test.go --
+package genericerr
+
+import "testing"
+
+func TestGeneric[T any](t *testing.T) {}
+
-- cycleerr/cycleerr_test.go --
package cycleerr
diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt
index 89e58a2cfd..154e68338b 100644
--- a/src/cmd/go/testdata/script/mod_download.txt
+++ b/src/cmd/go/testdata/script/mod_download.txt
@@ -128,6 +128,50 @@ rm go.sum
go mod download all
cmp go.mod.update go.mod
grep '^rsc.io/sampler v1.3.0 ' go.sum
+
+# https://golang.org/issue/44435: At go 1.17 or higher, 'go mod download'
+# (without arguments) should only download the modules explicitly required in
+# the go.mod file, not (presumed-irrelevant) transitive dependencies.
+#
+# (If the go.mod file is inconsistent, the version downloaded should be the
+# selected version from the broader graph, but the go.mod file will also be
+# updated to list the correct versions. If at some point we change 'go mod
+# download' to stop updating for consistency, then it should fail if the
+# requirements are inconsistent.)
+
+rm go.sum
+cp go.mod.orig go.mod
+go mod edit -go=1.17
+cp go.mod.update go.mod.go117
+go mod edit -go=1.17 go.mod.go117
+
+go clean -modcache
+go mod download
+cmp go.mod go.mod.go117
+
+go list -e -m all
+stdout '^rsc.io/quote v1.5.2$'
+exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip
+stdout '^rsc.io/sampler v1.3.0$'
+! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip
+exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip
+stdout '^golang\.org/x/text v0.0.0-20170915032832-14c0d48ead0c$'
+! exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip
+cmp go.mod go.mod.go117
+
+# However, 'go mod download all' continues to download the selected version
+# of every module reported by 'go list -m all'.
+
+cp go.mod.orig go.mod
+go mod edit -go=1.17
+go clean -modcache
+go mod download all
+exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.zip
+! exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.2.1.zip
+exists $GOPATH/pkg/mod/cache/download/rsc.io/sampler/@v/v1.3.0.zip
+exists $GOPATH/pkg/mod/cache/download/golang.org/x/text/@v/v0.0.0-20170915032832-14c0d48ead0c.zip
+cmp go.mod go.mod.go117
+
cd ..
# allow go mod download without go.mod
diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt
index f9317b676d..428b8aa60e 100644
--- a/src/cmd/go/testdata/script/mod_invalid_version.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_version.txt
@@ -57,20 +57,20 @@ cp go.mod.orig go.mod
go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0
cd outside
! go list -m golang.org/x/text
-stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)'
cd ..
! go list -m golang.org/x/text
-stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(14c0d48ead0c\)'
+stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0: invalid pseudo-version: revision is shorter than canonical \(expected 14c0d48ead0c\)'
# A pseudo-version with more than 12 digits of SHA-1 prefix is invalid.
cp go.mod.orig go.mod
go mod edit -require golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a
cd outside
! go list -m golang.org/x/text
-stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)'
+stderr 'go: example.com@v0.0.0 requires\n\tgolang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)'
cd ..
! go list -m golang.org/x/text
-stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(14c0d48ead0c\)'
+stderr 'golang.org/x/text@v0.1.1-0.20170915032832-14c0d48ead0cd47e3104ada247d91be04afc7a5a: invalid pseudo-version: revision is longer than canonical \(expected 14c0d48ead0c\)'
# A pseudo-version that does not match the commit timestamp is invalid.
cp go.mod.orig go.mod
diff --git a/src/cmd/go/testdata/script/mod_vendor_goversion.txt b/src/cmd/go/testdata/script/mod_vendor_goversion.txt
index 7f1966c8e8..a92eb73d27 100644
--- a/src/cmd/go/testdata/script/mod_vendor_goversion.txt
+++ b/src/cmd/go/testdata/script/mod_vendor_goversion.txt
@@ -3,7 +3,6 @@
[short] skip
-
# Control case: without a vendor directory, need117 builds and bad114 doesn't.
go build example.net/need117
@@ -26,7 +25,8 @@ go mod vendor
! grep 1.17 vendor/modules.txt
! go build example.net/need117
-stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:1[89]: .*conversion of slices to array pointers only supported as of -lang=go1\.17'
+stderr '^vendor[/\\]example\.net[/\\]need117[/\\]need117.go:5:1[89]:'
+stderr 'conversion of slices to array pointers only supported as of -lang=go1\.17'
! grep 1.13 vendor/modules.txt
go build example.net/bad114
diff --git a/src/cmd/go/testdata/script/test_fail_newline.txt b/src/cmd/go/testdata/script/test_fail_newline.txt
new file mode 100644
index 0000000000..43cee565a1
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_fail_newline.txt
@@ -0,0 +1,65 @@
+[short] skip
+
+# In package list mode, output is buffered.
+# Check that a newline is printed after the buffer's contents.
+cd fail
+! go test .
+! stderr .
+stdout '^exitcode=1\n'
+stdout '^FAIL\s+example/fail'
+
+# In local directory mode output is streamed, so we don't know
+# whether the test printed anything at all, so we print the exit code
+# (just in case it failed without emitting any output at all),
+# and that happens to add the needed newline as well.
+! go test
+! stderr .
+stdout '^exitcode=1exit status 1\n'
+stdout '^FAIL\s+example/fail'
+
+# In package list mode, if the test passes the 'ok' message appears
+# on its own line.
+cd ../skip
+go test -v .
+! stderr .
+stdout '^skipping\n'
+stdout '^ok\s+example/skip'
+
+# If the output is streamed and the test passes, we can't tell whether it ended
+# in a partial line, and don't want to emit any extra output in the
+# overwhelmingly common case that it did not.
+# (In theory we could hook the 'os' package to report whether output
+# was emitted and whether it ended in a newline, but that seems too invasive.)
+go test
+! stderr .
+stdout '^skippingok\s+example/skip'
+
+
+-- go.mod --
+module example
+
+go 1.18
+-- fail/fail_test.go --
+package fail
+
+import (
+ "os"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ os.Stderr.WriteString("exitcode=1")
+ os.Exit(1)
+}
+-- skip/skip_test.go --
+package skip
+
+import (
+ "os"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ os.Stderr.WriteString("skipping")
+ os.Exit(0)
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz.txt b/src/cmd/go/testdata/script/test_fuzz.txt
index c9930aa37e..150491be04 100644
--- a/src/cmd/go/testdata/script/test_fuzz.txt
+++ b/src/cmd/go/testdata/script/test_fuzz.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# Test that running a fuzz target that returns without failing or calling
# f.Fuzz fails and causes a non-zero exit status.
@@ -70,6 +69,12 @@ stdout 'f.Fuzz function'
stdout FAIL
stdout 'f.Fuzz function'
+# Test that f.Fail within f.Fuzz panics
+! go test fail_fuzz_fn_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'f.Fuzz function'
+
# Test that f.Skip within f.Fuzz panics
! go test skip_fuzz_fn_fuzz_test.go
! stdout ^ok
@@ -77,6 +82,14 @@ stdout 'f.Fuzz function'
stdout FAIL
stdout 'f.Fuzz function'
+# Test that f.Skipped within f.Fuzz panics
+! go test skipped_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'f.Skipped is'
+stdout FAIL
+stdout 'f.Fuzz function'
+stdout 't.Skipped is false'
+
# Test that runtime.Goexit within the fuzz function is an error.
! go test goexit_fuzz_fn_fuzz_test.go
! stdout ^ok
@@ -139,6 +152,7 @@ stdout FAIL
# Test that the wrong type given with f.Add will fail.
! go test -run FuzzWrongType fuzz_add_test.go
! stdout ^ok
+stdout '\[string int\], want \[\[\]uint8 int8\]'
stdout FAIL
# Test fatal with testdata seed corpus
@@ -260,6 +274,18 @@ func Fuzz(f *testing.F) {
})
}
+-- fail_fuzz_fn_fuzz_test.go --
+package skip_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+ f.Add([]byte("aa"))
+ f.Fuzz(func(t *testing.T, b []byte) {
+ f.Fail()
+ })
+}
+
-- skip_fuzz_fn_fuzz_test.go --
package skip_fuzz_fn_fuzz
@@ -272,6 +298,19 @@ func Fuzz(f *testing.F) {
})
}
+-- skipped_fuzz_fn_fuzz_test.go --
+package skipped_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+ f.Add([]byte("aa"))
+ f.Fuzz(func(t *testing.T, b []byte) {
+ t.Logf("t.Skipped is %t\n", t.Skipped())
+ t.Logf("f.Skipped is %t\n", f.Skipped())
+ })
+}
+
-- goexit_fuzz_fn_fuzz_test.go --
package goexit_fuzz_fn_fuzz
@@ -397,8 +436,8 @@ func FuzzAddDifferentType(f *testing.F) {
}
func FuzzWrongType(f *testing.F) {
- f.Add("hello")
- f.Fuzz(func(*testing.T, []byte) {})
+ f.Add("hello", 50)
+ f.Fuzz(func(*testing.T, []byte, int8) {})
}
-- corpustesting/fuzz_testdata_corpus_test.go --
@@ -456,4 +495,4 @@ go test fuzz v1
[]byte("12345")
-- corpustesting/testdata/fuzz/FuzzWrongType/1 --
go test fuzz v1
-int("00000")
\ No newline at end of file
+int("00000")
diff --git a/src/cmd/go/testdata/script/test_fuzz_cache.txt b/src/cmd/go/testdata/script/test_fuzz_cache.txt
index fc1c9a1752..552966b06b 100644
--- a/src/cmd/go/testdata/script/test_fuzz_cache.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_cache.txt
@@ -1,9 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
-# Instrumentation not supported on other archs.
-# See #14565.
-[!amd64] [!arm64] skip
+[!fuzz-instrumented] skip
[short] skip
env GOCACHE=$WORK/cache
diff --git a/src/cmd/go/testdata/script/test_fuzz_chatty.txt b/src/cmd/go/testdata/script/test_fuzz_chatty.txt
index 9ebd480c90..1abcbbd389 100644
--- a/src/cmd/go/testdata/script/test_fuzz_chatty.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_chatty.txt
@@ -1,6 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# Run chatty fuzz targets with an error.
diff --git a/src/cmd/go/testdata/script/test_fuzz_cleanup.txt b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt
index 88625916ba..b65022bd74 100644
--- a/src/cmd/go/testdata/script/test_fuzz_cleanup.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
[short] skip
# Cleanup should run after F.Skip.
diff --git a/src/cmd/go/testdata/script/test_fuzz_deadline.txt b/src/cmd/go/testdata/script/test_fuzz_deadline.txt
index 12f1054f61..5ba76a3d4f 100644
--- a/src/cmd/go/testdata/script/test_fuzz_deadline.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_deadline.txt
@@ -1,6 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# The fuzz function should be able to detect whether -timeout
diff --git a/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt
index 9c9972f9e9..56d94a4bcf 100644
--- a/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt
@@ -1,17 +1,15 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# There are no seed values, so 'go test' should finish quickly.
go test
# Fuzzing should exit 0 after fuzztime, even if timeout is short.
-go test -timeout=10ms -fuzz=FuzzFast -fuzztime=5s
+go test -timeout=3s -fuzz=FuzzFast -fuzztime=5s
# We should see the same behavior when invoking the test binary directly.
go test -c
-exec ./fuzz.test$GOEXE -test.timeout=10ms -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache
+exec ./fuzz.test$GOEXE -test.timeout=3s -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache
# Timeout should not cause inputs to be written as crashers.
! exists testdata/fuzz
diff --git a/src/cmd/go/testdata/script/test_fuzz_io_error.txt b/src/cmd/go/testdata/script/test_fuzz_io_error.txt
index 4c7ab4c152..1a0aa6427e 100644
--- a/src/cmd/go/testdata/script/test_fuzz_io_error.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_io_error.txt
@@ -6,7 +6,7 @@
# This is unlikely, but possible. It's difficult to simulate interruptions
# due to ^C and EOF errors which are more common. We don't report those.
[short] skip
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# If the I/O error occurs before F.Fuzz is called, the coordinator should
# stop the worker and say that.
diff --git a/src/cmd/go/testdata/script/test_fuzz_match.txt b/src/cmd/go/testdata/script/test_fuzz_match.txt
index 3a2ca631ad..0c0085f2c2 100644
--- a/src/cmd/go/testdata/script/test_fuzz_match.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_match.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# Matches only fuzz targets to test.
go test standalone_fuzz_test.go
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize.txt b/src/cmd/go/testdata/script/test_fuzz_minimize.txt
index 0a0359fabb..462fb9a963 100644
--- a/src/cmd/go/testdata/script/test_fuzz_minimize.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_minimize.txt
@@ -1,62 +1,77 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# We clean the fuzz cache during this test. Don't clean the user's cache.
env GOCACHE=$WORK/gocache
# Test that fuzzminimizetime cannot be negative seconds
-! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms minimizer_test.go
+! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1ms .
! stdout '^ok'
! stdout 'contains a non-zero byte'
stdout 'invalid duration'
stdout FAIL
# Test that fuzzminimizetime cannot be negative times
-! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x minimizer_test.go
+! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x -fuzzminimizetime=-1x .
! stdout '^ok'
! stdout 'contains a non-zero byte'
stdout 'invalid count'
stdout FAIL
# Test that fuzzminimizetime can be zero seconds, and minimization is disabled
-! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s minimizer_test.go
+! go test -fuzz=FuzzMinimizeZeroDurationSet -run=FuzzMinimizeZeroDurationSet -fuzztime=10000x -fuzzminimizetime=0s .
! stdout '^ok'
! stdout 'minimizing'
stdout 'there was an Error'
stdout FAIL
# Test that fuzzminimizetime can be zero times, and minimization is disabled
-! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x minimizer_test.go
+! go test -fuzz=FuzzMinimizeZeroLimitSet -run=FuzzMinimizeZeroLimitSet -fuzztime=10000x -fuzzminimizetime=0x .
! stdout '^ok'
! stdout 'minimizing'
-stdout 'there was an Error'
+stdout -count=1 'there was an Error'
stdout FAIL
# Test that minimization is working for recoverable errors.
-! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x minimizer_test.go
+! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x .
! stdout '^ok'
stdout 'got the minimum size!'
-stdout 'contains a non-zero byte'
+# The error message that was printed should be for the one written to testdata.
+stdout 'contains a non-zero byte of length 50'
stdout FAIL
# Check that the bytes written to testdata are of length 50 (the minimum size)
-go run check_testdata.go FuzzMinimizerRecoverable 50
+go run ./check_testdata FuzzMinimizerRecoverable 50
# Test that re-running the minimized value causes a crash.
-! go test -run=FuzzMinimizerRecoverable minimizer_test.go
+! go test -run=FuzzMinimizerRecoverable .
+rm testdata
+
+# Test that minimization is working for recoverable errors. Run it with -v this
+# time to ensure the command line output still looks right.
+! go test -v -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=10000x .
+! stdout '^ok'
+stdout 'got the minimum size!'
+# The error message that was printed should be for the one written to testdata.
+stdout 'contains a non-zero byte of length 50'
+stdout FAIL
+
+# Check that the bytes written to testdata are of length 50 (the minimum size)
+go run ./check_testdata FuzzMinimizerRecoverable 50
+
+# Test that re-running the minimized value causes a crash.
+! go test -run=FuzzMinimizerRecoverable .
rm testdata
# Test that minimization doesn't run for non-recoverable errors.
-! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x minimizer_test.go
+! go test -fuzz=FuzzMinimizerNonrecoverable -run=FuzzMinimizerNonrecoverable -fuzztime=10000x .
! stdout '^ok'
! stdout 'minimizing'
-stdout 'fuzzing process terminated unexpectedly: exit status 99'
+stdout -count=1 'fuzzing process terminated unexpectedly: exit status 99'
stdout FAIL
# Check that re-running the value causes a crash.
-! go test -run=FuzzMinimizerNonrecoverable minimizer_test.go
+! go test -run=FuzzMinimizerNonrecoverable .
rm testdata
# Clear the fuzzing cache. There may already be minimized inputs that would
@@ -65,35 +80,28 @@ go clean -fuzzcache
# Test that minimization can be cancelled by fuzzminimizetime and the latest
# crash will still be logged and written to testdata.
-! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x minimizer_test.go
+! go test -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -fuzztime=100x -fuzzminimizetime=1x .
! stdout '^ok'
stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerRecoverable[/\\]'
! stdout 'got the minimum size!' # it shouldn't have had enough time to minimize it
stdout FAIL
# Test that re-running the unminimized value causes a crash.
-! go test -run=FuzzMinimizerRecoverable minimizer_test.go
-
-! go test -fuzz=FuzzMinimizerTooSlow -run=FuzzMinimizerTooSlow -fuzzminimizetime=3s minimizer_test.go
-stdout 'fuzz: minimizing'
-stdout 'fuzz: elapsed: \d+s, minimizing'
-stdout 'testdata[/\\]fuzz[/\\]FuzzMinimizerTooSlow[/\\]'
-stdout FAIL
+! go test -run=FuzzMinimizerRecoverable .
# TODO(jayconrod,katiehockman): add a test which verifies that the right bytes
# are written to testdata in the case of an interrupt during minimization.
-- go.mod --
-module m
+module example.com/y
go 1.16
--- minimizer_test.go --
-package fuzz_test
+-- y_test.go --
+package y
import (
"os"
"testing"
- "time"
)
func FuzzMinimizeZeroDurationSet(f *testing.F) {
@@ -126,42 +134,20 @@ func FuzzMinimizerRecoverable(f *testing.F) {
if len(b) == 50 {
t.Log("got the minimum size!")
}
- t.Fatal("contains a non-zero byte")
+ t.Fatalf("contains a non-zero byte of length %d", len(b))
}
}
})
}
func FuzzMinimizerNonrecoverable(f *testing.F) {
- f.Add(make([]byte, 100))
f.Fuzz(func(t *testing.T, b []byte) {
- if len(b) < 50 {
- // Make sure that b is large enough that it can be minimized
- return
- }
- // Given the randomness of the mutations, this should allow the
- // minimizer to trim down the value a bit.
- for _, n := range b {
- if n != 0 {
- t.Log("contains a non-zero byte")
- os.Exit(99)
- }
- }
+ os.Exit(99)
})
}
-
-func FuzzMinimizerTooSlow(f *testing.F) {
- f.Fuzz(func(t *testing.T, b []byte) {
- if len(b) > 50 {
- t.Error("error here")
- time.Sleep(2 * time.Second)
- }
- })
-}
-
--- check_testdata.go --
-// +build ignore
-
+-- empty/empty.go --
+package empty
+-- check_testdata/check_testdata.go --
package main
import (
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
index 5e1d90d8d9..e017a4cad3 100644
--- a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
@@ -1,8 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
-# Instrumentation only supported on 64-bit architectures.
-[!amd64] [!arm64] skip
+[!fuzz-instrumented] skip
# Test that when an interesting value is discovered (one that expands coverage),
# the fuzzing engine minimizes it before writing it to the cache.
@@ -18,22 +14,72 @@
go test -c -fuzz=. # Build using shared build cache for speed.
env GOCACHE=$WORK/gocache
-exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=. -test.fuzztime=1000x
-go run check_cache.go $GOCACHE/fuzz/FuzzMin
+exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
+go run check_cache.go $GOCACHE/fuzz/FuzzMinCache
+
+# Test that minimization occurs for a crash that appears while minimizing a
+# newly found interesting input. There must be only one worker for this test to
+# be flaky like we want.
+go test -c -fuzz=. # Build using shared build cache for speed.
+env GOCACHE=$WORK/gocache
+! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.fuzztime=10000x -test.parallel=1
+! stdout '^ok'
+stdout 'got the minimum size!'
+stdout -count=1 'flaky failure'
+stdout FAIL
+
+# Make sure the crash that was written will fail when run with go test
+! go test -run=FuzzMinimizerCrashInMinimization .
-- go.mod --
module fuzz
go 1.17
+-- y.go --
+package fuzz
+
+import (
+ "bytes"
+ "io"
+)
+
+func Y(w io.Writer, b []byte) {
+ if !bytes.Equal(b, []byte("y")) {
+ w.Write([]byte("not equal"))
+ }
+}
-- fuzz_test.go --
package fuzz
import (
"bytes"
+ "io"
"testing"
)
-func FuzzMin(f *testing.F) {
+func FuzzMinimizerCrashInMinimization(f *testing.F) {
+ seed := make([]byte, 1000)
+ f.Add(seed)
+ f.Fuzz(func(t *testing.T, b []byte) {
+ if len(b) < 50 || len(b) > 1100 {
+ // Make sure that b is large enough that it can be minimized
+ return
+ }
+ if !bytes.Equal(b, seed) {
+ // This should have hit a new edge, and the interesting input
+ // should be attempting minimization
+ Y(io.Discard, b)
+ }
+ if len(b) < 350 {
+ t.Error("flaky failure")
+ }
+ if len(b) == 50 {
+ t.Log("got the minimum size!")
+ }
+ })
+}
+
+func FuzzMinCache(f *testing.F) {
seed := bytes.Repeat([]byte("a"), 20)
f.Add(seed)
f.Fuzz(func(t *testing.T, buf []byte) {
diff --git a/src/cmd/go/testdata/script/test_fuzz_modcache.txt b/src/cmd/go/testdata/script/test_fuzz_modcache.txt
new file mode 100644
index 0000000000..c0f18ea3c0
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_fuzz_modcache.txt
@@ -0,0 +1,58 @@
+# This test demonstrates the fuzz corpus behavior for packages outside of the main module.
+# (See https://golang.org/issue/48495.)
+
+[short] skip
+
+# Set -modcacherw so that the test behaves the same regardless of whether the
+# module cache is writable. (For example, on some platforms it can always be
+# written if the user is running as root.) At one point, a failing fuzz test
+# in a writable module cache would corrupt module checksums in the cache.
+env GOFLAGS=-modcacherw
+
+
+# When the upstream module has no test corpus, running 'go test' should succeed,
+# but 'go test -fuzz=.' should error out before running the test.
+# (It should NOT corrupt the module cache by writing out new fuzz inputs,
+# even if the cache is writable.)
+
+go get -t example.com/fuzzfail@v0.1.0
+go test example.com/fuzzfail
+
+! go test -fuzz=. example.com/fuzzfail
+! stdout .
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+go mod verify
+
+
+# If the module does include a test corpus, 'go test' (without '-fuzz') should
+# load that corpus and run the fuzz tests against it, but 'go test -fuzz=.'
+# should continue to be rejected.
+
+go get -t example.com/fuzzfail@v0.2.0
+
+! go test example.com/fuzzfail
+stdout '^\s*fuzzfail_test\.go:7: oops:'
+
+! go test -fuzz=. example.com/fuzzfail
+! stdout .
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+go mod verify
+
+
+# Packages in 'std' cannot be fuzzed when the corresponding GOROOT module is not
+# the main module — either the failures would not be recorded or the behavior of
+# the 'std' tests would change globally.
+
+! go test -fuzz . encoding/json
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+! go test -fuzz . cmd/buildid
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+
+-- go.mod --
+module example.com/m
+
+go 1.18
diff --git a/src/cmd/go/testdata/script/test_fuzz_multiple.txt b/src/cmd/go/testdata/script/test_fuzz_multiple.txt
index 6a7732f514..d96b2b6206 100644
--- a/src/cmd/go/testdata/script/test_fuzz_multiple.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_multiple.txt
@@ -2,9 +2,7 @@
# enabled, and multiple package or multiple fuzz targets match.
# TODO(#46312): support fuzzing multiple targets in multiple packages.
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# With fuzzing disabled, multiple targets can be tested.
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt
index 628e003f41..4c4fa8e651 100644
--- a/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# Tests that a crash caused by a mutator-discovered input writes the bad input
# to testdata, and fails+reports correctly. This tests the end-to-end behavior
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt
index 935c22a05e..b5eab17349 100644
--- a/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_mutate_fail.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# Check that if a worker does not call F.Fuzz or calls F.Fail first,
# 'go test' exits non-zero and no crasher is recorded.
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator.txt b/src/cmd/go/testdata/script/test_fuzz_mutator.txt
index 9d0738e169..76b86488ad 100644
--- a/src/cmd/go/testdata/script/test_fuzz_mutator.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_mutator.txt
@@ -1,5 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
+[!fuzz] skip
# Test basic fuzzing mutator behavior.
#
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt b/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt
index 0924ed37e6..15d7cb6b32 100644
--- a/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_mutator_repeat.txt
@@ -7,11 +7,11 @@
[short] skip
-# Start fuzzing. The worker crashes after ~100 iterations.
+# Start fuzzing. The worker crashes after 100 iterations.
# The fuzz function writes the crashing input to "want" before exiting.
# The fuzzing engine reconstructs the crashing input and saves it to testdata.
! exists want
-! go test -fuzz=. -parallel=1
+! go test -fuzz=. -parallel=1 -fuzztime=110x -fuzzminimizetime=10x -v
stdout 'fuzzing process terminated unexpectedly'
stdout 'Crash written to testdata'
@@ -44,12 +44,20 @@ func FuzzRepeat(f *testing.F) {
if i == 100 {
f, err := os.OpenFile("want", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
- // Couldn't create the file, probably because it already exists,
- // and we're minimizing now. Return without crashing.
- return
+ // Couldn't create the file. Return without crashing, and try
+ // again.
+ i--
+ t.Skip(err)
+ }
+ if _, err := f.Write(b); err != nil {
+ // We already created the file, so if we failed to write it
+ // there's not much we can do. The test will fail anyway, but
+ // at least make sure the error is logged to stdout.
+ t.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatal(err)
}
- f.Write(b)
- f.Close()
os.Exit(1) // crash without communicating
}
diff --git a/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt b/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt
index 1568757de7..31d54bcb70 100644
--- a/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_non_crash_signal.txt
@@ -1,7 +1,7 @@
# NOTE: this test is skipped on Windows, since there's no concept of signals.
# When a process terminates another process, it provides an exit code.
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!freebsd] [!linux] skip
+[windows] skip
+[!fuzz] skip
[short] skip
# FuzzNonCrash sends itself a signal that does not appear to be a crash.
@@ -10,6 +10,15 @@
! exists testdata
! stdout unreachable
! stderr unreachable
+stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: terminated'
+
+# FuzzKill sends itself a signal that cannot be caught by the worker process
+# and does not appear to be a crash.
+# We should not save a crasher.
+! go test -fuzz=FuzzKill
+! exists testdata
+! stdout unreachable
+! stderr unreachable
stdout 'fuzzing process terminated by unexpected signal; no crash will be recorded: signal: killed'
# FuzzCrash sends itself a signal that looks like a crash.
@@ -33,6 +42,17 @@ import (
)
func FuzzNonCrash(f *testing.F) {
+ f.Fuzz(func(*testing.T, bool) {
+ pid := syscall.Getpid()
+ if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
+ panic(err)
+ }
+ // signal may not be received immediately. Wait for it.
+ select{}
+ })
+}
+
+func FuzzKill(f *testing.F) {
f.Fuzz(func(*testing.T, bool) {
pid := syscall.Getpid()
if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
diff --git a/src/cmd/go/testdata/script/test_fuzz_parallel.txt b/src/cmd/go/testdata/script/test_fuzz_parallel.txt
index a49f30a27f..1795e0b2a5 100644
--- a/src/cmd/go/testdata/script/test_fuzz_parallel.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_parallel.txt
@@ -1,6 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
# When running seed inputs, T.Parallel should let multiple inputs run in
diff --git a/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt b/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt
new file mode 100644
index 0000000000..5434c723ad
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_fuzz_profile_flags.txt
@@ -0,0 +1,38 @@
+[!fuzz] skip
+
+! go test -fuzz=FuzzTrivial -coverprofile=prof
+! stdout .
+stderr '^cannot use -coverprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -blockprofile=prof
+! stdout .
+stderr '^cannot use -blockprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -cpuprofile=prof
+! stdout .
+stderr '^cannot use -cpuprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -memprofile=prof
+! stdout .
+stderr '^cannot use -memprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -mutexprofile=prof
+! stdout .
+stderr '^cannot use -mutexprofile flag with -fuzz flag$'
+
+! go test -fuzz=FuzzTrivial -trace=prof
+! stdout .
+stderr '^cannot use -trace flag with -fuzz flag$'
+
+-- go.mod --
+module example
+
+go 1.18
+-- fuzz_test.go --
+package example
+
+import "testing"
+
+func FuzzTrivial(f *testing.F) {
+ f.Fuzz(func(t *testing.T, _ []byte) {})
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_run.txt b/src/cmd/go/testdata/script/test_fuzz_run.txt
index e546d997cb..99a4413d32 100644
--- a/src/cmd/go/testdata/script/test_fuzz_run.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_run.txt
@@ -1,6 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
+[!fuzz] skip
[short] skip
env GOCACHE=$WORK/cache
@@ -142,4 +140,4 @@ go test fuzz v1
string("fails")
-- testdata/fuzz/FuzzFoo/thispasses --
go test fuzz v1
-string("passes")
\ No newline at end of file
+string("passes")
diff --git a/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt b/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt
index 5d04d8c022..4be9a6e385 100644
--- a/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_seed_corpus.txt
@@ -1,10 +1,4 @@
-# TODO(jayconrod): support shared memory on more platforms.
-[!darwin] [!linux] [!windows] skip
-
-# Instrumentation not supported on other archs.
-# See #14565.
-[!amd64] [!arm64] skip
-
+[!fuzz-instrumented] skip
[short] skip
env GOCACHE=$WORK/cache
@@ -47,6 +41,23 @@ cp cache-file $GOCACHE/fuzz/example.com/x/FuzzWithCache/1
stdout 'Crash written to testdata[/\\]fuzz[/\\]FuzzWithCache[/\\]'
stdout FAIL
+# Write a crashing input to the cache
+mkdir $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache
+cp cache-file-bytes $GOCACHE/fuzz/example.com/x/FuzzWithMinimizableCache/1
+
+# Test that fuzzing a target with a failure in the cache minimizes it and writes
+# the new crash to testdata/fuzz
+! go test -fuzz=FuzzWithMinimizableCache -run=FuzzWithMinimizableCache -fuzztime=10000x
+! stdout ^ok
+stdout 'gathering baseline coverage'
+stdout 'got the minimum size!'
+stdout 'contains a non-zero byte of length 10'
+stdout 'Crash written to testdata[/\\]fuzz[/\\]FuzzWithMinimizableCache[/\\]'
+stdout FAIL
+# Make sure this crash didn't come from fuzzing
+# (the log line that states fuzzing began shouldn't have printed)
+! stdout 'execs'
+
# Clear the fuzz cache and make sure it's gone
go clean -fuzzcache
! exists $GOCACHE/fuzz
@@ -158,6 +169,22 @@ func FuzzWithCache(f *testing.F) {
})
}
+func FuzzWithMinimizableCache(f *testing.F) {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ if len(b) < 10 {
+ return
+ }
+ for _, n := range b {
+ if n != 0 {
+ if len(b) == 10 {
+ t.Log("got the minimum size!")
+ }
+ t.Fatalf("contains a non-zero byte of length %d", len(b))
+ }
+ }
+ })
+}
+
func FuzzRunNoneWithCache(f *testing.F) {
f.Fuzz(func(t *testing.T, i int) {
if i == 10 {
@@ -170,4 +197,7 @@ go test fuzz v1
int(10)
-- cache-file --
go test fuzz v1
-int(10)
\ No newline at end of file
+int(10)
+-- cache-file-bytes --
+go test fuzz v1
+[]byte("11111111111111111111")
diff --git a/src/cmd/go/testdata/script/test_fuzz_setenv.txt b/src/cmd/go/testdata/script/test_fuzz_setenv.txt
index 9738697a91..2924569de1 100644
--- a/src/cmd/go/testdata/script/test_fuzz_setenv.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_setenv.txt
@@ -1,5 +1,5 @@
+[!fuzz] skip
[short] skip
-[!darwin] [!linux] [!windows] skip
go test -fuzz=FuzzA -fuzztime=100x fuzz_setenv_test.go
diff --git a/src/cmd/go/testdata/script/test_fuzz_unsupported.txt b/src/cmd/go/testdata/script/test_fuzz_unsupported.txt
new file mode 100644
index 0000000000..1ed0b8a6f7
--- /dev/null
+++ b/src/cmd/go/testdata/script/test_fuzz_unsupported.txt
@@ -0,0 +1,18 @@
+[fuzz] skip
+
+! go test -fuzz=. -fuzztime=1x
+! stdout .
+stderr '^-fuzz flag is not supported on '$GOOS'/'$GOARCH'$'
+
+-- go.mod --
+module example
+
+go 1.18
+-- fuzz_test.go --
+package example
+
+import "testing"
+
+func FuzzTrivial(f *testing.F) {
+ f.Fuzz(func(t *testing.T, _ []byte) {})
+}
diff --git a/src/cmd/go/testdata/script/version_build_settings.txt b/src/cmd/go/testdata/script/version_build_settings.txt
new file mode 100644
index 0000000000..1ced285ac3
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_build_settings.txt
@@ -0,0 +1,66 @@
+[short] skip
+
+# Compiler name is always added.
+go build
+go version -m m$GOEXE
+stdout '^\tbuild\tcompiler\tgc$'
+! stdout asmflags|gcflags|ldflags|gccgoflags
+
+# Toolchain flags are added if present.
+# The raw flags are included, with package patterns if specified.
+go build -asmflags=example.com/m=-D=FOO=bar
+go version -m m$GOEXE
+stdout '^\tbuild\tasmflags\texample\.com/m=-D=FOO=bar$'
+
+go build -gcflags=example.com/m=-N
+go version -m m$GOEXE
+stdout '^\tbuild\tgcflags\texample\.com/m=-N$'
+
+go build -ldflags=example.com/m=-w
+go version -m m$GOEXE
+stdout '^\tbuild\tldflags\texample\.com/m=-w$'
+
+# gccgoflags are not added when gc is used, and vice versa.
+# TODO: test gccgo.
+go build -gccgoflags=all=UNUSED
+go version -m m$GOEXE
+! stdout gccgoflags
+
+# Build and tool tags are added but not release tags.
+# "race" is included with build tags but not "cgo".
+go build -tags=a,b
+go version -m m$GOEXE
+stdout '^\tbuild\ttags\ta,b(,goexperiment\.[a-z0-9]+)*$'
+[race] go build -race
+[race] go version -m m$GOEXE
+[race] stdout '^\tbuild\ttags\t.*race.*$'
+
+# CGO flags are separate settings.
+# CGO_ENABLED is always present.
+# Other flags are added if CGO_ENABLED is true.
+env CGO_ENABLED=0
+go build
+go version -m m$GOEXE
+stdout '^\tbuild\tCGO_ENABLED\tfalse$'
+! stdout CGO_CPPFLAGS|CGO_CFLAGS|CGO_CXXFLAGS|CGO_LDFLAGS
+[cgo] env CGO_ENABLED=1
+[cgo] env CGO_CPPFLAGS=-DFROM_CPPFLAGS=1
+[cgo] env CGO_CFLAGS=-DFROM_CFLAGS=1
+[cgo] env CGO_CXXFLAGS=-DFROM_CXXFLAGS=1
+[cgo] env CGO_LDFLAGS=-L/extra/dir/does/not/exist
+[cgo] go build
+[cgo] go version -m m$GOEXE
+[cgo] stdout '^\tbuild\tCGO_ENABLED\ttrue$'
+[cgo] stdout '^\tbuild\tCGO_CPPFLAGS\t-DFROM_CPPFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CFLAGS\t-DFROM_CFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_CXXFLAGS\t-DFROM_CXXFLAGS=1$'
+[cgo] stdout '^\tbuild\tCGO_LDFLAGS\t-L/extra/dir/does/not/exist$'
+
+-- go.mod --
+module example.com/m
+
+go 1.18
+-- m.go --
+package main
+
+func main() {}
diff --git a/src/cmd/go/testdata/script/version_buildvcs_bzr.txt b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt
new file mode 100644
index 0000000000..83069713d7
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt
@@ -0,0 +1,104 @@
+# This test checks that VCS information is stamped into Go binaries by default,
+# controlled with -buildvcs. This test focuses on Bazaar specifics.
+# The Git test covers common functionality.
+
+[!exec:bzr] skip
+[short] skip
+env GOBIN=$WORK/gopath/bin
+env oldpath=$PATH
+env HOME=$WORK
+cd repo/a
+exec bzr whoami 'J.R. Gopher '
+
+# If there's no local repository, there's no VCS info.
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout bzrrevision
+rm $GOBIN/a$GOEXE
+
+# If there is a repository, but it can't be used for some reason,
+# there should be an error. It should hint about -buildvcs=false.
+cd ..
+mkdir .bzr
+env PATH=$WORK${/}fakebin${:}$oldpath
+chmod 0755 $WORK/fakebin/bzr
+! exec bzr help
+cd a
+! go install
+stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$'
+rm $GOBIN/a$GOEXE
+cd ..
+env PATH=$oldpath
+rm .bzr
+
+# If there is an empty repository in a parent directory, only "uncommitted" is tagged.
+exec bzr init
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout bzrrevision
+! stdout bzrcommittime
+stdout '^\tbuild\tbzruncommitted\ttrue$'
+cd ..
+
+# Revision and commit time are tagged for repositories with commits.
+exec bzr add a README
+exec bzr commit -m 'initial commit'
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tbzrrevision\t'
+stdout '^\tbuild\tbzrcommittime\t'
+stdout '^\tbuild\tbzruncommitted\tfalse$'
+rm $GOBIN/a$GOEXE
+
+# Building an earlier commit should still build clean.
+cp ../../outside/empty.txt ../NEWS
+exec bzr add ../NEWS
+exec bzr commit -m 'add NEWS'
+exec bzr update -r1
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tbzrrevision\t'
+stdout '^\tbuild\tbzrcommittime\t'
+stdout '^\tbuild\tbzruncommitted\tfalse$'
+
+# Building with -buildvcs=false suppresses the info.
+go install -buildvcs=false
+go version -m $GOBIN/a$GOEXE
+! stdout bzrrevision
+rm $GOBIN/a$GOEXE
+
+# An untracked file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt .
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tbzruncommitted\ttrue$'
+rm empty.txt
+rm $GOBIN/a$GOEXE
+
+# An edited file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt ../README
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tbzruncommitted\ttrue$'
+exec bzr revert ../README
+rm $GOBIN/a$GOEXE
+
+-- $WORK/fakebin/bzr --
+#!/bin/sh
+exit 1
+-- $WORK/fakebin/bzr.bat --
+exit 1
+-- repo/README --
+Far out in the uncharted backwaters of the unfashionable end of the western
+spiral arm of the Galaxy lies a small, unregarded yellow sun.
+-- repo/a/go.mod --
+module example.com/a
+
+go 1.18
+-- repo/a/a.go --
+package main
+
+func main() {}
+-- outside/empty.txt --
diff --git a/src/cmd/go/testdata/script/version_buildvcs_fossil.txt b/src/cmd/go/testdata/script/version_buildvcs_fossil.txt
new file mode 100644
index 0000000000..3a4bde883f
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_buildvcs_fossil.txt
@@ -0,0 +1,90 @@
+# This test checks that VCS information is stamped into Go binaries by default,
+# controlled with -buildvcs. This test focuses on Fossil specifics.
+# The Git test covers common functionality.
+
+# "fossil" is the Fossil file server on Plan 9.
+[plan9] skip
+[!exec:fossil] skip
+[short] skip
+env GOBIN=$WORK/gopath/bin
+env oldpath=$PATH
+env HOME=$WORK
+env USER=gopher
+[!windows] env fslckout=.fslckout
+[windows] env fslckout=_FOSSIL_
+exec pwd
+exec fossil init repo.fossil
+cd repo/a
+
+# If there's no local repository, there's no VCS info.
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout fossilrevision
+rm $GOBIN/a$GOEXE
+
+# If there is a repository, but it can't be used for some reason,
+# there should be an error. It should hint about -buildvcs=false.
+cd ..
+mkdir $fslckout
+env PATH=$WORK${/}fakebin${:}$oldpath
+chmod 0755 $WORK/fakebin/fossil
+! exec fossil help
+cd a
+! go install
+stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$'
+rm $GOBIN/a$GOEXE
+cd ..
+env PATH=$oldpath
+rm $fslckout
+
+# Revision and commit time are tagged for repositories with commits.
+exec fossil open ../repo.fossil -f
+exec fossil add a README
+exec fossil commit -m 'initial commit'
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tfossilrevision\t'
+stdout '^\tbuild\tfossilcommittime\t'
+stdout '^\tbuild\tfossiluncommitted\tfalse$'
+rm $GOBIN/a$GOEXE
+
+# Building with -buildvcs=false suppresses the info.
+go install -buildvcs=false
+go version -m $GOBIN/a$GOEXE
+! stdout fossilrevision
+rm $GOBIN/a$GOEXE
+
+# An untracked file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt .
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tfossiluncommitted\ttrue$'
+rm empty.txt
+rm $GOBIN/a$GOEXE
+
+# An edited file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt ../README
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tfossiluncommitted\ttrue$'
+exec fossil revert ../README
+rm $GOBIN/a$GOEXE
+
+-- $WORK/fakebin/fossil --
+#!/bin/sh
+exit 1
+-- $WORK/fakebin/fossil.bat --
+exit 1
+-- repo/README --
+Far out in the uncharted backwaters of the unfashionable end of the western
+spiral arm of the Galaxy lies a small, unregarded yellow sun.
+-- repo/a/go.mod --
+module example.com/a
+
+go 1.18
+-- repo/a/a.go --
+package main
+
+func main() {}
+-- outside/empty.txt --
diff --git a/src/cmd/go/testdata/script/version_buildvcs_git.txt b/src/cmd/go/testdata/script/version_buildvcs_git.txt
new file mode 100644
index 0000000000..3d56c6d8b4
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_buildvcs_git.txt
@@ -0,0 +1,144 @@
+# This test checks that VCS information is stamped into Go binaries by default,
+# controlled with -buildvcs. This test focuses on Git. Other tests focus on
+# other VCS tools but may not cover common functionality.
+
+[!exec:git] skip
+[short] skip
+env GOBIN=$WORK/gopath/bin
+env oldpath=$PATH
+cd repo/a
+
+# If there's no local repository, there's no VCS info.
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout gitrevision
+rm $GOBIN/a$GOEXE
+
+# If there is a repository, but it can't be used for some reason,
+# there should be an error. It should hint about -buildvcs=false.
+cd ..
+mkdir .git
+env PATH=$WORK${/}fakebin${:}$oldpath
+chmod 0755 $WORK/fakebin/git
+! exec git help
+cd a
+! go install
+stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$'
+cd ..
+env PATH=$oldpath
+rm .git
+
+# If there is an empty repository in a parent directory, only "uncommitted" is tagged.
+exec git init
+exec git config user.email gopher@golang.org
+exec git config user.name 'J.R. Gopher'
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout gitrevision
+! stdout gitcommittime
+stdout '^\tbuild\tgituncommitted\ttrue$'
+rm $GOBIN/a$GOEXE
+
+# Revision and commit time are tagged for repositories with commits.
+exec git add -A
+exec git commit -m 'initial commit'
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tgitrevision\t'
+stdout '^\tbuild\tgitcommittime\t'
+stdout '^\tbuild\tgituncommitted\tfalse$'
+rm $GOBIN/a$GOEXE
+
+# Building with -buildvcs=false suppresses the info.
+go install -buildvcs=false
+go version -m $GOBIN/a$GOEXE
+! stdout gitrevision
+rm $GOBIN/a$GOEXE
+
+# An untracked file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt .
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tgituncommitted\ttrue$'
+rm empty.txt
+rm $GOBIN/a$GOEXE
+
+# An edited file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt ../README
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\tgituncommitted\ttrue$'
+exec git checkout ../README
+rm $GOBIN/a$GOEXE
+
+# If the build doesn't include any packages from the repository,
+# there should be no VCS info.
+go install example.com/cmd/a@v1.0.0
+go version -m $GOBIN/a$GOEXE
+! stdout gitrevision
+rm $GOBIN/a$GOEXE
+
+go mod edit -require=example.com/c@v0.0.0
+go mod edit -replace=example.com/c@v0.0.0=../../outside/c
+go install example.com/c
+go version -m $GOBIN/c$GOEXE
+! stdout gitrevision
+rm $GOBIN/c$GOEXE
+exec git checkout go.mod
+
+# If the build depends on a package in the repository, but it's not in the
+# main module, there should be no VCS info.
+go mod edit -require=example.com/b@v0.0.0
+go mod edit -replace=example.com/b@v0.0.0=../b
+go mod edit -require=example.com/d@v0.0.0
+go mod edit -replace=example.com/d@v0.0.0=../../outside/d
+go install example.com/d
+go version -m $GOBIN/d$GOEXE
+! stdout gitrevision
+exec git checkout go.mod
+rm $GOBIN/d$GOEXE
+
+-- $WORK/fakebin/git --
+#!/bin/sh
+exit 1
+-- $WORK/fakebin/git.bat --
+exit 1
+-- repo/README --
+Far out in the uncharted backwaters of the unfashionable end of the western
+spiral arm of the Galaxy lies a small, unregarded yellow sun.
+-- repo/a/go.mod --
+module example.com/a
+
+go 1.18
+-- repo/a/a.go --
+package main
+
+func main() {}
+-- repo/b/go.mod --
+module example.com/b
+
+go 1.18
+-- repo/b/b.go --
+package b
+-- outside/empty.txt --
+-- outside/c/go.mod --
+module example.com/c
+
+go 1.18
+-- outside/c/main.go --
+package main
+
+func main() {}
+-- outside/d/go.mod --
+module example.com/d
+
+go 1.18
+
+require example.com/b v0.0.0
+-- outside/d/main.go --
+package main
+
+import _ "example.com/b"
+
+func main() {}
diff --git a/src/cmd/go/testdata/script/version_buildvcs_hg.txt b/src/cmd/go/testdata/script/version_buildvcs_hg.txt
new file mode 100644
index 0000000000..df4938742d
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_buildvcs_hg.txt
@@ -0,0 +1,91 @@
+# This test checks that VCS information is stamped into Go binaries by default,
+# controlled with -buildvcs. This test focuses on Mercurial specifics.
+# The Git test covers common functionality.
+
+[!exec:hg] skip
+[short] skip
+env GOBIN=$WORK/gopath/bin
+env oldpath=$PATH
+cd repo/a
+
+# If there's no local repository, there's no VCS info.
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout hgrevision
+rm $GOBIN/a$GOEXE
+
+# If there is a repository, but it can't be used for some reason,
+# there should be an error. It should hint about -buildvcs=false.
+cd ..
+mkdir .hg
+env PATH=$WORK${/}fakebin${:}$oldpath
+chmod 0755 $WORK/fakebin/hg
+! exec hg help
+cd a
+! go install
+stderr '^error obtaining VCS status: exit status 1\n\tUse -buildvcs=false to disable VCS stamping.$'
+rm $GOBIN/a$GOEXE
+cd ..
+env PATH=$oldpath
+rm .hg
+
+# If there is an empty repository in a parent directory, only "uncommitted" is tagged.
+exec hg init
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout hgrevision
+! stdout hgcommittime
+stdout '^\tbuild\thguncommitted\ttrue$'
+cd ..
+
+# Revision and commit time are tagged for repositories with commits.
+exec hg add a README
+exec hg commit -m 'initial commit'
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\thgrevision\t'
+stdout '^\tbuild\thgcommittime\t'
+stdout '^\tbuild\thguncommitted\tfalse$'
+rm $GOBIN/a$GOEXE
+
+# Building with -buildvcs=false suppresses the info.
+go install -buildvcs=false
+go version -m $GOBIN/a$GOEXE
+! stdout hgrevision
+rm $GOBIN/a$GOEXE
+
+# An untracked file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt .
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\thguncommitted\ttrue$'
+rm empty.txt
+rm $GOBIN/a$GOEXE
+
+# An edited file is shown as uncommitted, even if it isn't part of the build.
+cp ../../outside/empty.txt ../README
+go install
+go version -m $GOBIN/a$GOEXE
+stdout '^\tbuild\thguncommitted\ttrue$'
+exec hg revert ../README
+rm $GOBIN/a$GOEXE
+
+-- $WORK/fakebin/hg --
+#!/bin/sh
+exit 1
+-- $WORK/fakebin/hg.bat --
+exit 1
+-- repo/README --
+Far out in the uncharted backwaters of the unfashionable end of the western
+spiral arm of the Galaxy lies a small, unregarded yellow sun.
+-- repo/a/go.mod --
+module example.com/a
+
+go 1.18
+-- repo/a/a.go --
+package main
+
+func main() {}
+-- outside/empty.txt --
diff --git a/src/cmd/go/testdata/script/version_buildvcs_nested.txt b/src/cmd/go/testdata/script/version_buildvcs_nested.txt
new file mode 100644
index 0000000000..08d4c92baf
--- /dev/null
+++ b/src/cmd/go/testdata/script/version_buildvcs_nested.txt
@@ -0,0 +1,51 @@
+[!exec:git] skip
+[!exec:hg] skip
+[short] skip
+env GOFLAGS=-n
+
+# Create a root module in a root Git repository.
+mkdir root
+cd root
+go mod init example.com/root
+exec git init
+
+# Nesting repositories in parent directories are ignored, as the current
+# directory main package, and containing main module are in the same repository.
+# This is an error in GOPATH mode (to prevent VCS injection), but for modules,
+# we assume users have control over repositories they've checked out.
+mkdir hgsub
+cd hgsub
+exec hg init
+cp ../../main.go main.go
+! go build
+stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*hgsub"$'
+stderr '^\tUse -buildvcs=false to disable VCS stamping.$'
+go build -buildvcs=false
+go mod init example.com/root/hgsub
+go build
+cd ..
+
+# It's an error to build a package from a nested Git repository if the package
+# is in a separate repository from the current directory or from the module
+# root directory.
+mkdir gitsub
+cd gitsub
+exec git init
+exec git config user.name 'J.R.Gopher'
+exec git config user.email 'gopher@golang.org'
+cp ../../main.go main.go
+! go build
+stderr '^error obtaining VCS status: main module is in repository ".*root" but current directory is in repository ".*gitsub"$'
+go build -buildvcs=false
+go mod init example.com/root/gitsub
+exec git commit --allow-empty -m empty # status commands fail without this
+go build
+rm go.mod
+cd ..
+! go build ./gitsub
+stderr '^error obtaining VCS status: main package is in repository ".*gitsub" but current directory is in repository ".*root"$'
+go build -buildvcs=false -o=gitsub${/} ./gitsub
+
+-- main.go --
+package main
+func main() {}
diff --git a/src/cmd/go/testdata/script/work.txt b/src/cmd/go/testdata/script/work.txt
index 657cd060cc..613f037615 100644
--- a/src/cmd/go/testdata/script/work.txt
+++ b/src/cmd/go/testdata/script/work.txt
@@ -1,8 +1,12 @@
! go mod initwork doesnotexist
stderr 'go: creating workspace file: no go.mod file exists in directory doesnotexist'
+go env GOWORK
+! stdout .
go mod initwork ./a ./b
cmp go.work go.work.want
+go env GOWORK
+stdout '^'$WORK'(\\|/)gopath(\\|/)src(\\|/)go.work$'
! go run example.com/b
stderr 'a(\\|/)a.go:4:8: no required module provides package rsc.io/quote; to add it:\n\tcd '$WORK(\\|/)gopath(\\|/)src(\\|/)a'\n\tgo get rsc.io/quote'
diff --git a/src/cmd/go/testdata/script/work_edit.txt b/src/cmd/go/testdata/script/work_edit.txt
index 001ac7f65c..979c1f98e0 100644
--- a/src/cmd/go/testdata/script/work_edit.txt
+++ b/src/cmd/go/testdata/script/work_edit.txt
@@ -30,7 +30,7 @@ cmp stdout go.work.want_print
go mod editwork -json -go 1.19 -directory b -dropdirectory c -replace 'x.1@v1.4.0 = ../z' -dropreplace x.1 -dropreplace x.1@v1.3.0
cmp stdout go.work.want_json
-go mod editwork -print -fmt -workfile unformatted
+go mod editwork -print -fmt -workfile $GOPATH/src/unformatted
cmp stdout formatted
-- m/go.mod --
diff --git a/src/cmd/go/testdata/script/work_env.txt b/src/cmd/go/testdata/script/work_env.txt
new file mode 100644
index 0000000000..de67255696
--- /dev/null
+++ b/src/cmd/go/testdata/script/work_env.txt
@@ -0,0 +1,24 @@
+go env GOWORK
+stdout '^'$GOPATH'[\\/]src[\\/]go.work$'
+go env
+stdout '^(set )?GOWORK="?'$GOPATH'[\\/]src[\\/]go.work"?$'
+
+cd ..
+go env GOWORK
+! stdout .
+go env
+stdout 'GOWORK=("")?'
+
+cd src
+go env GOWORK
+stdout 'go.work'
+
+! go env -w GOWORK=off
+stderr '^go: GOWORK cannot be modified$'
+
+-- go.work --
+go 1.18
+
+directory a
+-- a/go.mod --
+module example.com/a
diff --git a/src/cmd/go/testdata/script/work_replace.txt b/src/cmd/go/testdata/script/work_replace.txt
new file mode 100644
index 0000000000..5a4cb0eebb
--- /dev/null
+++ b/src/cmd/go/testdata/script/work_replace.txt
@@ -0,0 +1,55 @@
+# Support replace statement in go.work file
+
+# Replacement in go.work file, and none in go.mod file.
+go list -m example.com/dep
+stdout 'example.com/dep v1.0.0 => ./dep'
+
+# Wildcard replacement in go.work file overrides version replacement in go.mod
+# file.
+go list -m example.com/other
+stdout 'example.com/other v1.0.0 => ./other2'
+
+-- go.work --
+directory m
+
+replace example.com/dep => ./dep
+replace example.com/other => ./other2
+
+-- m/go.mod --
+module example.com/m
+
+require example.com/dep v1.0.0
+require example.com/other v1.0.0
+
+replace example.com/other v1.0.0 => ./other
+-- m/m.go --
+package m
+
+import "example.com/dep"
+import "example.com/other"
+
+func F() {
+ dep.G()
+ other.H()
+}
+-- dep/go.mod --
+module example.com/dep
+-- dep/dep.go --
+package dep
+
+func G() {
+}
+-- other/go.mod --
+module example.com/other
+-- other/dep.go --
+package other
+
+func G() {
+}
+-- other2/go.mod --
+module example.com/other
+-- other2/dep.go --
+package other
+
+func G() {
+}
\ No newline at end of file
diff --git a/src/cmd/go/testdata/script/work_replace_conflict.txt b/src/cmd/go/testdata/script/work_replace_conflict.txt
new file mode 100644
index 0000000000..a2f76d13a0
--- /dev/null
+++ b/src/cmd/go/testdata/script/work_replace_conflict.txt
@@ -0,0 +1,53 @@
+# Conflicting replaces in workspace modules returns error that suggests
+# overriding it in the go.work file.
+
+! go list -m example.com/dep
+stderr 'go: conflicting replacements for example.com/dep@v1.0.0:\n\t./dep1\n\t./dep2\nuse "go mod editwork -replace example.com/dep@v1.0.0=\[override\]" to resolve'
+go mod editwork -replace example.com/dep@v1.0.0=./dep1
+go list -m example.com/dep
+stdout 'example.com/dep v1.0.0 => ./dep1'
+
+-- foo --
+-- go.work --
+directory m
+directory n
+-- m/go.mod --
+module example.com/m
+
+require example.com/dep v1.0.0
+replace example.com/dep v1.0.0 => ./dep1
+-- m/m.go --
+package m
+
+import "example.com/dep"
+
+func F() {
+ dep.G()
+}
+-- n/go.mod --
+module example.com/n
+
+require example.com/dep v1.0.0
+replace example.com/dep v1.0.0 => ./dep2
+-- n/n.go --
+package n
+
+import "example.com/dep"
+
+func F() {
+ dep.G()
+}
+-- dep1/go.mod --
+module example.com/dep
+-- dep1/dep.go --
+package dep
+
+func G() {
+}
+-- dep2/go.mod --
+module example.com/dep
+-- dep2/dep.go --
+package dep
+
+func G() {
+}
diff --git a/src/cmd/go/testdata/script/work_replace_conflict_override.txt b/src/cmd/go/testdata/script/work_replace_conflict_override.txt
new file mode 100644
index 0000000000..ebb517dd7c
--- /dev/null
+++ b/src/cmd/go/testdata/script/work_replace_conflict_override.txt
@@ -0,0 +1,57 @@
+# Conflicting workspace module replaces can be overridden by a replace in the
+# go.work file.
+
+go list -m example.com/dep
+stdout 'example.com/dep v1.0.0 => ./dep3'
+
+-- go.work --
+directory m
+directory n
+replace example.com/dep => ./dep3
+-- m/go.mod --
+module example.com/m
+
+require example.com/dep v1.0.0
+replace example.com/dep => ./dep1
+-- m/m.go --
+package m
+
+import "example.com/dep"
+
+func F() {
+ dep.G()
+}
+-- n/go.mod --
+module example.com/n
+
+require example.com/dep v1.0.0
+replace example.com/dep => ./dep2
+-- n/n.go --
+package n
+
+import "example.com/dep"
+
+func F() {
+ dep.G()
+}
+-- dep1/go.mod --
+module example.com/dep
+-- dep1/dep.go --
+package dep
+
+func G() {
+}
+-- dep2/go.mod --
+module example.com/dep
+-- dep2/dep.go --
+package dep
+
+func G() {
+}
+-- dep3/go.mod --
+module example.com/dep
+-- dep3/dep.go --
+package dep
+
+func G() {
+}
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index 0e736e6132..a98c6a0cd9 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -292,7 +292,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value)
}
return v
- case reflect.Ptr:
+ case reflect.Pointer:
v := reflect.New(p.Type()).Elem()
if elem := p.Elem(); elem.IsValid() {
v.Set(subst(m, elem, pos).Addr())
diff --git a/src/cmd/gofmt/testdata/typeparams.golden b/src/cmd/gofmt/testdata/typeparams.golden
index f71bd130db..d57a2ba59b 100644
--- a/src/cmd/gofmt/testdata/typeparams.golden
+++ b/src/cmd/gofmt/testdata/typeparams.golden
@@ -21,7 +21,7 @@ func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
func f[P interface{}](x P)
func f[P1, P2, P3 interface {
m1(P1)
- type P2, P3
+ ~P2 | ~P3
}](x1 P1, x2 P2, x3 P3) struct{}
func f[P any](T1[P], T2[P]) T3[P]
diff --git a/src/cmd/gofmt/testdata/typeparams.input b/src/cmd/gofmt/testdata/typeparams.input
index 5d4c53d9f7..775cf9eb7b 100644
--- a/src/cmd/gofmt/testdata/typeparams.input
+++ b/src/cmd/gofmt/testdata/typeparams.input
@@ -19,7 +19,7 @@ func f[P any](x P)
func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
func f[P interface{}](x P)
-func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{}
+func f[P1, P2, P3 interface{ m1(P1); ~P2|~P3 }](x1 P1, x2 P2, x3 P3) struct{}
func f[P any](T1[P], T2[P]) T3[P]
func (x T[P]) m()
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index 2c44696f84..9765058392 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -19,21 +19,19 @@
package goobj
import (
- "bytes"
"cmd/internal/bio"
"crypto/sha1"
"encoding/binary"
"errors"
"fmt"
"internal/unsafeheader"
- "io"
"unsafe"
)
// New object file format.
//
// Header struct {
-// Magic [...]byte // "\x00go117ld"
+// Magic [...]byte // "\x00go118ld"
// Fingerprint [8]byte
// Flags uint32
// Offsets [...]uint32 // byte offset of each block below
@@ -217,7 +215,7 @@ type Header struct {
Offsets [NBlk]uint32
}
-const Magic = "\x00go117ld"
+const Magic = "\x00go118ld"
func (h *Header) Write(w *Writer) {
w.RawString(h.Magic)
@@ -594,13 +592,12 @@ type Reader struct {
b []byte // mmapped bytes, if not nil
readonly bool // whether b is backed with read-only memory
- rd io.ReaderAt
start uint32
h Header // keep block offsets
}
func NewReaderFromBytes(b []byte, readonly bool) *Reader {
- r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
+ r := &Reader{b: b, readonly: readonly, start: 0}
err := r.h.Read(r)
if err != nil {
return nil
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 1454d8a7c9..38aa11cde9 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -634,6 +634,61 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
+ if c.ctxt.Flag_maymorestack != "" {
+ // Save LR and make room for REGCTXT.
+ const frameSize = 8
+ // MOVW.W R14,$-8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVW
+ p.Scond |= C_WBIT
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = -frameSize
+ p.To.Reg = REGSP
+ p.Spadj = frameSize
+
+ // MOVW REGCTXT, 4(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVW
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 4
+ p.To.Reg = REGSP
+
+ // CALL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = obj.ACALL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+
+ // Restore REGCTXT and LR.
+
+ // MOVW 4(SP), REGCTXT
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVW
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 4
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // MOVW.P 8(SP), R14
+ p.As = AMOVW
+ p.Scond |= C_PBIT
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = frameSize
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+ p.Spadj = -frameSize
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
// MOVW g_stackguard(g), R1
p = obj.Appendp(p, c.newprog)
@@ -761,7 +816,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
b := obj.Appendp(pcdata, c.newprog)
b.As = obj.AJMP
b.To.Type = obj.TYPE_BRANCH
- b.To.SetTarget(c.cursym.Func().Text.Link)
+ b.To.SetTarget(startPred.Link)
b.Spadj = +framesize
return end
diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go
index efd4577f56..14f0f4c616 100644
--- a/src/cmd/internal/obj/arm64/doc.go
+++ b/src/cmd/internal/obj/arm64/doc.go
@@ -96,6 +96,19 @@ And for a 128-bit interger, it take two 64-bit operands, for the high and low pa
VMOVD $0x1122334455667788, V1
VMOVQ $0x1122334455667788, $8877665544332211, V2 // V2=0x11223344556677888877665544332211
+8. Move an optionally-shifted 16-bit immediate value to a register.
+
+The instructions are MOVK(W), MOVZ(W) and MOVN(W), the assembly syntax is "op $(uimm16<". The
+is the 16-bit unsigned immediate, in the range 0 to 65535; For the 32-bit variant, the is 0 or 16, for the
+64-bit variant, the is 0, 16, 32 or 48.
+
+The current Go assembler does not accept zero shifts, such as "op $0, Rd" and "op $(0<<(16|32|48)), Rd" instructions.
+
+ Examples:
+ MOVK $(10<<32), R20 <=> movk x20, #10, lsl #32
+ MOVZW $(20<<16), R8 <=> movz w8, #20, lsl #16
+ MOVK $(0<<16), R10 will be reported as an error by the assembler.
+
Special Cases.
(1) umov is written as VMOV.
diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go
index ae8deede3a..e9eb786cb2 100644
--- a/src/cmd/internal/obj/arm64/obj7.go
+++ b/src/cmd/internal/obj/arm64/obj7.go
@@ -58,6 +58,91 @@ var noZRreplace = map[obj.As]bool{
}
func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
+ if c.ctxt.Flag_maymorestack != "" {
+ p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
+
+ // Save LR and make room for FP, REGCTXT. Leave room
+ // for caller's saved FP.
+ const frameSize = 32
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ p.To.Type = obj.TYPE_MEM
+ p.Scond = C_XPRE
+ p.To.Offset = -frameSize
+ p.To.Reg = REGSP
+ p.Spadj = frameSize
+
+ // Save FP.
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGFP
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = REGSP
+ p.To.Offset = -8
+
+ p = obj.Appendp(p, c.newprog)
+ p.As = ASUB
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 8
+ p.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGFP
+
+ // Save REGCTXT (for simplicity we do this whether or
+ // not we need it.)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = REGSP
+ p.To.Offset = 8
+
+ // BL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = ABL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+
+ // Restore REGCTXT.
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = REGSP
+ p.From.Offset = 8
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // Restore FP.
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = REGSP
+ p.From.Offset = -8
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGFP
+
+ // Restore LR and SP.
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.Scond = C_XPOST
+ p.From.Offset = frameSize
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+ p.Spadj = -frameSize
+
+ p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
// MOV g_stackguard(g), RT1
p = obj.Appendp(p, c.newprog)
@@ -212,7 +297,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
jmp := obj.Appendp(pcdata, c.newprog)
jmp.As = AB
jmp.To.Type = obj.TYPE_BRANCH
- jmp.To.SetTarget(c.cursym.Func().Text.Link)
+ jmp.To.SetTarget(startPred.Link)
jmp.Spadj = +framesize
return end
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index abb37416cc..11af143f22 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -486,6 +486,7 @@ type FuncInfo struct {
StackObjects *LSym
OpenCodedDeferInfo *LSym
ArgInfo *LSym // argument info for traceback
+ ArgLiveInfo *LSym // argument liveness info for traceback
FuncInfoSym *LSym
}
@@ -879,7 +880,8 @@ type Link struct {
Flag_linkshared bool
Flag_optimize bool
Flag_locationlists bool
- Retpoline bool // emit use of retpoline stubs for indirect jmp/call
+ Retpoline bool // emit use of retpoline stubs for indirect jmp/call
+ Flag_maymorestack string // If not "", call this function before stack checks
Bso *bufio.Writer
Pathname string
Pkgpath string // the current package's import path, "" if unknown
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index 1f31d0c4cd..9e2ccc1929 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -658,6 +658,82 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
mov = AMOVW
}
+ if c.ctxt.Flag_maymorestack != "" {
+ // Save LR and REGCTXT.
+ frameSize := 2 * c.ctxt.Arch.PtrSize
+
+ p = c.ctxt.StartUnsafePoint(p, c.newprog)
+
+ // MOV REGLINK, -8/-16(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGLINK
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = int64(-frameSize)
+ p.To.Reg = REGSP
+
+ // MOV REGCTXT, -4/-8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
+ p.To.Reg = REGSP
+
+ // ADD $-8/$-16, SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(-frameSize)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = int32(frameSize)
+
+ // JAL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = AJAL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+ p.Mark |= BRANCH
+
+ // Restore LR and REGCTXT.
+
+ // MOV 0(SP), REGLINK
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 0
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGLINK
+
+ // MOV 4/8(SP), REGCTXT
+ p = obj.Appendp(p, c.newprog)
+ p.As = mov
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = int64(c.ctxt.Arch.PtrSize)
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // ADD $8/$16, SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = add
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(frameSize)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = int32(-frameSize)
+
+ p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
// MOV g_stackguard(g), R1
p = obj.Appendp(p, c.newprog)
@@ -787,7 +863,8 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
p.As = AJMP
p.To.Type = obj.TYPE_BRANCH
- p.To.SetTarget(c.cursym.Func().Text.Link)
+ p.To.SetTarget(startPred.Link)
+ startPred.Link.Mark |= LABEL
p.Mark |= BRANCH
// placeholder for q1's jump target
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index a590549f52..fa616691eb 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -347,7 +347,8 @@ func (w *writer) Sym(s *LSym) {
strings.HasPrefix(name, "runtime.gcbits."),
strings.HasSuffix(name, ".opendefer"),
strings.HasSuffix(name, ".arginfo0"),
- strings.HasSuffix(name, ".arginfo1"):
+ strings.HasSuffix(name, ".arginfo1"),
+ strings.HasSuffix(name, ".argliveinfo"):
// These are just bytes, or varints.
align = 1
case strings.HasPrefix(name, "gclocals·"):
@@ -415,6 +416,7 @@ func contentHashSection(s *LSym) byte {
strings.HasSuffix(name, ".opendefer") ||
strings.HasSuffix(name, ".arginfo0") ||
strings.HasSuffix(name, ".arginfo1") ||
+ strings.HasSuffix(name, ".argliveinfo") ||
strings.HasSuffix(name, ".args_stackmap") ||
strings.HasSuffix(name, ".stkobj") {
return 'F' // go.func.* or go.funcrel.*
diff --git a/src/cmd/internal/obj/ppc64/a.out.go b/src/cmd/internal/obj/ppc64/a.out.go
index dd6d903e28..1e74e64a29 100644
--- a/src/cmd/internal/obj/ppc64/a.out.go
+++ b/src/cmd/internal/obj/ppc64/a.out.go
@@ -79,10 +79,44 @@ const (
REG_R30
REG_R31
+ // CR bits. Use Book 1, chapter 2 naming for bits. Keep aligned to 32
+ REG_CR0LT
+ REG_CR0GT
+ REG_CR0EQ
+ REG_CR0SO
+ REG_CR1LT
+ REG_CR1GT
+ REG_CR1EQ
+ REG_CR1SO
+ REG_CR2LT
+ REG_CR2GT
+ REG_CR2EQ
+ REG_CR2SO
+ REG_CR3LT
+ REG_CR3GT
+ REG_CR3EQ
+ REG_CR3SO
+ REG_CR4LT
+ REG_CR4GT
+ REG_CR4EQ
+ REG_CR4SO
+ REG_CR5LT
+ REG_CR5GT
+ REG_CR5EQ
+ REG_CR5SO
+ REG_CR6LT
+ REG_CR6GT
+ REG_CR6EQ
+ REG_CR6SO
+ REG_CR7LT
+ REG_CR7GT
+ REG_CR7EQ
+ REG_CR7SO
+
/* Align FPR and VSR vectors such that when masked with 0x3F they produce
an equivalent VSX register. */
/* F0=4160 ... F31=4191 */
- REG_F0 = obj.RBasePPC64 + iota + 32
+ REG_F0
REG_F1
REG_F2
REG_F3
@@ -350,41 +384,65 @@ const (
)
const (
- C_NONE = iota
- C_REG
- C_FREG
- C_VREG
- C_VSREG
- C_CREG
- C_SPR /* special processor register */
- C_ZCON
- C_SCON /* 16 bit signed */
- C_UCON /* 32 bit signed, low 16 bits 0 */
- C_ADDCON /* -0x8000 <= v < 0 */
- C_ANDCON /* 0 < v <= 0xFFFF */
- C_LCON /* other 32 */
- C_DCON /* other 64 (could subdivide further) */
- C_SACON /* $n(REG) where n <= int16 */
- C_LACON /* $n(REG) where int16 < n <= int32 */
- C_DACON /* $n(REG) where int32 < n */
- C_SBRA
- C_LBRA
- C_LBRAPIC
- C_ZOREG // conjecture: either (1) register + zeroed offset, or (2) "R0" implies zero or C_REG
- C_SOREG // D/DS form memory operation
- C_LOREG // 32 bit addis + D/DS-form memory operation
- C_FPSCR
- C_XER
- C_LR
- C_CTR
- C_ANY
- C_GOK
- C_ADDR
- C_TLS_LE
- C_TLS_IE
- C_TEXTSIZE
+ C_NONE = iota
+ C_REGP /* An even numbered gpr which can be used a gpr pair argument */
+ C_REG /* Any gpr register */
+ C_FREGP /* An even numbered fpr which can be used a fpr pair argument */
+ C_FREG /* Any fpr register */
+ C_VREG /* Any vector register */
+ C_VSREGP /* An even numbered vsx register which can be used as a vsx register pair argument */
+ C_VSREG /* Any vector-scalar register */
+ C_CREG /* The condition registor (CR) */
+ C_CRBIT /* A single bit of the CR register (0-31) */
+ C_SPR /* special processor register */
+ C_ZCON /* The constant zero */
+ C_U1CON /* 1 bit unsigned constant */
+ C_U2CON /* 2 bit unsigned constant */
+ C_U3CON /* 3 bit unsigned constant */
+ C_U4CON /* 4 bit unsigned constant */
+ C_U5CON /* 5 bit unsigned constant */
+ C_U8CON /* 8 bit unsigned constant */
+ C_U15CON /* 15 bit unsigned constant */
+ C_S16CON /* 16 bit signed constant */
+ C_U16CON /* 16 bit unsigned constant */
+ C_32S16CON /* Any 32 bit constant of the form 0x....0000, signed or unsigned */
+ C_32CON /* Any constant which fits into 32 bits. Can be signed or unsigned */
+ C_S34CON /* 34 bit signed constant */
+ C_64CON /* Any constant which fits into 64 bits. Can be signed or unsigned */
+ C_SACON /* $n(REG) where n <= int16 */
+ C_LACON /* $n(REG) where n <= int32 */
+ C_DACON /* $n(REG) where n <= int64 */
+ C_SBRA /* A short offset argument to a branching instruction */
+ C_LBRA /* A long offset argument to a branching instruction */
+ C_LBRAPIC /* Like C_LBRA, but requires an extra NOP for potential TOC restore by the linker. */
+ C_ZOREG /* An reg+reg memory arg, or a $0+reg memory op */
+ C_SOREG /* An $n+reg memory arg where n is a 16 bit signed offset */
+ C_LOREG /* An $n+reg memory arg where n is a 32 bit signed offset */
+ C_FPSCR /* The fpscr register */
+ C_XER /* The xer, holds the carry bit */
+ C_LR /* The link register */
+ C_CTR /* The count register */
+ C_ANY /* Any argument */
+ C_GOK /* A non-matched argument */
+ C_ADDR /* A symbolic memory location */
+ C_TLS_LE /* A thread local, local-exec, type memory arg */
+ C_TLS_IE /* A thread local, initial-exec, type memory arg */
+ C_TEXTSIZE /* An argument with Type obj.TYPE_TEXTSIZE */
C_NCLASS /* must be the last */
+
+ /* Aliased names which should be cleaned up, or integrated. */
+ C_SCON = C_U15CON
+ C_UCON = C_32S16CON
+ C_ADDCON = C_S16CON
+ C_ANDCON = C_U16CON
+ C_LCON = C_32CON
+
+ /* Aliased names which may be generated by ppc64map for the optab. */
+ C_S3216CON = C_32S16CON // TODO: these should be treated differently (e.g xoris vs addis)
+ C_U3216CON = C_32S16CON
+ C_S32CON = C_32CON
+ C_U32CON = C_32CON
)
const (
diff --git a/src/cmd/internal/obj/ppc64/anames9.go b/src/cmd/internal/obj/ppc64/anames9.go
index b2632aa9ed..05bfd944d1 100644
--- a/src/cmd/internal/obj/ppc64/anames9.go
+++ b/src/cmd/internal/obj/ppc64/anames9.go
@@ -6,19 +6,30 @@ package ppc64
var cnames9 = []string{
"NONE",
+ "REGP",
"REG",
+ "FREGP",
"FREG",
"VREG",
+ "VSREGP",
"VSREG",
"CREG",
+ "CRBIT",
"SPR",
"ZCON",
- "SCON",
- "UCON",
- "ADDCON",
- "ANDCON",
- "LCON",
- "DCON",
+ "U1CON",
+ "U2CON",
+ "U3CON",
+ "U4CON",
+ "U5CON",
+ "U8CON",
+ "U15CON",
+ "S16CON",
+ "U16CON",
+ "32S16CON",
+ "32CON",
+ "S34CON",
+ "64CON",
"SACON",
"LACON",
"DACON",
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 6ed11b3f7e..31fbb7f7bf 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -307,8 +307,7 @@ var optab = []Optab{
{as: ABC, a6: C_ZOREG, type_: 15, size: 8},
{as: ASYNC, type_: 46, size: 4},
{as: AWORD, a1: C_LCON, type_: 40, size: 4},
- {as: ADWORD, a1: C_LCON, type_: 31, size: 8},
- {as: ADWORD, a1: C_DCON, type_: 31, size: 8},
+ {as: ADWORD, a1: C_64CON, type_: 31, size: 8},
{as: ADWORD, a1: C_LACON, type_: 31, size: 8},
{as: AADDME, a1: C_REG, a6: C_REG, type_: 47, size: 4},
{as: AEXTSB, a1: C_REG, a6: C_REG, type_: 48, size: 4},
@@ -336,7 +335,7 @@ var optab = []Optab{
{as: ALDMX, a1: C_SOREG, a6: C_REG, type_: 45, size: 4}, /* load doubleword monitored, x-form */
{as: AMADDHD, a1: C_REG, a2: C_REG, a3: C_REG, a6: C_REG, type_: 83, size: 4}, /* multiply-add high/low doubleword, va-form */
{as: AADDEX, a1: C_REG, a2: C_REG, a3: C_SCON, a6: C_REG, type_: 94, size: 4}, /* add extended using alternate carry, z23-form */
- {as: ACRAND, a1: C_CREG, a6: C_CREG, type_: 2, size: 4}, /* logical ops for condition registers xl-form */
+ {as: ACRAND, a1: C_CRBIT, a2: C_CRBIT, a6: C_CRBIT, type_: 2, size: 4}, /* logical ops for condition register bits xl-form */
/* Vector instructions */
@@ -485,15 +484,15 @@ var optab = []Optab{
{as: AXVCVSXDDP, a1: C_VSREG, a6: C_VSREG, type_: 89, size: 4}, /* vsx vector integer-fp conversion, xx2-form */
{as: ACMP, a1: C_REG, a6: C_REG, type_: 70, size: 4},
- {as: ACMP, a1: C_REG, a2: C_REG, a6: C_REG, type_: 70, size: 4},
+ {as: ACMP, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
{as: ACMP, a1: C_REG, a6: C_ADDCON, type_: 71, size: 4},
- {as: ACMP, a1: C_REG, a2: C_REG, a6: C_ADDCON, type_: 71, size: 4},
+ {as: ACMP, a1: C_REG, a2: C_CREG, a6: C_ADDCON, type_: 71, size: 4},
{as: ACMPU, a1: C_REG, a6: C_REG, type_: 70, size: 4},
- {as: ACMPU, a1: C_REG, a2: C_REG, a6: C_REG, type_: 70, size: 4},
+ {as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_REG, type_: 70, size: 4},
{as: ACMPU, a1: C_REG, a6: C_ANDCON, type_: 71, size: 4},
- {as: ACMPU, a1: C_REG, a2: C_REG, a6: C_ANDCON, type_: 71, size: 4},
+ {as: ACMPU, a1: C_REG, a2: C_CREG, a6: C_ANDCON, type_: 71, size: 4},
{as: AFCMPO, a1: C_FREG, a6: C_FREG, type_: 70, size: 4},
- {as: AFCMPO, a1: C_FREG, a2: C_REG, a6: C_FREG, type_: 70, size: 4},
+ {as: AFCMPO, a1: C_FREG, a2: C_CREG, a6: C_FREG, type_: 70, size: 4},
{as: ATW, a1: C_LCON, a2: C_REG, a6: C_REG, type_: 60, size: 4},
{as: ATW, a1: C_LCON, a2: C_REG, a6: C_ADDCON, type_: 61, size: 4},
{as: ADCBF, a1: C_ZOREG, type_: 43, size: 4},
@@ -584,9 +583,11 @@ func addpad(pc, a int64, ctxt *obj.Link, cursym *obj.LSym) int {
// or "MOVD R5, foo+10(SP) or pseudo-register is used. The other common case is when
// generating constants in register like "MOVD $constant, Rx".
func (c *ctxt9) getimpliedreg(a *obj.Addr, p *obj.Prog) int {
- switch oclass(a) {
- case C_ADDCON, C_ANDCON, C_UCON, C_LCON, C_SCON, C_ZCON:
+ class := oclass(a)
+ if class >= C_ZCON && class <= C_64CON {
return REGZERO
+ }
+ switch class {
case C_SACON, C_LACON:
return REGSP
case C_LOREG, C_SOREG, C_ZOREG:
@@ -839,45 +840,52 @@ func isuint32(v uint64) bool {
return uint64(uint32(v)) == v
}
+func (c *ctxt9) aclassreg(reg int16) int {
+ if REG_R0 <= reg && reg <= REG_R31 {
+ return C_REGP + int(reg&1)
+ }
+ if REG_F0 <= reg && reg <= REG_F31 {
+ return C_FREGP + int(reg&1)
+ }
+ if REG_V0 <= reg && reg <= REG_V31 {
+ return C_VREG
+ }
+ if REG_VS0 <= reg && reg <= REG_VS63 {
+ return C_VSREGP + int(reg&1)
+ }
+ if REG_CR0 <= reg && reg <= REG_CR7 || reg == REG_CR {
+ return C_CREG
+ }
+ if REG_CR0LT <= reg && reg <= REG_CR7SO {
+ return C_CRBIT
+ }
+ if REG_SPR0 <= reg && reg <= REG_SPR0+1023 {
+ switch reg {
+ case REG_LR:
+ return C_LR
+
+ case REG_XER:
+ return C_XER
+
+ case REG_CTR:
+ return C_CTR
+ }
+
+ return C_SPR
+ }
+ if reg == REG_FPSCR {
+ return C_FPSCR
+ }
+ return C_GOK
+}
+
func (c *ctxt9) aclass(a *obj.Addr) int {
switch a.Type {
case obj.TYPE_NONE:
return C_NONE
case obj.TYPE_REG:
- if REG_R0 <= a.Reg && a.Reg <= REG_R31 {
- return C_REG
- }
- if REG_F0 <= a.Reg && a.Reg <= REG_F31 {
- return C_FREG
- }
- if REG_V0 <= a.Reg && a.Reg <= REG_V31 {
- return C_VREG
- }
- if REG_VS0 <= a.Reg && a.Reg <= REG_VS63 {
- return C_VSREG
- }
- if REG_CR0 <= a.Reg && a.Reg <= REG_CR7 || a.Reg == REG_CR {
- return C_CREG
- }
- if REG_SPR0 <= a.Reg && a.Reg <= REG_SPR0+1023 {
- switch a.Reg {
- case REG_LR:
- return C_LR
-
- case REG_XER:
- return C_XER
-
- case REG_CTR:
- return C_CTR
- }
-
- return C_SPR
- }
- if a.Reg == REG_FPSCR {
- return C_FPSCR
- }
- return C_GOK
+ return c.aclassreg(a.Reg)
case obj.TYPE_MEM:
switch a.Name {
@@ -948,7 +956,7 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
case obj.NAME_NONE:
c.instoffset = a.Offset
if a.Reg != 0 {
- if -BIG <= c.instoffset && c.instoffset <= BIG {
+ if -BIG <= c.instoffset && c.instoffset < BIG {
return C_SACON
}
if isint32(c.instoffset) {
@@ -985,35 +993,47 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
}
if c.instoffset >= 0 {
- if c.instoffset == 0 {
- return C_ZCON
+ sbits := bits.Len64(uint64(c.instoffset))
+ switch {
+ case sbits <= 5:
+ return C_ZCON + sbits
+ case sbits <= 8:
+ return C_U8CON
+ case sbits <= 15:
+ return C_U15CON
+ case sbits <= 16:
+ return C_U16CON
+ case sbits <= 31:
+ // Special case, a positive int32 value which is a multiple of 2^16
+ if c.instoffset&0xFFFF == 0 {
+ return C_U3216CON
+ }
+ return C_U32CON
+ case sbits <= 32:
+ return C_U32CON
+ case sbits <= 33:
+ return C_S34CON
+ default:
+ return C_64CON
}
- if c.instoffset <= 0x7fff {
- return C_SCON
+ } else {
+ sbits := bits.Len64(uint64(^c.instoffset))
+ switch {
+ case sbits <= 15:
+ return C_S16CON
+ case sbits <= 31:
+ // Special case, a negative int32 value which is a multiple of 2^16
+ if c.instoffset&0xFFFF == 0 {
+ return C_S3216CON
+ }
+ return C_S32CON
+ case sbits <= 33:
+ return C_S34CON
+ default:
+ return C_64CON
}
- if c.instoffset <= 0xffff {
- return C_ANDCON
- }
- if c.instoffset&0xffff == 0 && isuint32(uint64(c.instoffset)) { /* && (instoffset & (1<<31)) == 0) */
- return C_UCON
- }
- if isint32(c.instoffset) || isuint32(uint64(c.instoffset)) {
- return C_LCON
- }
- return C_DCON
}
- if c.instoffset >= -0x8000 {
- return C_ADDCON
- }
- if c.instoffset&0xffff == 0 && isint32(c.instoffset) {
- return C_UCON
- }
- if isint32(c.instoffset) {
- return C_LCON
- }
- return C_DCON
-
case obj.TYPE_BRANCH:
if a.Sym != nil && c.ctxt.Flag_dynlink {
return C_LBRAPIC
@@ -1062,15 +1082,7 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab {
a2 := C_NONE
if p.Reg != 0 {
- if REG_R0 <= p.Reg && p.Reg <= REG_R31 {
- a2 = C_REG
- } else if REG_V0 <= p.Reg && p.Reg <= REG_V31 {
- a2 = C_VREG
- } else if REG_VS0 <= p.Reg && p.Reg <= REG_VS63 {
- a2 = C_VSREG
- } else if REG_F0 <= p.Reg && p.Reg <= REG_F31 {
- a2 = C_FREG
- }
+ a2 = c.aclassreg(p.Reg)
}
// c.ctxt.Logf("oplook %v %d %d %d %d\n", p, a1, a2, a3, a4, a5, a6)
@@ -1097,71 +1109,72 @@ func (c *ctxt9) oplook(p *obj.Prog) *Optab {
return &ops[0]
}
+// Compare two operand types (ex C_REG, or C_SCON)
+// and return true if b is compatible with a.
+//
+// Argument comparison isn't reflexitive, so care must be taken.
+// a is the argument type as found in optab, b is the argument as
+// fitted by aclass.
func cmp(a int, b int) bool {
if a == b {
return true
}
switch a {
- case C_LCON:
- if b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON {
- return true
- }
-
- case C_ADDCON:
- if b == C_ZCON || b == C_SCON {
- return true
- }
-
- case C_ANDCON:
- if b == C_ZCON || b == C_SCON {
- return true
- }
case C_SPR:
if b == C_LR || b == C_XER || b == C_CTR {
return true
}
- case C_UCON:
- if b == C_ZCON {
- return true
- }
+ case C_U1CON:
+ return cmp(C_ZCON, b)
+ case C_U2CON:
+ return cmp(C_U1CON, b)
+ case C_U3CON:
+ return cmp(C_U2CON, b)
+ case C_U4CON:
+ return cmp(C_U3CON, b)
+ case C_U5CON:
+ return cmp(C_U4CON, b)
+ case C_U8CON:
+ return cmp(C_U5CON, b)
+ case C_U15CON:
+ return cmp(C_U8CON, b)
+ case C_U16CON:
+ return cmp(C_U15CON, b)
- case C_SCON:
- if b == C_ZCON {
- return true
- }
+ case C_S16CON:
+ return cmp(C_U15CON, b)
+ case C_32CON:
+ return cmp(C_S16CON, b) || cmp(C_U16CON, b) || cmp(C_32S16CON, b)
+ case C_S34CON:
+ return cmp(C_32CON, b)
+ case C_64CON:
+ return cmp(C_S34CON, b)
+
+ case C_32S16CON:
+ return cmp(C_ZCON, b)
case C_LACON:
- if b == C_SACON {
- return true
- }
+ return cmp(C_SACON, b)
case C_LBRA:
- if b == C_SBRA {
- return true
- }
+ return cmp(C_SBRA, b)
case C_SOREG:
- if b == C_ZOREG {
- return true
- }
+ return cmp(C_ZOREG, b)
case C_LOREG:
- if b == C_SOREG || b == C_ZOREG {
- return true
- }
+ return cmp(C_SOREG, b)
+ // An even/odd register input always matches the regular register types.
case C_REG:
- if b == C_ZCON {
- return r0iszero != 0 /*TypeKind(100016)*/
- }
-
+ return cmp(C_REGP, b) || (b == C_ZCON && r0iszero != 0)
+ case C_FREG:
+ return cmp(C_FREGP, b)
case C_VSREG:
/* Allow any VR argument as a VSR operand. */
- if b == C_VREG {
- return true
- }
+ return cmp(C_VSREGP, b) || cmp(C_VREG, b)
case C_ANY:
return true
diff --git a/src/cmd/internal/obj/ppc64/asm_test.go b/src/cmd/internal/obj/ppc64/asm_test.go
index ec0ba19e37..ee2e5962f7 100644
--- a/src/cmd/internal/obj/ppc64/asm_test.go
+++ b/src/cmd/internal/obj/ppc64/asm_test.go
@@ -6,9 +6,12 @@ package ppc64
import (
"bytes"
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
"fmt"
"internal/testenv"
"io/ioutil"
+ "math"
"os"
"os/exec"
"path/filepath"
@@ -432,8 +435,121 @@ func TestRegValueAlignment(t *testing.T) {
{REG_F0, REG_F31, 63, 0},
{REG_SPR0, REG_SPR0 + 1023, 1023, 0},
{REG_CR0, REG_CR7, 7, 0},
+ {REG_CR0LT, REG_CR7SO, 31, 0},
}
for _, t := range testType {
tstFunc(t.rstart, t.rend, t.msk, t.rout)
}
}
+
+// Verify interesting obj.Addr arguments are classified correctly.
+func TestAddrClassifier(t *testing.T) {
+ type cmplx struct {
+ pic int
+ pic_dyn int
+ dyn int
+ nonpic int
+ }
+ tsts := [...]struct {
+ arg obj.Addr
+ output interface{}
+ }{
+ // Supported register type args
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R1}, C_REG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_R2}, C_REGP},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F1}, C_FREG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_F2}, C_FREGP},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_V2}, C_VREG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS1}, C_VSREG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_VS2}, C_VSREGP},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR}, C_CREG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1}, C_CREG},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_CR1SO}, C_CRBIT},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0}, C_SPR},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 1}, C_XER},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 8}, C_LR},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_SPR0 + 9}, C_CTR},
+ {obj.Addr{Type: obj.TYPE_REG, Reg: REG_FPSCR}, C_FPSCR},
+
+ // Memory type arguments.
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_GOTREF}, C_ADDR},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_TOCREF}, C_ADDR},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.STLSBSS}}, cmplx{C_TLS_IE, C_TLS_IE, C_TLS_LE, C_TLS_LE}},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_ADDR},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO}, C_SOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: BIG}, C_LOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM}, C_SOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: BIG}, C_LOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LOREG}, // 33 is FixedFrameSize-1
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE}, C_ZOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: 1}, C_SOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: BIG}, C_LOREG},
+ {obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_NONE, Offset: -BIG - 33}, C_LOREG},
+
+ // Misc (golang initializes -0.0 to 0.0, hence the obfuscation below)
+ {obj.Addr{Type: obj.TYPE_TEXTSIZE}, C_TEXTSIZE},
+ {obj.Addr{Type: obj.TYPE_FCONST, Val: 0.0}, C_ZCON},
+ {obj.Addr{Type: obj.TYPE_FCONST, Val: math.Float64frombits(0x8000000000000000)}, C_S16CON},
+
+ // Address type arguments
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1}, C_SACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: BIG}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: -BIG - 1}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_NONE, Offset: 1 << 32}, C_DACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_STATIC, Sym: &obj.LSym{Type: objabi.SDATA}}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: 1}, C_SACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: BIG}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_AUTO, Offset: -BIG - 1}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: 1}, C_SACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: BIG}, C_LACON},
+ {obj.Addr{Type: obj.TYPE_ADDR, Reg: REG_R0, Name: obj.NAME_PARAM, Offset: -BIG - 33}, C_LACON}, // 33 is FixedFrameSize-1
+
+ // Constant type arguments
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 0}, C_ZCON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1}, C_U1CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 2}, C_U2CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 4}, C_U3CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 8}, C_U4CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 16}, C_U5CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 32}, C_U8CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 14}, C_U15CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 15}, C_U16CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 16}, C_U3216CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 + 1<<16}, C_U32CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 32}, C_S34CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: 1 << 33}, C_64CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -1}, C_S16CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10000}, C_S3216CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -0x10001}, C_S32CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 33)}, C_S34CON},
+ {obj.Addr{Type: obj.TYPE_CONST, Name: obj.NAME_NONE, Offset: -(1 << 34)}, C_64CON},
+
+ // Branch like arguments
+ {obj.Addr{Type: obj.TYPE_BRANCH, Sym: &obj.LSym{Type: objabi.SDATA}}, cmplx{C_SBRA, C_LBRAPIC, C_LBRAPIC, C_SBRA}},
+ {obj.Addr{Type: obj.TYPE_BRANCH}, C_SBRA},
+ }
+
+ pic_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Arch: &Linkppc64}, autosize: 0}
+ pic_dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_shared: true, Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
+ dyn_ctxt9 := ctxt9{ctxt: &obj.Link{Flag_dynlink: true, Arch: &Linkppc64}, autosize: 0}
+ nonpic_ctxt9 := ctxt9{ctxt: &obj.Link{Arch: &Linkppc64}, autosize: 0}
+ ctxts := [...]*ctxt9{&pic_ctxt9, &pic_dyn_ctxt9, &dyn_ctxt9, &nonpic_ctxt9}
+ name := [...]string{"pic", "pic_dyn", "dyn", "nonpic"}
+ for _, tst := range tsts {
+ var expect []int
+ switch tst.output.(type) {
+ case cmplx:
+ v := tst.output.(cmplx)
+ expect = []int{v.pic, v.pic_dyn, v.dyn, v.nonpic}
+ case int:
+ expect = []int{tst.output.(int), tst.output.(int), tst.output.(int), tst.output.(int)}
+ }
+ for i, _ := range ctxts {
+ if output := ctxts[i].aclass(&tst.arg); output != expect[i] {
+ t.Errorf("%s.aclass(%v) = %v, expected %v\n", name[i], tst.arg, DRconv(output), DRconv(expect[i]))
+ }
+ }
+ }
+}
diff --git a/src/cmd/internal/obj/ppc64/doc.go b/src/cmd/internal/obj/ppc64/doc.go
index 6e601df82e..a9d89c93b4 100644
--- a/src/cmd/internal/obj/ppc64/doc.go
+++ b/src/cmd/internal/obj/ppc64/doc.go
@@ -239,6 +239,12 @@ Register naming
VSn is used for vector-scalar registers. V0-V31 overlap with VS32-VS63. (0-63)
CTR represents the count register.
LR represents the link register.
+ CR represents the condition register
+ CRn represents a condition register field. (0-7)
+ CRnLT represents CR bit 0 of CR field n. (0-7)
+ CRnGT represents CR bit 1 of CR field n. (0-7)
+ CRnEQ represents CR bit 2 of CR field n. (0-7)
+ CRnSO represents CR bit 3 of CR field n. (0-7)
*/
package ppc64
diff --git a/src/cmd/internal/obj/ppc64/list9.go b/src/cmd/internal/obj/ppc64/list9.go
index 8b0b36f375..ea0dae9e02 100644
--- a/src/cmd/internal/obj/ppc64/list9.go
+++ b/src/cmd/internal/obj/ppc64/list9.go
@@ -62,6 +62,11 @@ func rconv(r int) string {
if REG_CR0 <= r && r <= REG_CR7 {
return fmt.Sprintf("CR%d", r-REG_CR0)
}
+ if REG_CR0LT <= r && r <= REG_CR7SO {
+ bits := [4]string{"LT", "GT", "EQ", "SO"}
+ crf := (r - REG_CR0LT) / 4
+ return fmt.Sprintf("CR%d%s", crf, bits[r%4])
+ }
if r == REG_CR {
return "CR"
}
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index ee93fe048b..7ac6465a72 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -1048,7 +1048,96 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
*/
func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
- p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
+ if c.ctxt.Flag_maymorestack != "" {
+ if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink {
+ // See the call to morestack for why these are
+ // complicated to support.
+ c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported")
+ }
+
+ // Spill arguments. This has to happen before we open
+ // any more frame space.
+ p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
+
+ // Save LR and REGCTXT
+ frameSize := 8 + c.ctxt.FixedFrameSize()
+
+ // MOVD LR, REGTMP
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REG_LR
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGTMP
+ // MOVDU REGTMP, -16(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVDU
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGTMP
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = -frameSize
+ p.To.Reg = REGSP
+ p.Spadj = int32(frameSize)
+
+ // MOVD REGCTXT, 8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ p.To.Type = obj.TYPE_MEM
+ p.To.Offset = 8
+ p.To.Reg = REGSP
+
+ // BL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = ABL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+
+ // Restore LR and REGCTXT
+
+ // MOVD 8(SP), REGCTXT
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 8
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+
+ // MOVD 0(SP), REGTMP
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_MEM
+ p.From.Offset = 0
+ p.From.Reg = REGSP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGTMP
+
+ // MOVD REGTMP, LR
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGTMP
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REG_LR
+
+ // ADD $16, SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = AADD
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = frameSize
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGSP
+ p.Spadj = -int32(frameSize)
+
+ // Unspill arguments.
+ p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
+ }
+
+ // save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack
+ startPred := p
// MOVD g_stackguard(g), R22
p = obj.Appendp(p, c.newprog)
@@ -1262,7 +1351,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
p = obj.Appendp(p, c.newprog)
p.As = ABR
p.To.Type = obj.TYPE_BRANCH
- p.To.SetTarget(p0.Link)
+ p.To.SetTarget(startPred.Link)
// placeholder for q1's jump target
p = obj.Appendp(p, c.newprog)
diff --git a/src/cmd/internal/obj/riscv/asm_test.go b/src/cmd/internal/obj/riscv/asm_test.go
index f8f7b4f2ce..b23142dbe8 100644
--- a/src/cmd/internal/obj/riscv/asm_test.go
+++ b/src/cmd/internal/obj/riscv/asm_test.go
@@ -16,32 +16,30 @@ import (
"testing"
)
-// TestLarge generates a very large file to verify that large
-// program builds successfully, in particular, too-far
-// conditional branches are fixed.
-func TestLarge(t *testing.T) {
+// TestLargeBranch generates a large function with a very far conditional
+// branch, in order to ensure that it assembles successfully.
+func TestLargeBranch(t *testing.T) {
if testing.Short() {
- t.Skip("Skip in short mode")
+ t.Skip("Skipping test in short mode")
}
testenv.MustHaveGoBuild(t)
- dir, err := ioutil.TempDir("", "testlarge")
+ dir, err := ioutil.TempDir("", "testlargebranch")
if err != nil {
- t.Fatalf("could not create directory: %v", err)
+ t.Fatalf("Could not create directory: %v", err)
}
defer os.RemoveAll(dir)
// Generate a very large function.
buf := bytes.NewBuffer(make([]byte, 0, 7000000))
- gen(buf)
+ genLargeBranch(buf)
tmpfile := filepath.Join(dir, "x.s")
- err = ioutil.WriteFile(tmpfile, buf.Bytes(), 0644)
- if err != nil {
- t.Fatalf("can't write output: %v\n", err)
+ if err := ioutil.WriteFile(tmpfile, buf.Bytes(), 0644); err != nil {
+ t.Fatalf("Failed to write file: %v", err)
}
- // Build generated file.
+ // Assemble generated file.
cmd := exec.Command(testenv.GoToolPath(t), "tool", "asm", "-o", filepath.Join(dir, "x.o"), tmpfile)
cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
out, err := cmd.CombinedOutput()
@@ -50,8 +48,7 @@ func TestLarge(t *testing.T) {
}
}
-// gen generates a very large program, with a very far conditional branch.
-func gen(buf *bytes.Buffer) {
+func genLargeBranch(buf *bytes.Buffer) {
fmt.Fprintln(buf, "TEXT f(SB),0,$0-0")
fmt.Fprintln(buf, "BEQ X0, X0, label")
for i := 0; i < 1<<19; i++ {
@@ -61,6 +58,76 @@ func gen(buf *bytes.Buffer) {
fmt.Fprintln(buf, "ADD $0, X0, X0")
}
+// TestLargeCall generates a large function (>1MB of text) with a call to
+// a following function, in order to ensure that it assembles and links
+// correctly.
+func TestLargeCall(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode")
+ }
+ testenv.MustHaveGoBuild(t)
+
+ dir, err := ioutil.TempDir("", "testlargecall")
+ if err != nil {
+ t.Fatalf("could not create directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "go.mod"), []byte("module largecall"), 0644); err != nil {
+ t.Fatalf("Failed to write file: %v\n", err)
+ }
+ main := `package main
+func main() {
+ x()
+}
+
+func x()
+func y()
+`
+ if err := ioutil.WriteFile(filepath.Join(dir, "x.go"), []byte(main), 0644); err != nil {
+ t.Fatalf("failed to write main: %v\n", err)
+ }
+
+ // Generate a very large function with call.
+ buf := bytes.NewBuffer(make([]byte, 0, 7000000))
+ genLargeCall(buf)
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "x.s"), buf.Bytes(), 0644); err != nil {
+ t.Fatalf("Failed to write file: %v\n", err)
+ }
+
+ // Build generated files.
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal")
+ cmd.Dir = dir
+ cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("Build failed: %v, output: %s", err, out)
+ }
+
+ if runtime.GOARCH == "riscv64" && testenv.HasCGO() {
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=external")
+ cmd.Dir = dir
+ cmd.Env = append(os.Environ(), "GOARCH=riscv64", "GOOS=linux")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("Build failed: %v, output: %s", err, out)
+ }
+ }
+}
+
+func genLargeCall(buf *bytes.Buffer) {
+ fmt.Fprintln(buf, "TEXT ·x(SB),0,$0-0")
+ fmt.Fprintln(buf, "CALL ·y(SB)")
+ for i := 0; i < 1<<19; i++ {
+ fmt.Fprintln(buf, "ADD $0, X0, X0")
+ }
+ fmt.Fprintln(buf, "RET")
+ fmt.Fprintln(buf, "TEXT ·y(SB),0,$0-0")
+ fmt.Fprintln(buf, "ADD $0, X0, X0")
+ fmt.Fprintln(buf, "RET")
+}
+
// Issue 20348.
func TestNoRet(t *testing.T) {
dir, err := ioutil.TempDir("", "testnoret")
@@ -134,9 +201,6 @@ TEXT _stub(SB),$0-0
}
func TestBranch(t *testing.T) {
- if testing.Short() {
- t.Skip("Skipping in short mode")
- }
if runtime.GOARCH != "riscv64" {
t.Skip("Requires riscv64 to run")
}
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index b346b13577..5755b118db 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -280,14 +280,15 @@ func containsCall(sym *obj.LSym) bool {
}
// setPCs sets the Pc field in all instructions reachable from p.
-// It uses pc as the initial value.
-func setPCs(p *obj.Prog, pc int64) {
+// It uses pc as the initial value and returns the next available pc.
+func setPCs(p *obj.Prog, pc int64) int64 {
for ; p != nil; p = p.Link {
p.Pc = pc
for _, ins := range instructionsForProg(p) {
pc += int64(ins.length())
}
}
+ return pc
}
// stackOffset updates Addr offsets based on the current stack size.
@@ -582,17 +583,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
+ var callCount int
for p := cursym.Func().Text; p != nil; p = p.Link {
markRelocs(p)
+ if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC {
+ callCount++
+ }
}
+ const callTrampSize = 8 // 2 machine instructions.
+ maxTrampSize := int64(callCount * callTrampSize)
// Compute instruction addresses. Once we do that, we need to check for
// overextended jumps and branches. Within each iteration, Pc differences
// are always lower bounds (since the program gets monotonically longer,
// a fixed point will be reached). No attempt to handle functions > 2GiB.
for {
- rescan := false
- setPCs(cursym.Func().Text, 0)
+ big, rescan := false, false
+ maxPC := setPCs(cursym.Func().Text, 0)
+ if maxPC+maxTrampSize > (1 << 20) {
+ big = true
+ }
for p := cursym.Func().Text; p != nil; p = p.Link {
switch p.As {
@@ -619,6 +629,24 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
case AJAL:
// Linker will handle the intersymbol case and trampolines.
if p.To.Target() == nil {
+ if !big {
+ break
+ }
+ // This function is going to be too large for JALs
+ // to reach trampolines. Replace with AUIPC+JALR.
+ jmp := obj.Appendp(p, newprog)
+ jmp.As = AJALR
+ jmp.From = p.From
+ jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
+
+ p.As = AAUIPC
+ p.Mark = (p.Mark &^ NEED_CALL_RELOC) | NEED_PCREL_ITYPE_RELOC
+ p.SetFrom3(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym})
+ p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
+ p.Reg = obj.REG_NONE
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
+
+ rescan = true
break
}
offset := p.To.Target().Pc - p.Pc
@@ -694,6 +722,62 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
return p
}
+ if ctxt.Flag_maymorestack != "" {
+ // Save LR and REGCTXT
+ const frameSize = 16
+ p = ctxt.StartUnsafePoint(p, newprog)
+ // MOV LR, -16(SP)
+ p = obj.Appendp(p, newprog)
+ p.As = AMOV
+ p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -frameSize}
+ // ADDI $-16, SP
+ p = obj.Appendp(p, newprog)
+ p.As = AADDI
+ p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -frameSize}
+ p.Reg = REG_SP
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
+ p.Spadj = frameSize
+ // MOV REGCTXT, 8(SP)
+ p = obj.Appendp(p, newprog)
+ p.As = AMOV
+ p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
+
+ // CALL maymorestack
+ p = obj.Appendp(p, newprog)
+ p.As = obj.ACALL
+ p.To.Type = obj.TYPE_BRANCH
+ // See ../x86/obj6.go
+ p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
+ jalToSym(ctxt, p, REG_X5)
+
+ // Restore LR and REGCTXT
+
+ // MOV 8(SP), REGCTXT
+ p = obj.Appendp(p, newprog)
+ p.As = AMOV
+ p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
+ // MOV (SP), LR
+ p = obj.Appendp(p, newprog)
+ p.As = AMOV
+ p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
+ // ADDI $16, SP
+ p = obj.Appendp(p, newprog)
+ p.As = AADDI
+ p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: frameSize}
+ p.Reg = REG_SP
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
+ p.Spadj = -frameSize
+
+ p = ctxt.EndUnsafePoint(p, newprog, -1)
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
// MOV g_stackguard(g), X10
p = obj.Appendp(p, newprog)
p.As = AMOV
@@ -793,7 +877,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
p.As = AJAL
p.To = obj.Addr{Type: obj.TYPE_BRANCH}
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
- p.To.SetTarget(cursym.Func().Text.Link)
+ p.To.SetTarget(startPred.Link)
// placeholder for to_done's jump target
p = obj.Appendp(p, newprog)
@@ -1582,7 +1666,8 @@ func instructionsForOpImmediate(p *obj.Prog, as obj.As, rs int16) []*instruction
}
// Split into two additions, if possible.
- if ins.as == AADDI && ins.imm >= -(1<<12) && ins.imm < 1<<12-1 {
+ // Do not split SP-writing instructions, as otherwise the recorded SP delta may be wrong.
+ if p.Spadj == 0 && ins.as == AADDI && ins.imm >= -(1<<12) && ins.imm < 1<<12-1 {
imm0 := ins.imm / 2
imm1 := ins.imm - imm0
diff --git a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go
index de412c64a7..68d9589bf2 100644
--- a/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go
+++ b/src/cmd/internal/obj/riscv/testdata/testbranch/branch_test.go
@@ -32,7 +32,7 @@ func testGoBGTU(a, b int64) bool { return uint64(a) > uint64(b) }
func testGoBLE(a, b int64) bool { return a <= b }
func testGoBLEU(a, b int64) bool { return uint64(a) <= uint64(b) }
func testGoBLT(a, b int64) bool { return a < b }
-func testGoBLTZ(a, b int64) bool { return uint64(a) < uint64(b) }
+func testGoBLTU(a, b int64) bool { return uint64(a) < uint64(b) }
func TestBranchCondition(t *testing.T) {
tests := []struct {
diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go
index 201163b015..de40ff05af 100644
--- a/src/cmd/internal/obj/s390x/objz.go
+++ b/src/cmd/internal/obj/s390x/objz.go
@@ -294,6 +294,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
var pLast *obj.Prog
var pPre *obj.Prog
var pPreempt *obj.Prog
+ var pCheck *obj.Prog
wasSplit := false
for p := c.cursym.Func().Text; p != nil; p = p.Link {
pLast = p
@@ -323,7 +324,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q := p
if !p.From.Sym.NoSplit() {
- p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
+ p, pPreempt, pCheck = c.stacksplitPre(p, autosize) // emit pre part of split check
pPre = p
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
wasSplit = true //need post part of split
@@ -563,14 +564,69 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
if wasSplit {
- c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
+ c.stacksplitPost(pLast, pPre, pPreempt, pCheck, autosize) // emit post part of split check
}
}
-func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
+// stacksplitPre generates the function stack check prologue following
+// Prog p (which should be the TEXT Prog). It returns one or two
+// branch Progs that must be patched to jump to the morestack epilogue,
+// and the Prog that starts the morestack check.
+func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCheck *obj.Prog) {
+ if c.ctxt.Flag_maymorestack != "" {
+ // Save LR and REGCTXT
+ const frameSize = 16
+ p = c.ctxt.StartUnsafePoint(p, c.newprog)
+ // MOVD LR, -16(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: -frameSize}
+ // MOVD $-16(SP), SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_ADDR, Offset: -frameSize, Reg: REGSP}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
+ p.Spadj = frameSize
+ // MOVD REGCTXT, 8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
+
+ // BL maymorestack
+ p = obj.Appendp(p, c.newprog)
+ p.As = ABL
+ // See ../x86/obj6.go
+ sym := c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
+ p.To = obj.Addr{Type: obj.TYPE_BRANCH, Sym: sym}
+
+ // Restore LR and REGCTXT
+
+ // MOVD REGCTXT, 8(SP)
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
+ // MOVD (SP), LR
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 0}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
+ // MOVD $16(SP), SP
+ p = obj.Appendp(p, c.newprog)
+ p.As = AMOVD
+ p.From = obj.Addr{Type: obj.TYPE_CONST, Reg: REGSP, Offset: frameSize}
+ p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
+ p.Spadj = -frameSize
+
+ p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
+ }
// MOVD g_stackguard(g), R3
p = obj.Appendp(p, c.newprog)
+ // Jump back to here after morestack returns.
+ pCheck = p
p.As = AMOVD
p.From.Type = obj.TYPE_MEM
@@ -599,12 +655,11 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
p.As = ACMPUBGE
p.To.Type = obj.TYPE_BRANCH
- return p, nil
+ return p, nil, pCheck
}
// large stack: SP-framesize < stackguard-StackSmall
- var q *obj.Prog
offset := int64(framesize) - objabi.StackSmall
if framesize > objabi.StackBig {
// Such a large stack we need to protect against underflow.
@@ -625,7 +680,7 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
p.To.Reg = REG_R4
p = obj.Appendp(p, c.newprog)
- q = p
+ pPreempt = p
p.As = ACMPUBLT
p.From.Type = obj.TYPE_REG
p.From.Reg = REGSP
@@ -651,10 +706,16 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
p.As = ACMPUBGE
p.To.Type = obj.TYPE_BRANCH
- return p, q
+ return p, pPreempt, pCheck
}
-func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
+// stacksplitPost generates the function epilogue that calls morestack
+// and returns the new last instruction in the function.
+//
+// p is the last Prog in the function. pPre and pPreempt, if non-nil,
+// are the instructions that branch to the epilogue. This will fill in
+// their branch targets. pCheck is the Prog that begins the stack check.
+func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, framesize int32) *obj.Prog {
// Now we are at the end of the function, but logically
// we are still in function prologue. We need to fix the
// SP data and PCDATA.
@@ -692,12 +753,12 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog,
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
- // BR start
+ // BR pCheck
p = obj.Appendp(p, c.newprog)
p.As = ABR
p.To.Type = obj.TYPE_BRANCH
- p.To.SetTarget(c.cursym.Func().Text.Link)
+ p.To.SetTarget(pCheck)
return p
}
diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go
index 4d276db678..1c726f77d3 100644
--- a/src/cmd/internal/obj/wasm/wasmobj.go
+++ b/src/cmd/internal/obj/wasm/wasmobj.go
@@ -243,6 +243,51 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
p.Spadj = int32(framesize)
}
+ needMoreStack := !s.Func().Text.From.Sym.NoSplit()
+
+ // If the maymorestack debug option is enabled, insert the
+ // call to maymorestack *before* processing resume points so
+ // we can construct a resume point after maymorestack for
+ // morestack to resume at.
+ var pMorestack = s.Func().Text
+ if needMoreStack && ctxt.Flag_maymorestack != "" {
+ p := pMorestack
+
+ // Save REGCTXT on the stack.
+ const tempFrame = 8
+ p = appendp(p, AGet, regAddr(REG_SP))
+ p = appendp(p, AI32Const, constAddr(tempFrame))
+ p = appendp(p, AI32Sub)
+ p = appendp(p, ASet, regAddr(REG_SP))
+ p.Spadj = tempFrame
+ ctxtp := obj.Addr{
+ Type: obj.TYPE_MEM,
+ Reg: REG_SP,
+ Offset: 0,
+ }
+ p = appendp(p, AMOVD, regAddr(REGCTXT), ctxtp)
+
+ // maymorestack must not itself preempt because we
+ // don't have full stack information, so this can be
+ // ACALLNORESUME.
+ p = appendp(p, ACALLNORESUME, constAddr(0))
+ // See ../x86/obj6.go
+ sym := ctxt.LookupABI(ctxt.Flag_maymorestack, s.ABI())
+ p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
+
+ // Restore REGCTXT.
+ p = appendp(p, AMOVD, ctxtp, regAddr(REGCTXT))
+ p = appendp(p, AGet, regAddr(REG_SP))
+ p = appendp(p, AI32Const, constAddr(tempFrame))
+ p = appendp(p, AI32Add)
+ p = appendp(p, ASet, regAddr(REG_SP))
+ p.Spadj = -tempFrame
+
+ // Add an explicit ARESUMEPOINT after maymorestack for
+ // morestack to resume at.
+ pMorestack = appendp(p, ARESUMEPOINT)
+ }
+
// Introduce resume points for CALL instructions
// and collect other explicit resume points.
numResumePoints := 0
@@ -303,8 +348,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
tableIdxs = append(tableIdxs, uint64(numResumePoints))
s.Size = pc + 1
- if !s.Func().Text.From.Sym.NoSplit() {
- p := s.Func().Text
+ if needMoreStack {
+ p := pMorestack
if framesize <= objabi.StackSmall {
// small stack: SP <= stackguard
@@ -341,6 +386,13 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
// TODO(neelance): handle wraparound case
p = appendp(p, AIf)
+ // This CALL does *not* have a resume point after it
+ // (we already inserted all of the resume points). As
+ // a result, morestack will resume at the *previous*
+ // resume point (typically, the beginning of the
+ // function) and perform the morestack check again.
+ // This is why we don't need an explicit loop like
+ // other architectures.
p = appendp(p, obj.ACALL, constAddr(0))
if s.Func().Text.From.Sym.NeedCtxt() {
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 43ce832b17..6555756fd3 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2035,6 +2035,11 @@ type nopPad struct {
}
func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
+ if ctxt.Retpoline && ctxt.Arch.Family == sys.I386 {
+ ctxt.Diag("-spectre=ret not supported on 386")
+ ctxt.Retpoline = false // don't keep printing
+ }
+
pjc := makePjcCtx(ctxt)
if s.P != nil {
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index 183ca2ebe9..a82285a0d3 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -644,19 +644,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
- var regg int16
- if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
- if ctxt.Arch.Family == sys.AMD64 && cursym.ABI() == obj.ABIInternal {
- regg = REGG // use the g register directly in ABIInternal
- } else {
- p = obj.Appendp(p, newprog)
- regg = REG_CX
- if ctxt.Arch.Family == sys.AMD64 {
- regg = REGG // == REG_R14
- }
- p = load_g(ctxt, p, newprog, regg) // load g into regg
- }
- }
var regEntryTmp0, regEntryTmp1 int16
if ctxt.Arch.Family == sys.AMD64 {
regEntryTmp0, regEntryTmp1 = REGENTRYTMP0, REGENTRYTMP1
@@ -664,8 +651,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
regEntryTmp0, regEntryTmp1 = REG_BX, REG_DI
}
- if !cursym.Func().Text.From.Sym.NoSplit() {
- p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg), regg) // emit split check
+ var regg int16
+ if !p.From.Sym.NoSplit() {
+ // Emit split check and load G register
+ p, regg = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg))
+ } else if p.From.Sym.Wrapper() {
+ // Load G register for the wrapper code
+ p, regg = loadG(ctxt, cursym, p, newprog)
}
// Delve debugger would like the next instruction to be noted as the end of the function prologue.
@@ -973,12 +965,21 @@ func indir_cx(ctxt *obj.Link, a *obj.Addr) {
a.Reg = REG_CX
}
-// Append code to p to load g into cx.
-// Overwrites p with the first instruction (no first appendp).
-// Overwriting p is unusual but it lets use this in both the
-// prologue (caller must call appendp first) and in the epilogue.
-// Returns last new instruction.
-func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.Prog {
+// loadG ensures the G is loaded into a register (either CX or REGG),
+// appending instructions to p if necessary. It returns the new last
+// instruction and the G register.
+func loadG(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc) (*obj.Prog, int16) {
+ if ctxt.Arch.Family == sys.AMD64 && cursym.ABI() == obj.ABIInternal {
+ // Use the G register directly in ABIInternal
+ return p, REGG
+ }
+
+ var regg int16 = REG_CX
+ if ctxt.Arch.Family == sys.AMD64 {
+ regg = REGG // == REG_R14
+ }
+
+ p = obj.Appendp(p, newprog)
p.As = AMOVQ
if ctxt.Arch.PtrSize == 4 {
p.As = AMOVL
@@ -987,8 +988,9 @@ func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.P
p.From.Reg = REG_TLS
p.From.Offset = 0
p.To.Type = obj.TYPE_REG
- p.To.Reg = rg
+ p.To.Reg = regg
+ // Rewrite TLS instruction if necessary.
next := p.Link
progedit(ctxt, p, newprog)
for p.Link != next {
@@ -1000,24 +1002,26 @@ func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.P
p.From.Scale = 2
}
- return p
+ return p, regg
}
// Append code to p to check for stack split.
// Appends to (does not overwrite) p.
// Assumes g is in rg.
-// Returns last new instruction.
-func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32, rg int16) *obj.Prog {
+// Returns last new instruction and G register.
+func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) (*obj.Prog, int16) {
cmp := ACMPQ
lea := ALEAQ
mov := AMOVQ
sub := ASUBQ
+ push, pop := APUSHQ, APOPQ
if ctxt.Arch.Family == sys.I386 {
cmp = ACMPL
lea = ALEAL
mov = AMOVL
sub = ASUBL
+ push, pop = APUSHL, APOPL
}
tmp := int16(REG_AX) // use AX for 32-bit
@@ -1026,6 +1030,45 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
tmp = int16(REGENTRYTMP0)
}
+ if ctxt.Flag_maymorestack != "" {
+ p = cursym.Func().SpillRegisterArgs(p, newprog)
+
+ if cursym.Func().Text.From.Sym.NeedCtxt() {
+ p = obj.Appendp(p, newprog)
+ p.As = push
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = REGCTXT
+ }
+
+ // We call maymorestack with an ABI matching the
+ // caller's ABI. Since this is the first thing that
+ // happens in the function, we have to be consistent
+ // with the caller about CPU state (notably,
+ // fixed-meaning registers).
+
+ p = obj.Appendp(p, newprog)
+ p.As = obj.ACALL
+ p.To.Type = obj.TYPE_BRANCH
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
+
+ if cursym.Func().Text.From.Sym.NeedCtxt() {
+ p = obj.Appendp(p, newprog)
+ p.As = pop
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = REGCTXT
+ }
+
+ p = cursym.Func().UnspillRegisterArgs(p, newprog)
+ }
+
+ // Jump back to here after morestack returns.
+ startPred := p
+
+ // Load G register
+ var rg int16
+ p, rg = loadG(ctxt, cursym, p, newprog)
+
var q1 *obj.Prog
if framesize <= objabi.StackSmall {
// small stack: SP <= stackguard
@@ -1171,7 +1214,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
jmp := obj.Appendp(pcdata, newprog)
jmp.As = obj.AJMP
jmp.To.Type = obj.TYPE_BRANCH
- jmp.To.SetTarget(cursym.Func().Text.Link)
+ jmp.To.SetTarget(startPred.Link)
jmp.Spadj = +framesize
jls.To.SetTarget(spill)
@@ -1179,7 +1222,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
q1.To.SetTarget(spill)
}
- return end
+ return end, rg
}
func isR15(r int16) bool {
diff --git a/src/cmd/internal/objabi/flag.go b/src/cmd/internal/objabi/flag.go
index e41fc570b0..f75c054fcb 100644
--- a/src/cmd/internal/objabi/flag.go
+++ b/src/cmd/internal/objabi/flag.go
@@ -13,6 +13,8 @@ import (
"io/ioutil"
"log"
"os"
+ "reflect"
+ "sort"
"strconv"
"strings"
)
@@ -202,3 +204,165 @@ func DecodeArg(arg string) string {
}
return b.String()
}
+
+type debugField struct {
+ name string
+ help string
+ val interface{} // *int or *string
+}
+
+type DebugFlag struct {
+ tab map[string]debugField
+ any *bool
+
+ debugSSA DebugSSA
+}
+
+// A DebugSSA function is called to set a -d ssa/... option.
+// If nil, those options are reported as invalid options.
+// If DebugSSA returns a non-empty string, that text is reported as a compiler error.
+// If phase is "help", it should print usage information and terminate the process.
+type DebugSSA func(phase, flag string, val int, valString string) string
+
+// NewDebugFlag constructs a DebugFlag for the fields of debug, which
+// must be a pointer to a struct.
+//
+// Each field of *debug is a different value, named for the lower-case of the field name.
+// Each field must be an int or string and must have a `help` struct tag.
+// There may be an "Any bool" field, which will be set if any debug flags are set.
+//
+// The returned flag takes a comma-separated list of settings.
+// Each setting is name=value; for ints, name is short for name=1.
+//
+// If debugSSA is non-nil, any debug flags of the form ssa/... will be
+// passed to debugSSA for processing.
+func NewDebugFlag(debug interface{}, debugSSA DebugSSA) *DebugFlag {
+ flag := &DebugFlag{
+ tab: make(map[string]debugField),
+ debugSSA: debugSSA,
+ }
+
+ v := reflect.ValueOf(debug).Elem()
+ t := v.Type()
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ ptr := v.Field(i).Addr().Interface()
+ if f.Name == "Any" {
+ switch ptr := ptr.(type) {
+ default:
+ panic("debug.Any must have type bool")
+ case *bool:
+ flag.any = ptr
+ }
+ continue
+ }
+ name := strings.ToLower(f.Name)
+ help := f.Tag.Get("help")
+ if help == "" {
+ panic(fmt.Sprintf("debug.%s is missing help text", f.Name))
+ }
+ switch ptr.(type) {
+ default:
+ panic(fmt.Sprintf("debug.%s has invalid type %v (must be int or string)", f.Name, f.Type))
+ case *int, *string:
+ // ok
+ }
+ flag.tab[name] = debugField{name, help, ptr}
+ }
+
+ return flag
+}
+
+func (f *DebugFlag) Set(debugstr string) error {
+ if debugstr == "" {
+ return nil
+ }
+ if f.any != nil {
+ *f.any = true
+ }
+ for _, name := range strings.Split(debugstr, ",") {
+ if name == "" {
+ continue
+ }
+ // display help about the debug option itself and quit
+ if name == "help" {
+ fmt.Print(debugHelpHeader)
+ maxLen, names := 0, []string{}
+ if f.debugSSA != nil {
+ maxLen = len("ssa/help")
+ }
+ for name := range f.tab {
+ if len(name) > maxLen {
+ maxLen = len(name)
+ }
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ // Indent multi-line help messages.
+ nl := fmt.Sprintf("\n\t%-*s\t", maxLen, "")
+ for _, name := range names {
+ help := f.tab[name].help
+ fmt.Printf("\t%-*s\t%s\n", maxLen, name, strings.Replace(help, "\n", nl, -1))
+ }
+ if f.debugSSA != nil {
+ // ssa options have their own help
+ fmt.Printf("\t%-*s\t%s\n", maxLen, "ssa/help", "print help about SSA debugging")
+ }
+ os.Exit(0)
+ }
+
+ val, valstring, haveInt := 1, "", true
+ if i := strings.IndexAny(name, "=:"); i >= 0 {
+ var err error
+ name, valstring = name[:i], name[i+1:]
+ val, err = strconv.Atoi(valstring)
+ if err != nil {
+ val, haveInt = 1, false
+ }
+ }
+
+ if t, ok := f.tab[name]; ok {
+ switch vp := t.val.(type) {
+ case nil:
+ // Ignore
+ case *string:
+ *vp = valstring
+ case *int:
+ if !haveInt {
+ log.Fatalf("invalid debug value %v", name)
+ }
+ *vp = val
+ default:
+ panic("bad debugtab type")
+ }
+ } else if f.debugSSA != nil && strings.HasPrefix(name, "ssa/") {
+ // expect form ssa/phase/flag
+ // e.g. -d=ssa/generic_cse/time
+ // _ in phase name also matches space
+ phase := name[4:]
+ flag := "debug" // default flag is debug
+ if i := strings.Index(phase, "/"); i >= 0 {
+ flag = phase[i+1:]
+ phase = phase[:i]
+ }
+ err := f.debugSSA(phase, flag, val, valstring)
+ if err != "" {
+ log.Fatalf(err)
+ }
+ } else {
+ return fmt.Errorf("unknown debug key %s\n", name)
+ }
+ }
+
+ return nil
+}
+
+const debugHelpHeader = `usage: -d arg[,arg]* and arg is [=]
+
+ is one of:
+
+`
+
+func (f *DebugFlag) String() string {
+ return ""
+}
diff --git a/src/cmd/internal/objabi/funcdata.go b/src/cmd/internal/objabi/funcdata.go
index 4ff0ebe13d..4d49a8d548 100644
--- a/src/cmd/internal/objabi/funcdata.go
+++ b/src/cmd/internal/objabi/funcdata.go
@@ -14,6 +14,7 @@ const (
PCDATA_UnsafePoint = 0
PCDATA_StackMapIndex = 1
PCDATA_InlTreeIndex = 2
+ PCDATA_ArgLiveIndex = 3
FUNCDATA_ArgsPointerMaps = 0
FUNCDATA_LocalsPointerMaps = 1
@@ -21,6 +22,7 @@ const (
FUNCDATA_InlTree = 3
FUNCDATA_OpenCodedDeferInfo = 4
FUNCDATA_ArgInfo = 5
+ FUNCDATA_ArgLiveInfo = 6
// ArgsSizeUnknown is set in Func.argsize to mark all functions
// whose argument size is unknown (C vararg functions, and
diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go
index 0cc60fbe3b..a9c3030181 100644
--- a/src/cmd/internal/objabi/reloctype.go
+++ b/src/cmd/internal/objabi/reloctype.go
@@ -93,6 +93,11 @@ const (
// This is a marker relocation (0-sized), for the linker's reachabililty
// analysis.
R_USEIFACEMETHOD
+ // Similar to R_USEIFACEMETHOD, except instead of indicating a type +
+ // method offset with Sym+Add, Sym points to a symbol containing the name
+ // of the method being called. See the description in
+ // cmd/compile/internal/reflectdata/reflect.go:MarkUsedIfaceMethod for details.
+ R_USEGENERICIFACEMETHOD
// R_METHODOFF resolves to a 32-bit offset from the beginning of the section
// holding the data being relocated to the referenced symbol.
// It is a variant of R_ADDROFF used when linking from the uncommonType of a
diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go
index f2e06a5b21..d1b15b5a19 100644
--- a/src/cmd/internal/objabi/reloctype_string.go
+++ b/src/cmd/internal/objabi/reloctype_string.go
@@ -32,48 +32,49 @@ func _() {
_ = x[R_USETYPE-22]
_ = x[R_USEIFACE-23]
_ = x[R_USEIFACEMETHOD-24]
- _ = x[R_METHODOFF-25]
- _ = x[R_KEEP-26]
- _ = x[R_POWER_TOC-27]
- _ = x[R_GOTPCREL-28]
- _ = x[R_JMPMIPS-29]
- _ = x[R_DWARFSECREF-30]
- _ = x[R_DWARFFILEREF-31]
- _ = x[R_ARM64_TLS_LE-32]
- _ = x[R_ARM64_TLS_IE-33]
- _ = x[R_ARM64_GOTPCREL-34]
- _ = x[R_ARM64_GOT-35]
- _ = x[R_ARM64_PCREL-36]
- _ = x[R_ARM64_LDST8-37]
- _ = x[R_ARM64_LDST16-38]
- _ = x[R_ARM64_LDST32-39]
- _ = x[R_ARM64_LDST64-40]
- _ = x[R_ARM64_LDST128-41]
- _ = x[R_POWER_TLS_LE-42]
- _ = x[R_POWER_TLS_IE-43]
- _ = x[R_POWER_TLS-44]
- _ = x[R_ADDRPOWER_DS-45]
- _ = x[R_ADDRPOWER_GOT-46]
- _ = x[R_ADDRPOWER_PCREL-47]
- _ = x[R_ADDRPOWER_TOCREL-48]
- _ = x[R_ADDRPOWER_TOCREL_DS-49]
- _ = x[R_RISCV_CALL-50]
- _ = x[R_RISCV_CALL_TRAMP-51]
- _ = x[R_RISCV_PCREL_ITYPE-52]
- _ = x[R_RISCV_PCREL_STYPE-53]
- _ = x[R_RISCV_TLS_IE_ITYPE-54]
- _ = x[R_RISCV_TLS_IE_STYPE-55]
- _ = x[R_PCRELDBL-56]
- _ = x[R_ADDRMIPSU-57]
- _ = x[R_ADDRMIPSTLS-58]
- _ = x[R_ADDRCUOFF-59]
- _ = x[R_WASMIMPORT-60]
- _ = x[R_XCOFFREF-61]
+ _ = x[R_USEGENERICIFACEMETHOD-25]
+ _ = x[R_METHODOFF-26]
+ _ = x[R_KEEP-27]
+ _ = x[R_POWER_TOC-28]
+ _ = x[R_GOTPCREL-29]
+ _ = x[R_JMPMIPS-30]
+ _ = x[R_DWARFSECREF-31]
+ _ = x[R_DWARFFILEREF-32]
+ _ = x[R_ARM64_TLS_LE-33]
+ _ = x[R_ARM64_TLS_IE-34]
+ _ = x[R_ARM64_GOTPCREL-35]
+ _ = x[R_ARM64_GOT-36]
+ _ = x[R_ARM64_PCREL-37]
+ _ = x[R_ARM64_LDST8-38]
+ _ = x[R_ARM64_LDST16-39]
+ _ = x[R_ARM64_LDST32-40]
+ _ = x[R_ARM64_LDST64-41]
+ _ = x[R_ARM64_LDST128-42]
+ _ = x[R_POWER_TLS_LE-43]
+ _ = x[R_POWER_TLS_IE-44]
+ _ = x[R_POWER_TLS-45]
+ _ = x[R_ADDRPOWER_DS-46]
+ _ = x[R_ADDRPOWER_GOT-47]
+ _ = x[R_ADDRPOWER_PCREL-48]
+ _ = x[R_ADDRPOWER_TOCREL-49]
+ _ = x[R_ADDRPOWER_TOCREL_DS-50]
+ _ = x[R_RISCV_CALL-51]
+ _ = x[R_RISCV_CALL_TRAMP-52]
+ _ = x[R_RISCV_PCREL_ITYPE-53]
+ _ = x[R_RISCV_PCREL_STYPE-54]
+ _ = x[R_RISCV_TLS_IE_ITYPE-55]
+ _ = x[R_RISCV_TLS_IE_STYPE-56]
+ _ = x[R_PCRELDBL-57]
+ _ = x[R_ADDRMIPSU-58]
+ _ = x[R_ADDRMIPSTLS-59]
+ _ = x[R_ADDRCUOFF-60]
+ _ = x[R_WASMIMPORT-61]
+ _ = x[R_XCOFFREF-62]
}
-const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USEGENERICIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
-var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 221, 227, 238, 248, 257, 270, 284, 298, 312, 328, 339, 352, 365, 379, 393, 407, 422, 436, 450, 461, 475, 490, 507, 525, 546, 558, 576, 595, 614, 634, 654, 664, 675, 688, 699, 711, 721}
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 233, 244, 250, 261, 271, 280, 293, 307, 321, 335, 351, 362, 375, 388, 402, 416, 430, 445, 459, 473, 484, 498, 513, 530, 548, 569, 581, 599, 618, 637, 657, 677, 687, 698, 711, 722, 734, 744}
func (i RelocType) String() string {
i -= 1
diff --git a/src/cmd/internal/quoted/quoted.go b/src/cmd/internal/quoted/quoted.go
new file mode 100644
index 0000000000..e7575dfc66
--- /dev/null
+++ b/src/cmd/internal/quoted/quoted.go
@@ -0,0 +1,127 @@
+// Copyright 2017 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 quoted provides string manipulation utilities.
+package quoted
+
+import (
+ "flag"
+ "fmt"
+ "strings"
+ "unicode"
+)
+
+func isSpaceByte(c byte) bool {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
+
+// Split splits s into a list of fields,
+// allowing single or double quotes around elements.
+// There is no unescaping or other processing within
+// quoted fields.
+func Split(s string) ([]string, error) {
+ // Split fields allowing '' or "" around elements.
+ // Quotes further inside the string do not count.
+ var f []string
+ for len(s) > 0 {
+ for len(s) > 0 && isSpaceByte(s[0]) {
+ s = s[1:]
+ }
+ if len(s) == 0 {
+ break
+ }
+ // Accepted quoted string. No unescaping inside.
+ if s[0] == '"' || s[0] == '\'' {
+ quote := s[0]
+ s = s[1:]
+ i := 0
+ for i < len(s) && s[i] != quote {
+ i++
+ }
+ if i >= len(s) {
+ return nil, fmt.Errorf("unterminated %c string", quote)
+ }
+ f = append(f, s[:i])
+ s = s[i+1:]
+ continue
+ }
+ i := 0
+ for i < len(s) && !isSpaceByte(s[i]) {
+ i++
+ }
+ f = append(f, s[:i])
+ s = s[i:]
+ }
+ return f, nil
+}
+
+// Join joins a list of arguments into a string that can be parsed
+// with Split. Arguments are quoted only if necessary; arguments
+// without spaces or quotes are kept as-is. No argument may contain both
+// single and double quotes.
+func Join(args []string) (string, error) {
+ var buf []byte
+ for i, arg := range args {
+ if i > 0 {
+ buf = append(buf, ' ')
+ }
+ var sawSpace, sawSingleQuote, sawDoubleQuote bool
+ for _, c := range arg {
+ switch {
+ case c > unicode.MaxASCII:
+ continue
+ case isSpaceByte(byte(c)):
+ sawSpace = true
+ case c == '\'':
+ sawSingleQuote = true
+ case c == '"':
+ sawDoubleQuote = true
+ }
+ }
+ switch {
+ case !sawSpace && !sawSingleQuote && !sawDoubleQuote:
+ buf = append(buf, []byte(arg)...)
+
+ case !sawSingleQuote:
+ buf = append(buf, '\'')
+ buf = append(buf, []byte(arg)...)
+ buf = append(buf, '\'')
+
+ case !sawDoubleQuote:
+ buf = append(buf, '"')
+ buf = append(buf, []byte(arg)...)
+ buf = append(buf, '"')
+
+ default:
+ return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg)
+ }
+ }
+ return string(buf), nil
+}
+
+// A Flag parses a list of string arguments encoded with Join.
+// It is useful for flags like cmd/link's -extldflags.
+type Flag []string
+
+var _ flag.Value = (*Flag)(nil)
+
+func (f *Flag) Set(v string) error {
+ fs, err := Split(v)
+ if err != nil {
+ return err
+ }
+ *f = fs[:len(fs):len(fs)]
+ return nil
+}
+
+func (f *Flag) String() string {
+ if f == nil {
+ return ""
+ }
+ s, err := Join(*f)
+ if err != nil {
+ return strings.Join(*f, " ")
+ }
+ return s
+}
diff --git a/src/cmd/internal/str/str_test.go b/src/cmd/internal/quoted/quoted_test.go
similarity index 79%
rename from src/cmd/internal/str/str_test.go
rename to src/cmd/internal/quoted/quoted_test.go
index 3609af6a06..d76270c87b 100644
--- a/src/cmd/internal/str/str_test.go
+++ b/src/cmd/internal/quoted/quoted_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package str
+package quoted
import (
"reflect"
@@ -10,27 +10,7 @@ import (
"testing"
)
-var foldDupTests = []struct {
- list []string
- f1, f2 string
-}{
- {StringList("math/rand", "math/big"), "", ""},
- {StringList("math", "strings"), "", ""},
- {StringList("strings"), "", ""},
- {StringList("strings", "strings"), "strings", "strings"},
- {StringList("Rand", "rand", "math", "math/rand", "math/Rand"), "Rand", "rand"},
-}
-
-func TestFoldDup(t *testing.T) {
- for _, tt := range foldDupTests {
- f1, f2 := FoldDup(tt.list)
- if f1 != tt.f1 || f2 != tt.f2 {
- t.Errorf("foldDup(%q) = %q, %q, want %q, %q", tt.list, f1, f2, tt.f1, tt.f2)
- }
- }
-}
-
-func TestSplitQuotedFields(t *testing.T) {
+func TestSplit(t *testing.T) {
for _, test := range []struct {
name string
value string
@@ -54,7 +34,7 @@ func TestSplitQuotedFields(t *testing.T) {
{name: "quote_unclosed", value: `'a`, wantErr: "unterminated ' string"},
} {
t.Run(test.name, func(t *testing.T) {
- got, err := SplitQuotedFields(test.value)
+ got, err := Split(test.value)
if err != nil {
if test.wantErr == "" {
t.Fatalf("unexpected error: %v", err)
@@ -73,7 +53,7 @@ func TestSplitQuotedFields(t *testing.T) {
}
}
-func TestJoinAndQuoteFields(t *testing.T) {
+func TestJoin(t *testing.T) {
for _, test := range []struct {
name string
args []string
@@ -88,7 +68,7 @@ func TestJoinAndQuoteFields(t *testing.T) {
{name: "unquoteable", args: []string{`'"`}, wantErr: "contains both single and double quotes and cannot be quoted"},
} {
t.Run(test.name, func(t *testing.T) {
- got, err := JoinAndQuoteFields(test.args)
+ got, err := Join(test.args)
if err != nil {
if test.wantErr == "" {
t.Fatalf("unexpected error: %v", err)
diff --git a/src/cmd/internal/str/str.go b/src/cmd/internal/str/str.go
deleted file mode 100644
index 409cf8f7b4..0000000000
--- a/src/cmd/internal/str/str.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2017 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 str provides string manipulation utilities.
-package str
-
-import (
- "bytes"
- "flag"
- "fmt"
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-// StringList flattens its arguments into a single []string.
-// Each argument in args must have type string or []string.
-func StringList(args ...interface{}) []string {
- var x []string
- for _, arg := range args {
- switch arg := arg.(type) {
- case []string:
- x = append(x, arg...)
- case string:
- x = append(x, arg)
- default:
- panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
- }
- }
- return x
-}
-
-// ToFold returns a string with the property that
-// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
-// This lets us test a large set of strings for fold-equivalent
-// duplicates without making a quadratic number of calls
-// to EqualFold. Note that strings.ToUpper and strings.ToLower
-// do not have the desired property in some corner cases.
-func ToFold(s string) string {
- // Fast path: all ASCII, no upper case.
- // Most paths look like this already.
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
- goto Slow
- }
- }
- return s
-
-Slow:
- var buf bytes.Buffer
- for _, r := range s {
- // SimpleFold(x) cycles to the next equivalent rune > x
- // or wraps around to smaller values. Iterate until it wraps,
- // and we've found the minimum value.
- for {
- r0 := r
- r = unicode.SimpleFold(r0)
- if r <= r0 {
- break
- }
- }
- // Exception to allow fast path above: A-Z => a-z
- if 'A' <= r && r <= 'Z' {
- r += 'a' - 'A'
- }
- buf.WriteRune(r)
- }
- return buf.String()
-}
-
-// FoldDup reports a pair of strings from the list that are
-// equal according to strings.EqualFold.
-// It returns "", "" if there are no such strings.
-func FoldDup(list []string) (string, string) {
- clash := map[string]string{}
- for _, s := range list {
- fold := ToFold(s)
- if t := clash[fold]; t != "" {
- if s > t {
- s, t = t, s
- }
- return s, t
- }
- clash[fold] = s
- }
- return "", ""
-}
-
-// Contains reports whether x contains s.
-func Contains(x []string, s string) bool {
- for _, t := range x {
- if t == s {
- return true
- }
- }
- return false
-}
-
-// Uniq removes consecutive duplicate strings from ss.
-func Uniq(ss *[]string) {
- if len(*ss) <= 1 {
- return
- }
- uniq := (*ss)[:1]
- for _, s := range *ss {
- if s != uniq[len(uniq)-1] {
- uniq = append(uniq, s)
- }
- }
- *ss = uniq
-}
-
-func isSpaceByte(c byte) bool {
- return c == ' ' || c == '\t' || c == '\n' || c == '\r'
-}
-
-// SplitQuotedFields splits s into a list of fields,
-// allowing single or double quotes around elements.
-// There is no unescaping or other processing within
-// quoted fields.
-func SplitQuotedFields(s string) ([]string, error) {
- // Split fields allowing '' or "" around elements.
- // Quotes further inside the string do not count.
- var f []string
- for len(s) > 0 {
- for len(s) > 0 && isSpaceByte(s[0]) {
- s = s[1:]
- }
- if len(s) == 0 {
- break
- }
- // Accepted quoted string. No unescaping inside.
- if s[0] == '"' || s[0] == '\'' {
- quote := s[0]
- s = s[1:]
- i := 0
- for i < len(s) && s[i] != quote {
- i++
- }
- if i >= len(s) {
- return nil, fmt.Errorf("unterminated %c string", quote)
- }
- f = append(f, s[:i])
- s = s[i+1:]
- continue
- }
- i := 0
- for i < len(s) && !isSpaceByte(s[i]) {
- i++
- }
- f = append(f, s[:i])
- s = s[i:]
- }
- return f, nil
-}
-
-// JoinAndQuoteFields joins a list of arguments into a string that can be parsed
-// with SplitQuotedFields. Arguments are quoted only if necessary; arguments
-// without spaces or quotes are kept as-is. No argument may contain both
-// single and double quotes.
-func JoinAndQuoteFields(args []string) (string, error) {
- var buf []byte
- for i, arg := range args {
- if i > 0 {
- buf = append(buf, ' ')
- }
- var sawSpace, sawSingleQuote, sawDoubleQuote bool
- for _, c := range arg {
- switch {
- case c > unicode.MaxASCII:
- continue
- case isSpaceByte(byte(c)):
- sawSpace = true
- case c == '\'':
- sawSingleQuote = true
- case c == '"':
- sawDoubleQuote = true
- }
- }
- switch {
- case !sawSpace && !sawSingleQuote && !sawDoubleQuote:
- buf = append(buf, []byte(arg)...)
-
- case !sawSingleQuote:
- buf = append(buf, '\'')
- buf = append(buf, []byte(arg)...)
- buf = append(buf, '\'')
-
- case !sawDoubleQuote:
- buf = append(buf, '"')
- buf = append(buf, []byte(arg)...)
- buf = append(buf, '"')
-
- default:
- return "", fmt.Errorf("argument %q contains both single and double quotes and cannot be quoted", arg)
- }
- }
- return string(buf), nil
-}
-
-// A QuotedStringListFlag parses a list of string arguments encoded with
-// JoinAndQuoteFields. It is useful for flags like cmd/link's -extldflags.
-type QuotedStringListFlag []string
-
-var _ flag.Value = (*QuotedStringListFlag)(nil)
-
-func (f *QuotedStringListFlag) Set(v string) error {
- fs, err := SplitQuotedFields(v)
- if err != nil {
- return err
- }
- *f = fs[:len(fs):len(fs)]
- return nil
-}
-
-func (f *QuotedStringListFlag) String() string {
- if f == nil {
- return ""
- }
- s, err := JoinAndQuoteFields(*f)
- if err != nil {
- return strings.Join(*f, " ")
- }
- return s
-}
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index 0d2bad9612..c6a78a51eb 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -34,6 +34,41 @@ func MSanSupported(goos, goarch string) bool {
}
}
+// ASanSupported reports whether goos/goarch supports the address
+// sanitizer option.
+// There is a copy of this function in misc/cgo/testsanitizers/cc_test.go.
+func ASanSupported(goos, goarch string) bool {
+ switch goos {
+ case "linux":
+ return goarch == "arm64" || goarch == "amd64"
+ default:
+ return false
+ }
+}
+
+// FuzzSupported reports whether goos/goarch supports fuzzing
+// ('go test -fuzz=.').
+func FuzzSupported(goos, goarch string) bool {
+ switch goos {
+ case "darwin", "linux", "windows":
+ return true
+ default:
+ return false
+ }
+}
+
+// FuzzInstrumented reports whether fuzzing on goos/goarch uses coverage
+// instrumentation. (FuzzInstrumented implies FuzzSupported.)
+func FuzzInstrumented(goos, goarch string) bool {
+ switch goarch {
+ case "amd64", "arm64":
+ // TODO(#14565): support more architectures.
+ return FuzzSupported(goos, goarch)
+ default:
+ return false
+ }
+}
+
// MustLinkExternal reports whether goos/goarch requires external linking.
// (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.)
func MustLinkExternal(goos, goarch string) bool {
@@ -70,7 +105,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
case "c-shared":
switch platform {
- case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/s390x",
+ case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"darwin/amd64", "darwin/arm64",
diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go
index 604675caec..98c954f0f1 100644
--- a/src/cmd/link/doc.go
+++ b/src/cmd/link/doc.go
@@ -45,6 +45,8 @@ Flags:
Note that before Go 1.5 this option took two separate arguments.
-a
Disassemble output.
+ -asan
+ Link with C/C++ address sanitizer support.
-buildid id
Record id as Go toolchain build id.
-buildmode mode
diff --git a/src/cmd/link/dwarf_test.go b/src/cmd/link/dwarf_test.go
index f7bbb014d9..78ef3cfe97 100644
--- a/src/cmd/link/dwarf_test.go
+++ b/src/cmd/link/dwarf_test.go
@@ -8,7 +8,7 @@ import (
"bytes"
cmddwarf "cmd/internal/dwarf"
"cmd/internal/objfile"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"debug/dwarf"
"internal/testenv"
"os"
@@ -68,7 +68,7 @@ func testDWARF(t *testing.T, buildmode string, expectDWARF bool, env ...string)
if extld == "" {
extld = "gcc"
}
- extldArgs, err := str.SplitQuotedFields(extld)
+ extldArgs, err := quoted.Split(extld)
if err != nil {
t.Fatal(err)
}
diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go
index f56c9c1222..abae0f87bc 100644
--- a/src/cmd/link/internal/arm64/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -602,7 +602,7 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
rs := r.Xsym
rt := r.Type
- if r.Xadd != signext21(r.Xadd) {
+ if rt == objabi.R_ADDRARM64 && r.Xadd != signext21(r.Xadd) {
// If the relocation target would overflow the addend, then target
// a linker-manufactured label symbol with a smaller addend instead.
label := ldr.Lookup(offsetLabelName(ldr, rs, r.Xadd/peRelocLimit*peRelocLimit), ldr.SymVersion(rs))
diff --git a/src/cmd/link/internal/ld/config.go b/src/cmd/link/internal/ld/config.go
index 4045c97dd7..2d7e1bff68 100644
--- a/src/cmd/link/internal/ld/config.go
+++ b/src/cmd/link/internal/ld/config.go
@@ -74,7 +74,7 @@ func (mode *BuildMode) Set(s string) error {
*mode = BuildModeCArchive
case "c-shared":
switch buildcfg.GOARCH {
- case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
+ case "386", "amd64", "arm", "arm64", "ppc64le", "riscv64", "s390x":
default:
return badmode()
}
@@ -193,6 +193,10 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
return true, "msan"
}
+ if *flagAsan {
+ return true, "asan"
+ }
+
// Internally linking cgo is incomplete on some architectures.
// https://golang.org/issue/14449
if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.RISCV64) {
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 45e0e08793..0513a0d10b 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -2442,10 +2442,11 @@ func splitTextSections(ctxt *Link) bool {
return (ctxt.IsPPC64() || (ctxt.IsARM64() && ctxt.IsDarwin())) && ctxt.IsExternal()
}
-// On Wasm, we reserve 4096 bytes for zero page, then 4096 bytes for wasm_exec.js
-// to store command line args. Data sections starts from at least address 8192.
+// On Wasm, we reserve 4096 bytes for zero page, then 8192 bytes for wasm_exec.js
+// to store command line args and environment variables.
+// Data sections starts from at least address 12288.
// Keep in sync with wasm_exec.js.
-const wasmMinDataAddr = 4096 + 4096
+const wasmMinDataAddr = 4096 + 8192
// address assigns virtual addresses to all segments and sections and
// returns all segments in file order.
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 79acd73387..7b57a85cde 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -22,10 +22,11 @@ type deadcodePass struct {
ldr *loader.Loader
wq heap // work queue, using min-heap for better locality
- ifaceMethod map[methodsig]bool // methods declared in reached interfaces
- markableMethods []methodref // methods of reached types
- reflectSeen bool // whether we have seen a reflect method call
- dynlink bool
+ ifaceMethod map[methodsig]bool // methods called from reached interface call sites
+ genericIfaceMethod map[string]bool // names of methods called from reached generic interface call sites
+ markableMethods []methodref // methods of reached types
+ reflectSeen bool // whether we have seen a reflect method call
+ dynlink bool
methodsigstmp []methodsig // scratch buffer for decoding method signatures
}
@@ -33,6 +34,7 @@ type deadcodePass struct {
func (d *deadcodePass) init() {
d.ldr.InitReachable()
d.ifaceMethod = make(map[methodsig]bool)
+ d.genericIfaceMethod = make(map[string]bool)
if buildcfg.Experiment.FieldTrack {
d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
}
@@ -197,6 +199,13 @@ func (d *deadcodePass) flood() {
}
d.ifaceMethod[m] = true
continue
+ case objabi.R_USEGENERICIFACEMETHOD:
+ name := d.decodeGenericIfaceMethod(d.ldr, r.Sym())
+ if d.ctxt.Debugvlog > 1 {
+ d.ctxt.Logf("reached generic iface method: %s\n", name)
+ }
+ d.genericIfaceMethod[name] = true
+ continue // don't mark referenced symbol - it is not needed in the final binary.
}
rs := r.Sym()
if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
@@ -352,7 +361,7 @@ func deadcode(ctxt *Link) {
// in the last pass.
rem := d.markableMethods[:0]
for _, m := range d.markableMethods {
- if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+ if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
d.markMethod(m)
} else {
rem = append(rem, m)
@@ -425,6 +434,11 @@ func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, sym
return m
}
+// Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name.
+func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string {
+ return string(ldr.Data(symIdx))
+}
+
func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
p := ldr.Data(symIdx)
if !decodetypeHasUncommon(arch, p) {
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 3221d60f80..01ab6474b8 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -385,6 +385,9 @@ func libinit(ctxt *Link) {
} else if *flagMsan {
suffixsep = "_"
suffix = "msan"
+ } else if *flagAsan {
+ suffixsep = "_"
+ suffix = "asan"
}
Lflag(ctxt, filepath.Join(buildcfg.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", buildcfg.GOOS, buildcfg.GOARCH, suffixsep, suffix)))
@@ -529,6 +532,9 @@ func (ctxt *Link) loadlib() {
if *flagMsan {
loadinternal(ctxt, "runtime/msan")
}
+ if *flagAsan {
+ loadinternal(ctxt, "runtime/asan")
+ }
loadinternal(ctxt, "runtime")
for ; i < len(ctxt.Library); i++ {
lib := ctxt.Library[i]
@@ -1015,6 +1021,7 @@ var internalpkg = []string{
"runtime/cgo",
"runtime/race",
"runtime/msan",
+ "runtime/asan",
}
func ldhostobj(ld func(*Link, *bio.Reader, string, int64, string), headType objabi.HeadType, f *bio.Reader, pkg string, length int64, pn string, file string) *Hostobj {
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index 4d3b8b904c..a1d86965e4 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -34,7 +34,7 @@ import (
"bufio"
"cmd/internal/goobj"
"cmd/internal/objabi"
- "cmd/internal/str"
+ "cmd/internal/quoted"
"cmd/internal/sys"
"cmd/link/internal/benchmark"
"flag"
@@ -69,14 +69,15 @@ var (
flagDumpDep = flag.Bool("dumpdep", false, "dump symbol dependency graph")
flagRace = flag.Bool("race", false, "enable race detector")
flagMsan = flag.Bool("msan", false, "enable MSan interface")
+ flagAsan = flag.Bool("asan", false, "enable ASan interface")
flagAslr = flag.Bool("aslr", true, "enable ASLR for buildmode=c-shared on windows")
flagFieldTrack = flag.String("k", "", "set field tracking `symbol`")
flagLibGCC = flag.String("libgcc", "", "compiler support lib for internal linking; use \"none\" to disable")
flagTmpdir = flag.String("tmpdir", "", "use `directory` for temporary files")
- flagExtld str.QuotedStringListFlag
- flagExtldflags str.QuotedStringListFlag
+ flagExtld quoted.Flag
+ flagExtldflags quoted.Flag
flagExtar = flag.String("extar", "", "archive program for buildmode=c-archive")
flagA = flag.Bool("a", false, "no-op (deprecated)")
diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go
index 6920a0a843..b1ee3c5628 100644
--- a/src/cmd/link/internal/ld/outbuf_darwin.go
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build darwin && go1.12
+// +build darwin,go1.12
+
package ld
import (
diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go
index 6564bd54a3..3bffe4543d 100644
--- a/src/cmd/link/internal/ld/outbuf_nofallocate.go
+++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !darwin && !linux
-// +build !darwin,!linux
+//go:build (!darwin && !linux) || (darwin && !go1.12)
+// +build !darwin,!linux darwin,!go1.12
package ld
diff --git a/src/cmd/link/internal/ld/outbuf_notdarwin.go b/src/cmd/link/internal/ld/outbuf_notdarwin.go
index f9caa413e3..85e64421a3 100644
--- a/src/cmd/link/internal/ld/outbuf_notdarwin.go
+++ b/src/cmd/link/internal/ld/outbuf_notdarwin.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build !darwin
-// +build !darwin
+//go:build !darwin || (darwin && !go1.12)
+// +build !darwin darwin,!go1.12
package ld
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 7b1a0b7d1a..720c03afd2 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -566,6 +566,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
strings.HasSuffix(name, ".opendefer"),
strings.HasSuffix(name, ".arginfo0"),
strings.HasSuffix(name, ".arginfo1"),
+ strings.HasSuffix(name, ".argliveinfo"),
strings.HasSuffix(name, ".args_stackmap"),
strings.HasSuffix(name, ".stkobj"):
ldr.SetAttrNotInSymbolTable(s, true)
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 9b7888e940..34c1c6a4c8 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -2592,7 +2592,9 @@ type ErrorReporter struct {
//
func (reporter *ErrorReporter) Errorf(s Sym, format string, args ...interface{}) {
if s != 0 && reporter.ldr.SymName(s) != "" {
- format = reporter.ldr.SymName(s) + ": " + format
+ // Note: Replace is needed here because symbol names might have % in them,
+ // due to the use of LinkString for names of instantiating types.
+ format = strings.Replace(reporter.ldr.SymName(s), "%", "%%", -1) + ": " + format
} else {
format = fmt.Sprintf("sym %d: %s", s, format)
}
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index 9507010603..62b319d196 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -1067,35 +1067,31 @@ func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilde
return glink
}
- // This is essentially the resolver from the ppc64 ELF ABI.
+ // This is essentially the resolver from the ppc64 ELFv2 ABI.
// At entry, r12 holds the address of the symbol resolver stub
// for the target routine and the argument registers hold the
// arguments for the target routine.
//
+ // PC-rel offsets are computed once the final codesize of the
+ // resolver is known.
+ //
// This stub is PIC, so first get the PC of label 1 into r11.
- // Other things will be relative to this.
glink.AddUint32(ctxt.Arch, 0x7c0802a6) // mflr r0
glink.AddUint32(ctxt.Arch, 0x429f0005) // bcl 20,31,1f
glink.AddUint32(ctxt.Arch, 0x7d6802a6) // 1: mflr r11
- glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlf r0
+ glink.AddUint32(ctxt.Arch, 0x7c0803a6) // mtlr r0
- // Compute the .plt array index from the entry point address.
- // Because this is PIC, everything is relative to label 1b (in
- // r11):
- // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4
- glink.AddUint32(ctxt.Arch, 0x3800ffd0) // li r0,-(res_0-1b)=-48
+ // Compute the .plt array index from the entry point address
+ // into r0. This is computed relative to label 1 above.
+ glink.AddUint32(ctxt.Arch, 0x38000000) // li r0,-(res_0-1b)
glink.AddUint32(ctxt.Arch, 0x7c006214) // add r0,r0,r12
glink.AddUint32(ctxt.Arch, 0x7c0b0050) // sub r0,r0,r11
glink.AddUint32(ctxt.Arch, 0x7800f082) // srdi r0,r0,2
- // r11 = address of the first byte of the PLT
- r, _ := glink.AddRel(objabi.R_ADDRPOWER)
- r.SetSym(ctxt.PLT)
- r.SetSiz(8)
- r.SetOff(int32(glink.Size()))
- r.SetAdd(0)
- glink.AddUint32(ctxt.Arch, 0x3d600000) // addis r11,0,.plt@ha
- glink.AddUint32(ctxt.Arch, 0x396b0000) // addi r11,r11,.plt@l
+ // Load the PC-rel offset of ".plt - 1b", and add it to 1b.
+ // This is stored after this stub and before the resolvers.
+ glink.AddUint32(ctxt.Arch, 0xe98b0000) // ld r12,res_0-1b-8(r11)
+ glink.AddUint32(ctxt.Arch, 0x7d6b6214) // add r11,r11,r12
// Load r12 = dynamic resolver address and r11 = DSO
// identifier from the first two doublewords of the PLT.
@@ -1106,6 +1102,19 @@ func ensureglinkresolver(ctxt *ld.Link, ldr *loader.Loader) *loader.SymbolBuilde
glink.AddUint32(ctxt.Arch, 0x7d8903a6) // mtctr r12
glink.AddUint32(ctxt.Arch, 0x4e800420) // bctr
+ // Store the PC-rel offset to the PLT
+ r, _ := glink.AddRel(objabi.R_PCREL)
+ r.SetSym(ctxt.PLT)
+ r.SetSiz(8)
+ r.SetOff(int32(glink.Size()))
+ r.SetAdd(glink.Size()) // Adjust the offset to be relative to label 1 above.
+ glink.AddUint64(ctxt.Arch, 0) // The offset to the PLT.
+
+ // Resolve PC-rel offsets above now the final size of the stub is known.
+ res0m1b := glink.Size() - 8 // res_0 - 1b
+ glink.SetUint32(ctxt.Arch, 16, 0x38000000|uint32(uint16(-res0m1b)))
+ glink.SetUint32(ctxt.Arch, 32, 0xe98b0000|uint32(uint16(res0m1b-8)))
+
// The symbol resolvers must immediately follow.
// res_0:
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 2b0b2dc4a1..ad7658bb25 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -997,13 +997,31 @@ package main
var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
+var addr = [...]*byte{
+ &x[1<<23-1],
+ &x[1<<23],
+ &x[1<<23+1],
+ &x[1<<24-1],
+ &x[1<<24],
+ &x[1<<24+1],
+}
+
func main() {
+ // check relocations in instructions
check(x[1<<23-1], 0)
check(x[1<<23], 23)
check(x[1<<23+1], 0)
check(x[1<<24-1], 0)
check(x[1<<24], 24)
check(x[1<<24+1], 0)
+
+ // check absolute address relocations in data
+ check(*addr[0], 0)
+ check(*addr[1], 23)
+ check(*addr[2], 0)
+ check(*addr[3], 0)
+ check(*addr[4], 24)
+ check(*addr[5], 0)
}
func check(x, y byte) {
diff --git a/src/cmd/nm/nm_cgo_test.go b/src/cmd/nm/nm_cgo_test.go
index 1544be041a..0c826738ec 100644
--- a/src/cmd/nm/nm_cgo_test.go
+++ b/src/cmd/nm/nm_cgo_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build cgo
-// +build cgo
package main
diff --git a/src/cmd/pprof/readlineui.go b/src/cmd/pprof/readlineui.go
index f46e934e0f..7ad712cd60 100644
--- a/src/cmd/pprof/readlineui.go
+++ b/src/cmd/pprof/readlineui.go
@@ -6,9 +6,6 @@
// that provides the readline functionality if possible.
//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows) && !appengine && !android
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
-// +build !appengine
-// +build !android
package main
diff --git a/src/cmd/trace/annotations_test.go b/src/cmd/trace/annotations_test.go
index acd5693c7d..9f1c8e3b3b 100644
--- a/src/cmd/trace/annotations_test.go
+++ b/src/cmd/trace/annotations_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !js
-// +build !js
package main
diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go
index f94586abf3..3aeba223ee 100644
--- a/src/cmd/trace/main.go
+++ b/src/cmd/trace/main.go
@@ -66,7 +66,7 @@ var (
func main() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, usageMessage)
+ fmt.Fprint(os.Stderr, usageMessage)
os.Exit(2)
}
flag.Parse()
diff --git a/src/cmd/trace/trace_test.go b/src/cmd/trace/trace_test.go
index 2b1a68d7f3..87fd3a3515 100644
--- a/src/cmd/trace/trace_test.go
+++ b/src/cmd/trace/trace_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build !js
-// +build !js
package main
diff --git a/src/cmd/trace/trace_unix_test.go b/src/cmd/trace/trace_unix_test.go
index 8dc56a8c7b..f35061ec63 100644
--- a/src/cmd/trace/trace_unix_test.go
+++ b/src/cmd/trace/trace_unix_test.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
index 4f11645185..6447092d3d 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
@@ -165,18 +165,60 @@ func GetBuildID(binary io.ReaderAt) ([]byte, error) {
return nil, nil
}
+// kernelBase caluclates the base for kernel mappings, which usually require
+// special handling. For kernel mappings, tools (like perf) use the address of
+// the kernel relocation symbol (_text or _stext) as the mmap start. Additionaly,
+// for obfuscation, ChromeOS profiles have the kernel image remapped to the 0-th page.
+func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, bool) {
+ const (
+ // PAGE_OFFSET for PowerPC64, see arch/powerpc/Kconfig in the kernel sources.
+ pageOffsetPpc64 = 0xc000000000000000
+ pageSize = 4096
+ )
+
+ if loadSegment.Vaddr == start-offset {
+ return offset, true
+ }
+ if start == 0 && limit != 0 && stextOffset != nil {
+ // ChromeOS remaps its kernel to 0. Nothing else should come
+ // down this path. Empirical values:
+ // VADDR=0xffffffff80200000
+ // stextOffset=0xffffffff80200198
+ return start - *stextOffset, true
+ }
+ if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
+ // Some kernels look like:
+ // VADDR=0xffffffff80200000
+ // stextOffset=0xffffffff80200198
+ // Start=0xffffffff83200000
+ // Limit=0xffffffff84200000
+ // Offset=0 (0xc000000000000000 for PowerPC64) (== Start for ASLR kernel)
+ // So the base should be:
+ if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) {
+ // perf uses the address of _stext as start. Some tools may
+ // adjust for this before calling GetBase, in which case the page
+ // alignment should be different from that of stextOffset.
+ return start - *stextOffset, true
+ }
+
+ return start - loadSegment.Vaddr, true
+ }
+ if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize {
+ // ChromeOS remaps its kernel to 0 + start%pageSize. Nothing
+ // else should come down this path. Empirical values:
+ // start=0x198 limit=0x2f9fffff offset=0
+ // VADDR=0xffffffff81000000
+ // stextOffset=0xffffffff81000198
+ return start - *stextOffset, true
+ }
+ return 0, false
+}
+
// GetBase determines the base address to subtract from virtual
// address to get symbol table address. For an executable, the base
// is 0. Otherwise, it's a shared library, and the base is the
-// address where the mapping starts. The kernel is special, and may
-// use the address of the _stext symbol as the mmap start. _stext
-// offset can be obtained with `nm vmlinux | grep _stext`
+// address where the mapping starts. The kernel needs special hanldling.
func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) {
- const (
- pageSize = 4096
- // PAGE_OFFSET for PowerPC64, see arch/powerpc/Kconfig in the kernel sources.
- pageOffsetPpc64 = 0xc000000000000000
- )
if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) {
// Some tools may introduce a fake mapping that spans the entire
@@ -202,43 +244,15 @@ func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint6
// the 64-bit address space.
return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
}
- // Various kernel heuristics and cases follow.
- if loadSegment.Vaddr == start-offset {
- return offset, nil
+ // Various kernel heuristics and cases are handled separately.
+ if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
+ return base, nil
}
- if start == 0 && limit != 0 {
- // ChromeOS remaps its kernel to 0. Nothing else should come
- // down this path. Empirical values:
- // VADDR=0xffffffff80200000
- // stextOffset=0xffffffff80200198
- if stextOffset != nil {
- return -*stextOffset, nil
- }
- return -loadSegment.Vaddr, nil
- }
- if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
- // Some kernels look like:
- // VADDR=0xffffffff80200000
- // stextOffset=0xffffffff80200198
- // Start=0xffffffff83200000
- // Limit=0xffffffff84200000
- // Offset=0 (0xc000000000000000 for PowerPC64) (== Start for ASLR kernel)
- // So the base should be:
- if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) {
- // perf uses the address of _stext as start. Some tools may
- // adjust for this before calling GetBase, in which case the page
- // alignment should be different from that of stextOffset.
- return start - *stextOffset, nil
- }
-
+ // ChromeOS can remap its kernel to 0, and the caller might have not found
+ // the _stext symbol. Split this case from kernelBase() above, since we don't
+ // want to apply it to an ET_DYN user-mode executable.
+ if start == 0 && limit != 0 && stextOffset == nil {
return start - loadSegment.Vaddr, nil
- } else if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize {
- // ChromeOS remaps its kernel to 0 + start%pageSize. Nothing
- // else should come down this path. Empirical values:
- // start=0x198 limit=0x2f9fffff offset=0
- // VADDR=0xffffffff81000000
- // stextOffset=0xffffffff81000198
- return start - *stextOffset, nil
}
return 0, fmt.Errorf("don't know how to handle EXEC segment: %v start=0x%x limit=0x%x offset=0x%x", *loadSegment, start, limit, offset)
@@ -255,6 +269,11 @@ func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint6
if loadSegment == nil {
return start - offset, nil
}
+ // Kernels compiled as PIE can be ET_DYN as well. Use heuristic, similar to
+ // the ET_EXEC case above.
+ if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
+ return base, nil
+ }
// The program header, if not nil, indicates the offset in the file where
// the executable segment is located (loadSegment.Off), and the base virtual
// address where the first byte of the segment is loaded
diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go b/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go
index 17c9f6eb94..851693f1d0 100644
--- a/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go
+++ b/src/cmd/vendor/github.com/google/pprof/internal/report/source_html.go
@@ -72,5 +72,4 @@ function pprof_toggle_asm(e) {
const weblistPageClosing = `