-Converting a constant yields a typed constant as result.
+Converting a constant to a type that is not a type parameter
+yields a typed constant.
+Converting a constant to a type parameter yields a non-constant value of that type.
@@ -4587,7 +5251,8 @@ 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.
@@ -5036,7 +5701,8 @@ switch x.(type) {
Cases then match actual types T against the dynamic type of the
expression x. As with type assertions, x must be of
-interface type, and each non-interface type
+interface type, but not a
+type parameter, and each non-interface type
T listed in a case must implement the type of x.
The types listed in the cases of a type switch must all be
different.
@@ -5047,7 +5713,6 @@ TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClau
TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
TypeCaseClause = TypeSwitchCase ":" StatementList .
TypeSwitchCase = "case" TypeList | "default" .
-TypeList = Type { "," Type } .
@@ -5824,12 +6489,24 @@ len(s) string type string length in bytes
[]T slice length
map[K]T map length (number of defined keys)
chan T number of elements queued in channel buffer
+ type parameter see below
cap(s) [n]T, *[n]T array length (== n)
[]T slice capacity
chan T channel buffer capacity
+ type parameter see below
+
+If the argument type is a type parameter P,
+P must have specific types, and
+the call len(e) (or cap(e) respectively) must be valid for
+each specific type of P.
+The result is the length (or capacity, respectively) of the argument whose type
+corresponds to the type argument with which P was
+instantiated.
+
+
The capacity of a slice is the number of elements for which there is
space allocated in the underlying array.
@@ -6771,8 +7448,14 @@ uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
-Calls to Alignof, Offsetof, and
-Sizeof are compile-time constant expressions of type uintptr.
+A (variable of) type T has variable size if T
+is a type parameter, or if it is an array or struct type containing elements
+or fields of variable size. Otherwise the size is constant.
+Calls to Alignof, Offsetof, and Sizeof
+are compile-time constant expressions of
+type uintptr if their arguments (or the struct s in
+the selector expression s.f for Offsetof) are types
+of constant size.
diff --git a/lib/time/README b/lib/time/README
index aab4daa7e2df07e01a7318905eb35917d23d2bf5..0de06df13b9a4575a0f510e854d171878ec7b89d 100644
--- a/lib/time/README
+++ b/lib/time/README
@@ -4,7 +4,7 @@ The IANA asserts that the database is in the public domain.
For more information, see
https://www.iana.org/time-zones
-ftp://ftp.iana.org/tz/code/tz-link.htm
-http://tools.ietf.org/html/rfc6557
+ftp://ftp.iana.org/tz/code/tz-link.html
+https://datatracker.ietf.org/doc/html/rfc6557
To rebuild the archive, read and run update.bash.
diff --git a/lib/time/update.bash b/lib/time/update.bash
index e088ea6b908a5fc281654cb9bebbd87c46173ed5..feb95e2e53d30a1ecbc24662ae834ac17d6914ca 100755
--- a/lib/time/update.bash
+++ b/lib/time/update.bash
@@ -8,8 +8,8 @@
# Consult https://www.iana.org/time-zones for the latest versions.
# Versions to use.
-CODE=2021a
-DATA=2021a
+CODE=2021e
+DATA=2021e
set -e
rm -rf work
diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip
index d32fbba5175174b72ca42df853941f5a7122fad6..a859b4113b75fdc2f6e1685a13a80a85bffc03bd 100644
Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ
diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go
index 3af2bee5839b937aff5d724317e5b68df2259b06..168ebe88a2241340c8ecae41d5c3f85401bb7101 100644
--- a/misc/android/go_android_exec.go
+++ b/misc/android/go_android_exec.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
// This program can be used as go_android_GOARCH_exec by the Go tool.
diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go
index 68a30a44fe427d434df9dc634f702fc38552aa5e..e90ed1e058d83d7318b3ebaadf2c81f5dfaef7ce 100644
--- a/misc/cgo/errors/errors_test.go
+++ b/misc/cgo/errors/errors_test.go
@@ -36,14 +36,13 @@ func check(t *testing.T, file string) {
continue
}
- frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 2)
- if len(frags) == 1 {
+ _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: "))
+ if !ok {
continue
}
- frag := fmt.Sprintf(":%d:.*%s", i+1, frags[1])
- re, err := regexp.Compile(frag)
+ re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag))
if err != nil {
- t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1])
+ t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag)
continue
}
errors = append(errors, re)
diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go
index a90598fe35b6304434bba7253c29b1d62173dcc4..aa941584c3c2cfcaa7491f00733c4aa0244d674f 100644
--- a/misc/cgo/errors/testdata/err2.go
+++ b/misc/cgo/errors/testdata/err2.go
@@ -91,10 +91,18 @@ func main() {
// issue 26745
_ = func(i int) int {
- return C.i + 1 // ERROR HERE: 14
+ // typecheck reports at column 14 ('+'), but types2 reports at
+ // column 10 ('C').
+ // TODO(mdempsky): Investigate why, and see if types2 can be
+ // updated to match typecheck behavior.
+ return C.i + 1 // ERROR HERE: \b(10|14)\b
}
_ = func(i int) {
- C.fi(i) // ERROR HERE: 7
+ // typecheck reports at column 7 ('('), but types2 reports at
+ // column 8 ('i'). The types2 position is more correct, but
+ // updating typecheck here is fundamentally challenging because of
+ // IR limitations.
+ C.fi(i) // ERROR HERE: \b(7|8)\b
}
C.fi = C.fi // ERROR HERE
diff --git a/misc/cgo/gmp/fib.go b/misc/cgo/gmp/fib.go
index f1091b1c54f45e62187aa6a3776507a0af8e851c..f453fcf1843b7c13123398a212c0d8cc64935817 100644
--- a/misc/cgo/gmp/fib.go
+++ b/misc/cgo/gmp/fib.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
// Compute Fibonacci numbers with two goroutines
diff --git a/misc/cgo/gmp/pi.go b/misc/cgo/gmp/pi.go
index d5851e8e6bd389a0049d0369f960f1fc4014e090..5ea034900a9b172a92fc6b716135f6450c4f5767 100644
--- a/misc/cgo/gmp/pi.go
+++ b/misc/cgo/gmp/pi.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index 143f23f0e0cc36983ad44595469b5b7f55ff0d56..774277e10da6a080cfa9040f11ac5692459d5e9d 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -59,7 +59,9 @@ func Test28896(t *testing.T) { test28896(t) }
func Test30065(t *testing.T) { test30065(t) }
func Test32579(t *testing.T) { test32579(t) }
func Test31891(t *testing.T) { test31891(t) }
+func Test42018(t *testing.T) { test42018(t) }
func Test45451(t *testing.T) { test45451(t) }
+func Test49633(t *testing.T) { test49633(t) }
func TestAlign(t *testing.T) { testAlign(t) }
func TestAtol(t *testing.T) { testAtol(t) }
func TestBlocking(t *testing.T) { testBlocking(t) }
diff --git a/misc/cgo/test/cgo_thread_lock.go b/misc/cgo/test/cgo_thread_lock.go
index b1050685182287dd563a6d302ea819e1e6fbe138..3b9ac845493cd5cfd4d9a665e56e94cab2139956 100644
--- a/misc/cgo/test/cgo_thread_lock.go
+++ b/misc/cgo/test/cgo_thread_lock.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build linux && freebsd && openbsd
// +build linux,freebsd,openbsd
package cgotest
diff --git a/misc/cgo/test/cgo_unix_test.go b/misc/cgo/test/cgo_unix_test.go
index e3d591664983befc0ef8b6bfe3f251f352f491d5..a324503a22faf06ac261c079cdb70e30c851c446 100644
--- a/misc/cgo/test/cgo_unix_test.go
+++ b/misc/cgo/test/cgo_unix_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package cgotest
diff --git a/misc/cgo/test/issue1435.go b/misc/cgo/test/issue1435.go
index 92c6b998465caf39aeb7aebe1d39f266542de0f1..91db155c90b9836eb38042725612b9737512c28f 100644
--- a/misc/cgo/test/issue1435.go
+++ b/misc/cgo/test/issue1435.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build linux && cgo
// +build linux,cgo
package cgotest
diff --git a/misc/cgo/test/issue18146.go b/misc/cgo/test/issue18146.go
index f92d6c7f939d22078a428f4ebfc8a80541cd0fdc..e50f9ae53016fce2f88e1b0118145635f41c19d5 100644
--- a/misc/cgo/test/issue18146.go
+++ b/misc/cgo/test/issue18146.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
// Issue 18146: pthread_create failure during syscall.Exec.
diff --git a/misc/cgo/test/issue21897.go b/misc/cgo/test/issue21897.go
index d13246bd84afdea8995c2e1ab2b5a2c69c2cc4ca..8f39252e688580af7bcd5525426cdd43dc7725a3 100644
--- a/misc/cgo/test/issue21897.go
+++ b/misc/cgo/test/issue21897.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build darwin && cgo && !internal
// +build darwin,cgo,!internal
package cgotest
diff --git a/misc/cgo/test/issue21897b.go b/misc/cgo/test/issue21897b.go
index 08b5f4d808e240853d6b666ffd41ba2b5192d75f..50aece3528947ba2f8bd8ba2858628ffda1bf3fa 100644
--- a/misc/cgo/test/issue21897b.go
+++ b/misc/cgo/test/issue21897b.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !darwin || !cgo || internal
// +build !darwin !cgo internal
package cgotest
diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go
index b2d131833a9377ac3643893b5aaa92cff4a179db..90ca08cbfb7da8b17d4b128aaf471a8dd546772f 100644
--- a/misc/cgo/test/issue4029.go
+++ b/misc/cgo/test/issue4029.go
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build !windows,!static
+//go:build !windows && !static && (!darwin || (!internal_pie && !arm64))
+// +build !windows
+// +build !static
// +build !darwin !internal_pie,!arm64
// Excluded in darwin internal linking PIE mode, as dynamic export is not
diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go
index b969bdd0fe8702a74ddfdf8bd72dfde97abe46ca..c2f59485e490d6aec158fddda918bfddf618253e 100644
--- a/misc/cgo/test/issue4029w.go
+++ b/misc/cgo/test/issue4029w.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build windows || static || (darwin && internal_pie) || (darwin && arm64)
// +build windows static darwin,internal_pie darwin,arm64
package cgotest
diff --git a/misc/cgo/test/issue42018.go b/misc/cgo/test/issue42018.go
new file mode 100644
index 0000000000000000000000000000000000000000..fab686a678320cb82cf6a40b8cde2e1b2eb5866b
--- /dev/null
+++ b/misc/cgo/test/issue42018.go
@@ -0,0 +1,14 @@
+// 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.
+
+//go:build !windows
+// +build !windows
+
+package cgotest
+
+import "testing"
+
+func test42018(t *testing.T) {
+ t.Skip("skipping Windows-only test")
+}
diff --git a/misc/cgo/test/issue42018_windows.go b/misc/cgo/test/issue42018_windows.go
new file mode 100644
index 0000000000000000000000000000000000000000..8f4570ab2a5915cb3c4a015e5e8007901da6d14c
--- /dev/null
+++ b/misc/cgo/test/issue42018_windows.go
@@ -0,0 +1,46 @@
+// 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 cgotest
+
+/*
+typedef void *HANDLE;
+
+struct HWND__{int unused;}; typedef struct HWND__ *HWND;
+*/
+import "C"
+
+import (
+ "testing"
+ "unsafe"
+)
+
+func test42018(t *testing.T) {
+ // Test that Windows handles are marked go:notinheap, by growing the
+ // stack and checking for pointer adjustments. Trick from
+ // test/fixedbugs/issue40954.go.
+ var i int
+ handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
+ recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i)))
+ hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
+ recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i)))
+}
+
+func recurseHANDLE(n int, p C.HANDLE, v uintptr) {
+ if n > 0 {
+ recurseHANDLE(n-1, p, v)
+ }
+ if uintptr(unsafe.Pointer(p)) != v {
+ panic("adjusted notinheap pointer")
+ }
+}
+
+func recurseHWND(n int, p C.HWND, v uintptr) {
+ if n > 0 {
+ recurseHWND(n-1, p, v)
+ }
+ if uintptr(unsafe.Pointer(p)) != v {
+ panic("adjusted notinheap pointer")
+ }
+}
diff --git a/misc/cgo/test/issue6997_linux.go b/misc/cgo/test/issue6997_linux.go
index f19afb8b7ad81610520a3f8f5f69a3b5d8d0a746..4acc8c1a07062710cb33075dc5730d5d67e695bf 100644
--- a/misc/cgo/test/issue6997_linux.go
+++ b/misc/cgo/test/issue6997_linux.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !android
// +build !android
// Test that pthread_cancel works as expected
diff --git a/misc/cgo/test/issue8517.go b/misc/cgo/test/issue8517.go
index 4e431df921d1e33d4ca79faf9ea26e302a4aa6d9..7316ab0335d5cb587a50c90fa83ede25db8d649b 100644
--- a/misc/cgo/test/issue8517.go
+++ b/misc/cgo/test/issue8517.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package cgotest
diff --git a/misc/cgo/test/issue8694.go b/misc/cgo/test/issue8694.go
index 89be7ea090763a909e810bbcff1ebf4a67da6986..19071ce1595ec74a4f588ac9e274960dc0693337 100644
--- a/misc/cgo/test/issue8694.go
+++ b/misc/cgo/test/issue8694.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !android
// +build !android
package cgotest
diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go
index 034cc4b3719150c379d97c0f1efa9bc7ce4747c9..6b371897a73471812c674d44c13d07fcc738c8ed 100644
--- a/misc/cgo/test/sigaltstack.go
+++ b/misc/cgo/test/sigaltstack.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows && !android
// +build !windows,!android
// Test that the Go runtime still works if C code changes the signal stack.
diff --git a/misc/cgo/test/sigprocmask.go b/misc/cgo/test/sigprocmask.go
index e2b939f05e2b4b61166f180ccd4548549765e67a..983734cc7b620c16e9a55d4449a8b829e5667d27 100644
--- a/misc/cgo/test/sigprocmask.go
+++ b/misc/cgo/test/sigprocmask.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package cgotest
diff --git a/misc/cgo/test/test.go b/misc/cgo/test/test.go
index 3b8f548b13dd206fedd0653d8472c5b8d9958d12..dd81f770a2070c299a8ca4343bcd3dacb0ff0f8c 100644
--- a/misc/cgo/test/test.go
+++ b/misc/cgo/test/test.go
@@ -915,6 +915,11 @@ void issue40494(enum Enum40494 e, union Union40494* up) {}
// Issue 45451, bad handling of go:notinheap types.
typedef struct issue45451Undefined issue45451;
+
+// Issue 49633, example of cgo.Handle with void*.
+extern void GoFunc49633(void*);
+void cfunc49633(void *context) { GoFunc49633(context); }
+
*/
import "C"
diff --git a/misc/cgo/test/test_unix.go b/misc/cgo/test/test_unix.go
index 4a234469dbc6e0d826392b85447893d734fdaa5d..831b9ca625a1fbb354c30fa5da3c7358b7cc45ba 100644
--- a/misc/cgo/test/test_unix.go
+++ b/misc/cgo/test/test_unix.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package cgotest
diff --git a/misc/cgo/test/testdata/issue43639.go b/misc/cgo/test/testdata/issue43639.go
new file mode 100644
index 0000000000000000000000000000000000000000..e755fbd4bc005f4f8289bfa726559f2cb4d8c07f
--- /dev/null
+++ b/misc/cgo/test/testdata/issue43639.go
@@ -0,0 +1,9 @@
+// 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 cgotest
+
+// Issue 43639: No runtime test needed, make sure package cgotest/issue43639 compiles well.
+
+import _ "cgotest/issue43639"
diff --git a/misc/cgo/test/testdata/issue43639/a.go b/misc/cgo/test/testdata/issue43639/a.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe37d5e4b0f00d2feb9b4ddaccea6cbfc91d9876
--- /dev/null
+++ b/misc/cgo/test/testdata/issue43639/a.go
@@ -0,0 +1,8 @@
+// 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 issue43639
+
+// #cgo CFLAGS: -W -Wall -Werror
+import "C"
diff --git a/misc/cgo/test/testdata/issue9400_linux.go b/misc/cgo/test/testdata/issue9400_linux.go
index e94a9bb45f5def7cf41e7a6f3d0ae05c45037726..051b9ab0bbe526baf56dab936680434d4eb740e0 100644
--- a/misc/cgo/test/testdata/issue9400_linux.go
+++ b/misc/cgo/test/testdata/issue9400_linux.go
@@ -15,6 +15,7 @@ import "C"
import (
"runtime"
+ "runtime/debug"
"sync/atomic"
"testing"
@@ -46,6 +47,14 @@ func test9400(t *testing.T) {
big[i] = pattern
}
+ // Disable GC for the duration of the test.
+ // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too. See #49695.
+ runtime.GC()
+
// Temporarily rewind the stack and trigger SIGSETXID
issue9400.RewindAndSetgid()
diff --git a/misc/cgo/test/testx.go b/misc/cgo/test/testx.go
index 823c3e13d2927aac7bfd240ddd46b69882a8aff0..8ec84a8b22e01bcb6b6d78bd16f92d74e6d7cb7d 100644
--- a/misc/cgo/test/testx.go
+++ b/misc/cgo/test/testx.go
@@ -113,6 +113,7 @@ typedef struct {
int i;
} Issue38408, *PIssue38408;
+extern void cfunc49633(void*); // definition is in test.go
*/
import "C"
@@ -554,3 +555,26 @@ func GoFunc37033(handle C.uintptr_t) {
// A typedef pointer can be used as the element type.
// No runtime test; just make sure it compiles.
var _ C.PIssue38408 = &C.Issue38408{i: 1}
+
+// issue 49633, example use of cgo.Handle with void*
+
+type data49633 struct {
+ msg string
+}
+
+//export GoFunc49633
+func GoFunc49633(context unsafe.Pointer) {
+ h := *(*cgo.Handle)(context)
+ v := h.Value().(*data49633)
+ v.msg = "hello"
+}
+
+func test49633(t *testing.T) {
+ v := &data49633{}
+ h := cgo.NewHandle(v)
+ defer h.Delete()
+ C.cfunc49633(unsafe.Pointer(&h))
+ if v.msg != "hello" {
+ t.Errorf("msg = %q, want 'hello'", v.msg)
+ }
+}
diff --git a/misc/cgo/test/typeparam.go b/misc/cgo/test/typeparam.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f766c2bcb93ab35acf093e8070d0193f664683d
--- /dev/null
+++ b/misc/cgo/test/typeparam.go
@@ -0,0 +1,17 @@
+// 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 cgotest
+
+// #include
+import "C"
+
+func generic[T, U any](t T, u U) {}
+
+func useGeneric() {
+ const zero C.size_t = 0
+
+ generic(zero, zero)
+ generic[C.size_t, C.size_t](0, 0)
+}
diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go
index 55be3c5f70710ffe3f1847ac9a545790cdf0d501..a2b43bb72d8d3a351d9c86f715d736d390f42bc5 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 0000000000000000000000000000000000000000..718418ecb8765f297fdb7074ef6624295e2cb070
--- /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 0000000000000000000000000000000000000000..95fb7a349e145086ddd0a411f70a2d858758d63d
--- /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/cshared_test.go b/misc/cgo/testcshared/cshared_test.go
index 19ad8c76a838b54bad64c35635216088ff40db80..13ec8761e86d957f91e06dd0417fe7acea34546b 100644
--- a/misc/cgo/testcshared/cshared_test.go
+++ b/misc/cgo/testcshared/cshared_test.go
@@ -200,7 +200,7 @@ func adbRun(t *testing.T, env []string, adbargs ...string) string {
args := append(adbCmd(), "exec-out")
// Propagate LD_LIBRARY_PATH to the adb shell invocation.
for _, e := range env {
- if strings.Index(e, "LD_LIBRARY_PATH=") != -1 {
+ if strings.Contains(e, "LD_LIBRARY_PATH=") {
adbargs = append([]string{e}, adbargs...)
break
}
@@ -326,7 +326,7 @@ func createHeaders() error {
base, name := filepath.Split(args[0])
args[0] = filepath.Join(base, "llvm-dlltool")
var machine string
- switch strings.SplitN(name, "-", 2)[0] {
+ switch prefix, _, _ := strings.Cut(name, "-"); prefix {
case "i686":
machine = "i386"
case "x86_64":
@@ -781,10 +781,10 @@ func copyFile(t *testing.T, dst, src string) {
func TestGo2C2Go(t *testing.T) {
switch GOOS {
- case "darwin", "ios":
- // Darwin shared libraries don't support the multiple
+ case "darwin", "ios", "windows":
+ // Non-ELF shared libraries don't support the multiple
// copies of the runtime package implied by this test.
- t.Skip("linking c-shared into Go programs not supported on Darwin; issue 29061")
+ t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS)
case "android":
t.Skip("test fails on android; issue 29087")
}
diff --git a/misc/cgo/testcshared/testdata/libgo2/dup2.go b/misc/cgo/testcshared/testdata/libgo2/dup2.go
index d18f0b130d3f767671e4030fd34056c85c4bd132..d343aa54d9a8779450092461213c8d748d26fb84 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 c9c65a6e3c1f62be51add7bc6c129cb92dabe2b3..459f0dc196874fbf53db87bf2d976f21d18c4043 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/testgodefs/testdata/issue48396.go b/misc/cgo/testgodefs/testdata/issue48396.go
new file mode 100644
index 0000000000000000000000000000000000000000..d4c192403fde5058e55eec55475896775bc7bdaa
--- /dev/null
+++ b/misc/cgo/testgodefs/testdata/issue48396.go
@@ -0,0 +1,18 @@
+// 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.
+//
+// +build ignore
+
+package main
+
+/*
+// from
+struct issue48396 {
+ int fd;
+ int bpf_fd;
+};
+*/
+import "C"
+
+type Issue48396 C.struct_issue48396
diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go
index 4a3f6a701cc0e7e1a6a348afdbf72407b64a8ca1..5c670f3d329c1acc2e2788c8024cd2233ec0aa62 100644
--- a/misc/cgo/testgodefs/testdata/main.go
+++ b/misc/cgo/testgodefs/testdata/main.go
@@ -28,6 +28,9 @@ var v7 = S{}
// Test that #define'd type is fully defined
var _ = issue38649{X: 0}
+// Test that prefixes do not cause duplicate field names.
+var _ = Issue48396{Fd: 1, Bpf_fd: 2}
+
func main() {
pass := true
diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go
index aae34043605d340a64c6ede42b79ce1ca4575a6e..7628ffc595b7e261ad24e502d8257f1d7494f1f1 100644
--- a/misc/cgo/testgodefs/testgodefs_test.go
+++ b/misc/cgo/testgodefs/testgodefs_test.go
@@ -25,6 +25,7 @@ var filePrefixes = []string{
"issue37621",
"issue38649",
"issue39534",
+ "issue48396",
}
func TestGoDefs(t *testing.T) {
diff --git a/misc/cgo/testplugin/plugin_test.go b/misc/cgo/testplugin/plugin_test.go
index 9697dbf7a78e3c35b2a60ed168f8a254e67e94b0..a6accc1dfbba9e76cd674735618b9a4f8f9793b9 100644
--- a/misc/cgo/testplugin/plugin_test.go
+++ b/misc/cgo/testplugin/plugin_test.go
@@ -265,10 +265,6 @@ func TestIssue25756(t *testing.T) {
// Test with main using -buildmode=pie with plugin for issue #43228
func TestIssue25756pie(t *testing.T) {
- if os.Getenv("GO_BUILDER_NAME") == "darwin-arm64-11_0-toothrot" {
- t.Skip("broken on darwin/arm64 builder in sharded mode; see issue 46239")
- }
-
goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
run(t, "./issue25756pie.exe")
diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dbcce2fe2881055d7a544fedcc3d69be1c8ecaf0
--- /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 384b6250e1ef168ae130f6d77a5e302341d1cd99..b776afa3e63b319c0452e7ff307960b999690fc3 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))
}
@@ -344,7 +347,7 @@ func (c *config) checkCSanitizer() (skip bool, err error) {
if os.IsNotExist(err) {
return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err)
}
- snippet := bytes.SplitN(out, []byte{'\n'}, 2)[0]
+ snippet, _, _ := bytes.Cut(out, []byte("\n"))
return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet)
}
@@ -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 0000000000000000000000000000000000000000..e60db76981815711d09929df369969130d0b3171
--- /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 0000000000000000000000000000000000000000..e35670c440a14de820f18e85e06b33aa8ad9abe0
--- /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 0000000000000000000000000000000000000000..9f6d26dd89dbce12f1e9759749544520182b9a3a
--- /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 0000000000000000000000000000000000000000..12098458ae91d27f6c9d06c316240136dd8c3edf
--- /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 0000000000000000000000000000000000000000..3d3d5a6ab1ad473cd4911d1a2f13933b7538d16b
--- /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/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index e77f84891543f584ccb5bd2aa538f0e6237e9d17..d5d018f1516a774ecc4313a7a45c9a55da548077 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -20,6 +20,7 @@ import (
"regexp"
"runtime"
"sort"
+ "strconv"
"strings"
"testing"
"time"
@@ -694,7 +695,15 @@ func requireGccgo(t *testing.T) {
if err != nil {
t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output)
}
- if string(output) < "5" {
+ dot := bytes.Index(output, []byte{'.'})
+ if dot > 0 {
+ output = output[:dot]
+ }
+ major, err := strconv.Atoi(string(output))
+ if err != nil {
+ t.Skipf("can't parse gccgo version number %s", output)
+ }
+ if major < 5 {
t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output)))
}
@@ -1033,7 +1042,7 @@ func TestGlobal(t *testing.T) {
// Run a test using -linkshared of an installed shared package.
// Issue 26400.
func TestTestInstalledShared(t *testing.T) {
- goCmd(nil, "test", "-linkshared", "-test.short", "sync/atomic")
+ goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic")
}
// Test generated pointer method with -linkshared.
@@ -1045,8 +1054,8 @@ func TestGeneratedMethod(t *testing.T) {
// Test use of shared library struct with generated hash function.
// Issue 30768.
func TestGeneratedHash(t *testing.T) {
- goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
- goCmd(nil, "test", "-linkshared", "./issue30768")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
+ goCmd(t, "test", "-linkshared", "./issue30768")
}
// Test that packages can be added not in dependency order (here a depends on b, and a adds
@@ -1070,3 +1079,11 @@ func TestIssue44031(t *testing.T) {
goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b")
goCmd(t, "run", "-linkshared", "./issue44031/main")
}
+
+// Test that we use a variable from shared libraries (which implement an
+// interface in shared libraries.). A weak reference is used in the itab
+// in main process. It can cause unreacheble panic. See issue 47873.
+func TestIssue47873(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a")
+ goCmd(t, "run", "-linkshared", "./issue47837/main")
+}
diff --git a/misc/cgo/testshared/testdata/issue47837/a/a.go b/misc/cgo/testshared/testdata/issue47837/a/a.go
new file mode 100644
index 0000000000000000000000000000000000000000..68588eda2fa0a90019274e3b448e354c39ceaf6f
--- /dev/null
+++ b/misc/cgo/testshared/testdata/issue47837/a/a.go
@@ -0,0 +1,19 @@
+// 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 a
+
+type A interface {
+ M()
+}
+
+//go:noinline
+func TheFuncWithArgA(a A) {
+ a.M()
+}
+
+type ImplA struct{}
+
+//go:noinline
+func (A *ImplA) M() {}
diff --git a/misc/cgo/testshared/testdata/issue47837/main/main.go b/misc/cgo/testshared/testdata/issue47837/main/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..77c6f3437938a48e2b6c22b4c69df99d084e1a61
--- /dev/null
+++ b/misc/cgo/testshared/testdata/issue47837/main/main.go
@@ -0,0 +1,14 @@
+// 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 (
+ "testshared/issue47837/a"
+)
+
+func main() {
+ var vara a.ImplA
+ a.TheFuncWithArgA(&vara)
+}
diff --git a/misc/cgo/testso/noso_test.go b/misc/cgo/testso/noso_test.go
index c88aebfb02a91e13bf366973494413a9bab7d47c..1014534d62cf06962c4f17b96ebc3cfe896ded29 100644
--- a/misc/cgo/testso/noso_test.go
+++ b/misc/cgo/testso/noso_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !cgo
// +build !cgo
package so_test
diff --git a/misc/cgo/testso/so_test.go b/misc/cgo/testso/so_test.go
index 2023c51f113785d14482ee7a7b3556825784e7c3..6d14e32dc6caa474d9ca85082407c50db8b5e56c 100644
--- a/misc/cgo/testso/so_test.go
+++ b/misc/cgo/testso/so_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build cgo
// +build cgo
package so_test
diff --git a/misc/cgo/testsovar/noso_test.go b/misc/cgo/testsovar/noso_test.go
index c88aebfb02a91e13bf366973494413a9bab7d47c..1014534d62cf06962c4f17b96ebc3cfe896ded29 100644
--- a/misc/cgo/testsovar/noso_test.go
+++ b/misc/cgo/testsovar/noso_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !cgo
// +build !cgo
package so_test
diff --git a/misc/cgo/testsovar/so_test.go b/misc/cgo/testsovar/so_test.go
index 2023c51f113785d14482ee7a7b3556825784e7c3..6d14e32dc6caa474d9ca85082407c50db8b5e56c 100644
--- a/misc/cgo/testsovar/so_test.go
+++ b/misc/cgo/testsovar/so_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build cgo
// +build cgo
package so_test
diff --git a/misc/cgo/testtls/tls_test.go b/misc/cgo/testtls/tls_test.go
index 3076c2d5943b5cbc1482f504e7bbbe016ac71642..a3b67c004413aafcf305109fb893dd5ed0de09c8 100644
--- a/misc/cgo/testtls/tls_test.go
+++ b/misc/cgo/testtls/tls_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build !windows
// +build !windows
package cgotlstest
diff --git a/misc/go.mod b/misc/go.mod
index fc9f1133a4608ae4ca0143f4a8fc4414866d9400..712a051f4573c1fb521d67cb8e96be0008eb7c89 100644
--- a/misc/go.mod
+++ b/misc/go.mod
@@ -8,4 +8,4 @@
// directory.)
module misc
-go 1.12
+go 1.18
diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh
index dca3fcc90439d636f9a11756125fc8f28ab8653d..8f7b439315bff743618ad74ad1818ed6da017e2f 100755
--- a/misc/ios/clangwrap.sh
+++ b/misc/ios/clangwrap.sh
@@ -17,4 +17,4 @@ export IPHONEOS_DEPLOYMENT_TARGET=5.1
# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang.
CLANG=`xcrun --sdk $SDK --find clang`
-exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=10.0 "$@"
+exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@"
diff --git a/misc/ios/detect.go b/misc/ios/detect.go
index cde57238923be6b959a3ac6c9341903416cbd17e..1cb8ae5ff711cc1548a825ba0f5706a6fedd26c0 100644
--- a/misc/ios/detect.go
+++ b/misc/ios/detect.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
// detect attempts to autodetect the correct
diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go
index 9e63717d9214d6eb98726bb21d9f2c993fded2ac..34a734cda780e3112fd4a15c4befca692a3ec0a2 100644
--- a/misc/ios/go_ios_exec.go
+++ b/misc/ios/go_ios_exec.go
@@ -148,9 +148,8 @@ func runOnDevice(appdir string) error {
// Device IDs as listed with ios-deploy -c.
deviceID = os.Getenv("GOIOS_DEVICE_ID")
- parts := strings.SplitN(appID, ".", 2)
- if len(parts) == 2 {
- bundleID = parts[1]
+ if _, id, ok := strings.Cut(appID, "."); ok {
+ bundleID = id
}
if err := signApp(appdir); err != nil {
@@ -291,11 +290,10 @@ func findDevImage() (string, error) {
var iosVer, buildVer string
lines := bytes.Split(out, []byte("\n"))
for _, line := range lines {
- spl := bytes.SplitN(line, []byte(": "), 2)
- if len(spl) != 2 {
+ key, val, ok := strings.Cut(string(line), ": ")
+ if !ok {
continue
}
- key, val := string(spl[0]), string(spl[1])
switch key {
case "ProductVersion":
iosVer = val
diff --git a/misc/linkcheck/linkcheck.go b/misc/linkcheck/linkcheck.go
index 570b430da4f1cbc76f14250822406e5449978757..efe400965b2e06c7bdb9a02b5fd7d38d72cf07e6 100644
--- a/misc/linkcheck/linkcheck.go
+++ b/misc/linkcheck/linkcheck.go
@@ -81,10 +81,8 @@ func crawl(url string, sourceURL string) {
}
mu.Lock()
defer mu.Unlock()
- var frag string
- if i := strings.Index(url, "#"); i >= 0 {
- frag = url[i+1:]
- url = url[:i]
+ if u, frag, ok := strings.Cut(url, "#"); ok {
+ url = u
if frag != "" {
uf := urlFrag{url, frag}
neededFrags[uf] = append(neededFrags[uf], sourceURL)
diff --git a/misc/reboot/experiment_toolid_test.go b/misc/reboot/experiment_toolid_test.go
index 4f40284d80f107dd0014d1bfc1171d2a78ac05ef..87a828e32f73a40a1a0a64a31dd35bfc906139fd 100644
--- a/misc/reboot/experiment_toolid_test.go
+++ b/misc/reboot/experiment_toolid_test.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build explicit
// +build explicit
// Package experiment_toolid_test verifies that GOEXPERIMENT settings built
diff --git a/misc/reboot/reboot_test.go b/misc/reboot/reboot_test.go
index 6bafc608b5e234a051c7a19c7e4282cea7297f21..ef164d32327bec41cf719d4b5bc39ed2238fb367 100644
--- a/misc/reboot/reboot_test.go
+++ b/misc/reboot/reboot_test.go
@@ -15,6 +15,10 @@ import (
)
func TestRepeatBootstrap(t *testing.T) {
+ if testing.Short() {
+ t.Skipf("skipping test that rebuilds the entire toolchain")
+ }
+
goroot, err := os.MkdirTemp("", "reboot-goroot")
if err != nil {
t.Fatal(err)
diff --git a/misc/wasm/go_js_wasm_exec b/misc/wasm/go_js_wasm_exec
index b700722dfe97e57e423e1d51f50c24d778d5fc5f..fcbd0e4fc8ce0e4eb3a753289b0a8283327b3bdd 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 231185a123ed12f8bc6b2ad7a56a00e3615a656a..9ce6a20c3ffa82debd6853a72f02c316ccfb642d 100644
--- a/misc/wasm/wasm_exec.js
+++ b/misc/wasm/wasm_exec.js
@@ -2,47 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-(() => {
- // 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;
- }
- }
+"use strict";
+(() => {
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);
@@ -87,8 +58,8 @@
};
}
- if (!global.process) {
- global.process = {
+ if (!globalThis.process) {
+ globalThis.process = {
getuid() { return -1; },
getgid() { return -1; },
geteuid() { return -1; },
@@ -102,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 (!global.TextDecoder) {
- throw new Error("global.TextDecoder is not available, polyfill required");
+ if (!globalThis.TextDecoder) {
+ throw new Error("globalThis.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 = {};
@@ -517,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
@@ -526,7 +476,7 @@
[null, 2],
[true, 3],
[false, 4],
- [global, 5],
+ [globalThis, 5],
[this, 6],
]);
this._idPool = []; // unused ids that have been garbage collected
@@ -567,6 +517,13 @@
offset += 8;
});
+ // The linker guarantees global data starts from at least wasmMinDataAddr.
+ // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
+ const wasmMinDataAddr = 4096 + 8192;
+ if (offset >= wasmMinDataAddr) {
+ throw new Error("total length of command line and environment variables exceeds limit");
+ }
+
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
@@ -594,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 0000000000000000000000000000000000000000..f9200ca95021e93e1305eb9b9521faa3e8acc06b
--- /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/common.go b/src/archive/tar/common.go
index c667cfc8720b5b9db1a72184bd9fb42461916ec0..c99b5c19207f71f4c93455df42adb44cf6c63c69 100644
--- a/src/archive/tar/common.go
+++ b/src/archive/tar/common.go
@@ -316,10 +316,10 @@ func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
// fileState tracks the number of logical (includes sparse holes) and physical
// (actual in tar archive) bytes remaining for the current file.
//
-// Invariant: LogicalRemaining >= PhysicalRemaining
+// Invariant: logicalRemaining >= physicalRemaining
type fileState interface {
- LogicalRemaining() int64
- PhysicalRemaining() int64
+ logicalRemaining() int64
+ physicalRemaining() int64
}
// allowedFormats determines which formats can be used.
@@ -413,22 +413,22 @@ func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err
// Check basic fields.
var blk block
- v7 := blk.V7()
- ustar := blk.USTAR()
- gnu := blk.GNU()
- verifyString(h.Name, len(v7.Name()), "Name", paxPath)
- verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath)
- verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname)
- verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname)
- verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone)
- verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid)
- verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid)
- verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize)
- verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone)
- verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone)
- verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime)
- verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime)
- verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime)
+ v7 := blk.toV7()
+ ustar := blk.toUSTAR()
+ gnu := blk.toGNU()
+ verifyString(h.Name, len(v7.name()), "Name", paxPath)
+ verifyString(h.Linkname, len(v7.linkName()), "Linkname", paxLinkpath)
+ verifyString(h.Uname, len(ustar.userName()), "Uname", paxUname)
+ verifyString(h.Gname, len(ustar.groupName()), "Gname", paxGname)
+ verifyNumeric(h.Mode, len(v7.mode()), "Mode", paxNone)
+ verifyNumeric(int64(h.Uid), len(v7.uid()), "Uid", paxUid)
+ verifyNumeric(int64(h.Gid), len(v7.gid()), "Gid", paxGid)
+ verifyNumeric(h.Size, len(v7.size()), "Size", paxSize)
+ verifyNumeric(h.Devmajor, len(ustar.devMajor()), "Devmajor", paxNone)
+ verifyNumeric(h.Devminor, len(ustar.devMinor()), "Devminor", paxNone)
+ verifyTime(h.ModTime, len(v7.modTime()), "ModTime", paxMtime)
+ verifyTime(h.AccessTime, len(gnu.accessTime()), "AccessTime", paxAtime)
+ verifyTime(h.ChangeTime, len(gnu.changeTime()), "ChangeTime", paxCtime)
// Check for header-only types.
var whyOnlyPAX, whyOnlyGNU string
@@ -538,7 +538,7 @@ type headerFileInfo struct {
func (fi headerFileInfo) Size() int64 { return fi.h.Size }
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
-func (fi headerFileInfo) Sys() interface{} { return fi.h }
+func (fi headerFileInfo) Sys() any { return fi.h }
// Name returns the base name of the file.
func (fi headerFileInfo) Name() string {
diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go
index cfe24a5e1d339540c26feb898bfa6ce09d1813d8..21b9d9d4dbc628f7f18a370d7bb10f597b0efc70 100644
--- a/src/archive/tar/format.go
+++ b/src/archive/tar/format.go
@@ -156,28 +156,28 @@ var zeroBlock block
type block [blockSize]byte
// Convert block to any number of formats.
-func (b *block) V7() *headerV7 { return (*headerV7)(b) }
-func (b *block) GNU() *headerGNU { return (*headerGNU)(b) }
-func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) }
-func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) }
-func (b *block) Sparse() sparseArray { return sparseArray(b[:]) }
+func (b *block) toV7() *headerV7 { return (*headerV7)(b) }
+func (b *block) toGNU() *headerGNU { return (*headerGNU)(b) }
+func (b *block) toSTAR() *headerSTAR { return (*headerSTAR)(b) }
+func (b *block) toUSTAR() *headerUSTAR { return (*headerUSTAR)(b) }
+func (b *block) toSparse() sparseArray { return sparseArray(b[:]) }
// GetFormat checks that the block is a valid tar header based on the checksum.
// It then attempts to guess the specific format based on magic values.
// If the checksum fails, then FormatUnknown is returned.
-func (b *block) GetFormat() Format {
+func (b *block) getFormat() Format {
// Verify checksum.
var p parser
- value := p.parseOctal(b.V7().Chksum())
- chksum1, chksum2 := b.ComputeChecksum()
+ value := p.parseOctal(b.toV7().chksum())
+ chksum1, chksum2 := b.computeChecksum()
if p.err != nil || (value != chksum1 && value != chksum2) {
return FormatUnknown
}
// Guess the magic values.
- magic := string(b.USTAR().Magic())
- version := string(b.USTAR().Version())
- trailer := string(b.STAR().Trailer())
+ magic := string(b.toUSTAR().magic())
+ version := string(b.toUSTAR().version())
+ trailer := string(b.toSTAR().trailer())
switch {
case magic == magicUSTAR && trailer == trailerSTAR:
return formatSTAR
@@ -190,23 +190,23 @@ func (b *block) GetFormat() Format {
}
}
-// SetFormat writes the magic values necessary for specified format
+// setFormat writes the magic values necessary for specified format
// and then updates the checksum accordingly.
-func (b *block) SetFormat(format Format) {
+func (b *block) setFormat(format Format) {
// Set the magic values.
switch {
case format.has(formatV7):
// Do nothing.
case format.has(FormatGNU):
- copy(b.GNU().Magic(), magicGNU)
- copy(b.GNU().Version(), versionGNU)
+ copy(b.toGNU().magic(), magicGNU)
+ copy(b.toGNU().version(), versionGNU)
case format.has(formatSTAR):
- copy(b.STAR().Magic(), magicUSTAR)
- copy(b.STAR().Version(), versionUSTAR)
- copy(b.STAR().Trailer(), trailerSTAR)
+ copy(b.toSTAR().magic(), magicUSTAR)
+ copy(b.toSTAR().version(), versionUSTAR)
+ copy(b.toSTAR().trailer(), trailerSTAR)
case format.has(FormatUSTAR | FormatPAX):
- copy(b.USTAR().Magic(), magicUSTAR)
- copy(b.USTAR().Version(), versionUSTAR)
+ copy(b.toUSTAR().magic(), magicUSTAR)
+ copy(b.toUSTAR().version(), versionUSTAR)
default:
panic("invalid format")
}
@@ -214,17 +214,17 @@ func (b *block) SetFormat(format Format) {
// Update checksum.
// This field is special in that it is terminated by a NULL then space.
var f formatter
- field := b.V7().Chksum()
- chksum, _ := b.ComputeChecksum() // Possible values are 256..128776
+ field := b.toV7().chksum()
+ chksum, _ := b.computeChecksum() // Possible values are 256..128776
f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143
field[7] = ' '
}
-// ComputeChecksum computes the checksum for the header block.
+// computeChecksum computes the checksum for the header block.
// POSIX specifies a sum of the unsigned byte values, but the Sun tar used
// signed byte values.
// We compute and return both.
-func (b *block) ComputeChecksum() (unsigned, signed int64) {
+func (b *block) computeChecksum() (unsigned, signed int64) {
for i, c := range b {
if 148 <= i && i < 156 {
c = ' ' // Treat the checksum field itself as all spaces.
@@ -236,68 +236,68 @@ func (b *block) ComputeChecksum() (unsigned, signed int64) {
}
// Reset clears the block with all zeros.
-func (b *block) Reset() {
+func (b *block) reset() {
*b = block{}
}
type headerV7 [blockSize]byte
-func (h *headerV7) Name() []byte { return h[000:][:100] }
-func (h *headerV7) Mode() []byte { return h[100:][:8] }
-func (h *headerV7) UID() []byte { return h[108:][:8] }
-func (h *headerV7) GID() []byte { return h[116:][:8] }
-func (h *headerV7) Size() []byte { return h[124:][:12] }
-func (h *headerV7) ModTime() []byte { return h[136:][:12] }
-func (h *headerV7) Chksum() []byte { return h[148:][:8] }
-func (h *headerV7) TypeFlag() []byte { return h[156:][:1] }
-func (h *headerV7) LinkName() []byte { return h[157:][:100] }
+func (h *headerV7) name() []byte { return h[000:][:100] }
+func (h *headerV7) mode() []byte { return h[100:][:8] }
+func (h *headerV7) uid() []byte { return h[108:][:8] }
+func (h *headerV7) gid() []byte { return h[116:][:8] }
+func (h *headerV7) size() []byte { return h[124:][:12] }
+func (h *headerV7) modTime() []byte { return h[136:][:12] }
+func (h *headerV7) chksum() []byte { return h[148:][:8] }
+func (h *headerV7) typeFlag() []byte { return h[156:][:1] }
+func (h *headerV7) linkName() []byte { return h[157:][:100] }
type headerGNU [blockSize]byte
-func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) }
-func (h *headerGNU) Magic() []byte { return h[257:][:6] }
-func (h *headerGNU) Version() []byte { return h[263:][:2] }
-func (h *headerGNU) UserName() []byte { return h[265:][:32] }
-func (h *headerGNU) GroupName() []byte { return h[297:][:32] }
-func (h *headerGNU) DevMajor() []byte { return h[329:][:8] }
-func (h *headerGNU) DevMinor() []byte { return h[337:][:8] }
-func (h *headerGNU) AccessTime() []byte { return h[345:][:12] }
-func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] }
-func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) }
-func (h *headerGNU) RealSize() []byte { return h[483:][:12] }
+func (h *headerGNU) v7() *headerV7 { return (*headerV7)(h) }
+func (h *headerGNU) magic() []byte { return h[257:][:6] }
+func (h *headerGNU) version() []byte { return h[263:][:2] }
+func (h *headerGNU) userName() []byte { return h[265:][:32] }
+func (h *headerGNU) groupName() []byte { return h[297:][:32] }
+func (h *headerGNU) devMajor() []byte { return h[329:][:8] }
+func (h *headerGNU) devMinor() []byte { return h[337:][:8] }
+func (h *headerGNU) accessTime() []byte { return h[345:][:12] }
+func (h *headerGNU) changeTime() []byte { return h[357:][:12] }
+func (h *headerGNU) sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) }
+func (h *headerGNU) realSize() []byte { return h[483:][:12] }
type headerSTAR [blockSize]byte
-func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) }
-func (h *headerSTAR) Magic() []byte { return h[257:][:6] }
-func (h *headerSTAR) Version() []byte { return h[263:][:2] }
-func (h *headerSTAR) UserName() []byte { return h[265:][:32] }
-func (h *headerSTAR) GroupName() []byte { return h[297:][:32] }
-func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] }
-func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] }
-func (h *headerSTAR) Prefix() []byte { return h[345:][:131] }
-func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] }
-func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] }
-func (h *headerSTAR) Trailer() []byte { return h[508:][:4] }
+func (h *headerSTAR) v7() *headerV7 { return (*headerV7)(h) }
+func (h *headerSTAR) magic() []byte { return h[257:][:6] }
+func (h *headerSTAR) version() []byte { return h[263:][:2] }
+func (h *headerSTAR) userName() []byte { return h[265:][:32] }
+func (h *headerSTAR) groupName() []byte { return h[297:][:32] }
+func (h *headerSTAR) devMajor() []byte { return h[329:][:8] }
+func (h *headerSTAR) devMinor() []byte { return h[337:][:8] }
+func (h *headerSTAR) prefix() []byte { return h[345:][:131] }
+func (h *headerSTAR) accessTime() []byte { return h[476:][:12] }
+func (h *headerSTAR) changeTime() []byte { return h[488:][:12] }
+func (h *headerSTAR) trailer() []byte { return h[508:][:4] }
type headerUSTAR [blockSize]byte
-func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) }
-func (h *headerUSTAR) Magic() []byte { return h[257:][:6] }
-func (h *headerUSTAR) Version() []byte { return h[263:][:2] }
-func (h *headerUSTAR) UserName() []byte { return h[265:][:32] }
-func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] }
-func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] }
-func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] }
-func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] }
+func (h *headerUSTAR) v7() *headerV7 { return (*headerV7)(h) }
+func (h *headerUSTAR) magic() []byte { return h[257:][:6] }
+func (h *headerUSTAR) version() []byte { return h[263:][:2] }
+func (h *headerUSTAR) userName() []byte { return h[265:][:32] }
+func (h *headerUSTAR) groupName() []byte { return h[297:][:32] }
+func (h *headerUSTAR) devMajor() []byte { return h[329:][:8] }
+func (h *headerUSTAR) devMinor() []byte { return h[337:][:8] }
+func (h *headerUSTAR) prefix() []byte { return h[345:][:155] }
type sparseArray []byte
-func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) }
-func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] }
-func (s sparseArray) MaxEntries() int { return len(s) / 24 }
+func (s sparseArray) entry(i int) sparseElem { return sparseElem(s[i*24:]) }
+func (s sparseArray) isExtended() []byte { return s[24*s.maxEntries():][:1] }
+func (s sparseArray) maxEntries() int { return len(s) / 24 }
type sparseElem []byte
-func (s sparseElem) Offset() []byte { return s[00:][:12] }
-func (s sparseElem) Length() []byte { return s[12:][:12] }
+func (s sparseElem) offset() []byte { return s[00:][:12] }
+func (s sparseElem) length() []byte { return s[12:][:12] }
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 1b1d5b46891b6bc972ed6bd9bfb1efc63eef1433..4b11909bc9527d37af905cba8c33f495b83893ca 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -65,7 +65,7 @@ func (tr *Reader) next() (*Header, error) {
format := FormatUSTAR | FormatPAX | FormatGNU
for {
// Discard the remainder of the file and any padding.
- if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil {
+ if err := discard(tr.r, tr.curr.physicalRemaining()); err != nil {
return nil, err
}
if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil {
@@ -355,7 +355,7 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
}
// Verify the header matches a known format.
- format := tr.blk.GetFormat()
+ format := tr.blk.getFormat()
if format == FormatUnknown {
return nil, nil, ErrHeader
}
@@ -364,30 +364,30 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
hdr := new(Header)
// Unpack the V7 header.
- v7 := tr.blk.V7()
- hdr.Typeflag = v7.TypeFlag()[0]
- hdr.Name = p.parseString(v7.Name())
- hdr.Linkname = p.parseString(v7.LinkName())
- hdr.Size = p.parseNumeric(v7.Size())
- hdr.Mode = p.parseNumeric(v7.Mode())
- hdr.Uid = int(p.parseNumeric(v7.UID()))
- hdr.Gid = int(p.parseNumeric(v7.GID()))
- hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0)
+ v7 := tr.blk.toV7()
+ hdr.Typeflag = v7.typeFlag()[0]
+ hdr.Name = p.parseString(v7.name())
+ hdr.Linkname = p.parseString(v7.linkName())
+ hdr.Size = p.parseNumeric(v7.size())
+ hdr.Mode = p.parseNumeric(v7.mode())
+ hdr.Uid = int(p.parseNumeric(v7.uid()))
+ hdr.Gid = int(p.parseNumeric(v7.gid()))
+ hdr.ModTime = time.Unix(p.parseNumeric(v7.modTime()), 0)
// Unpack format specific fields.
if format > formatV7 {
- ustar := tr.blk.USTAR()
- hdr.Uname = p.parseString(ustar.UserName())
- hdr.Gname = p.parseString(ustar.GroupName())
- hdr.Devmajor = p.parseNumeric(ustar.DevMajor())
- hdr.Devminor = p.parseNumeric(ustar.DevMinor())
+ ustar := tr.blk.toUSTAR()
+ hdr.Uname = p.parseString(ustar.userName())
+ hdr.Gname = p.parseString(ustar.groupName())
+ hdr.Devmajor = p.parseNumeric(ustar.devMajor())
+ hdr.Devminor = p.parseNumeric(ustar.devMinor())
var prefix string
switch {
case format.has(FormatUSTAR | FormatPAX):
hdr.Format = format
- ustar := tr.blk.USTAR()
- prefix = p.parseString(ustar.Prefix())
+ ustar := tr.blk.toUSTAR()
+ prefix = p.parseString(ustar.prefix())
// For Format detection, check if block is properly formatted since
// the parser is more liberal than what USTAR actually permits.
@@ -396,23 +396,23 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
hdr.Format = FormatUnknown // Non-ASCII characters in block.
}
nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 }
- if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) &&
- nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) {
+ if !(nul(v7.size()) && nul(v7.mode()) && nul(v7.uid()) && nul(v7.gid()) &&
+ nul(v7.modTime()) && nul(ustar.devMajor()) && nul(ustar.devMinor())) {
hdr.Format = FormatUnknown // Numeric fields must end in NUL
}
case format.has(formatSTAR):
- star := tr.blk.STAR()
- prefix = p.parseString(star.Prefix())
- hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0)
- hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0)
+ star := tr.blk.toSTAR()
+ prefix = p.parseString(star.prefix())
+ hdr.AccessTime = time.Unix(p.parseNumeric(star.accessTime()), 0)
+ hdr.ChangeTime = time.Unix(p.parseNumeric(star.changeTime()), 0)
case format.has(FormatGNU):
hdr.Format = format
var p2 parser
- gnu := tr.blk.GNU()
- if b := gnu.AccessTime(); b[0] != 0 {
+ gnu := tr.blk.toGNU()
+ if b := gnu.accessTime(); b[0] != 0 {
hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0)
}
- if b := gnu.ChangeTime(); b[0] != 0 {
+ if b := gnu.changeTime(); b[0] != 0 {
hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0)
}
@@ -439,8 +439,8 @@ func (tr *Reader) readHeader() (*Header, *block, error) {
// See https://golang.org/issues/21005
if p2.err != nil {
hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{}
- ustar := tr.blk.USTAR()
- if s := p.parseString(ustar.Prefix()); isASCII(s) {
+ ustar := tr.blk.toUSTAR()
+ if s := p.parseString(ustar.prefix()); isASCII(s) {
prefix = s
}
hdr.Format = FormatUnknown // Buggy file is not GNU
@@ -465,38 +465,38 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err
// Make sure that the input format is GNU.
// Unfortunately, the STAR format also has a sparse header format that uses
// the same type flag but has a completely different layout.
- if blk.GetFormat() != FormatGNU {
+ if blk.getFormat() != FormatGNU {
return nil, ErrHeader
}
hdr.Format.mayOnlyBe(FormatGNU)
var p parser
- hdr.Size = p.parseNumeric(blk.GNU().RealSize())
+ hdr.Size = p.parseNumeric(blk.toGNU().realSize())
if p.err != nil {
return nil, p.err
}
- s := blk.GNU().Sparse()
- spd := make(sparseDatas, 0, s.MaxEntries())
+ s := blk.toGNU().sparse()
+ spd := make(sparseDatas, 0, s.maxEntries())
for {
- for i := 0; i < s.MaxEntries(); i++ {
+ for i := 0; i < s.maxEntries(); i++ {
// This termination condition is identical to GNU and BSD tar.
- if s.Entry(i).Offset()[0] == 0x00 {
+ if s.entry(i).offset()[0] == 0x00 {
break // Don't return, need to process extended headers (even if empty)
}
- offset := p.parseNumeric(s.Entry(i).Offset())
- length := p.parseNumeric(s.Entry(i).Length())
+ offset := p.parseNumeric(s.entry(i).offset())
+ length := p.parseNumeric(s.entry(i).length())
if p.err != nil {
return nil, p.err
}
spd = append(spd, sparseEntry{Offset: offset, Length: length})
}
- if s.IsExtended()[0] > 0 {
+ if s.isExtended()[0] > 0 {
// There are more entries. Read an extension header and parse its entries.
if _, err := mustReadFull(tr.r, blk[:]); err != nil {
return nil, err
}
- s = blk.Sparse()
+ s = blk.toSparse()
continue
}
return spd, nil // Done
@@ -678,11 +678,13 @@ func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) {
return io.Copy(w, struct{ io.Reader }{fr})
}
-func (fr regFileReader) LogicalRemaining() int64 {
+// logicalRemaining implements fileState.logicalRemaining.
+func (fr regFileReader) logicalRemaining() int64 {
return fr.nb
}
-func (fr regFileReader) PhysicalRemaining() int64 {
+// logicalRemaining implements fileState.physicalRemaining.
+func (fr regFileReader) physicalRemaining() int64 {
return fr.nb
}
@@ -694,9 +696,9 @@ type sparseFileReader struct {
}
func (sr *sparseFileReader) Read(b []byte) (n int, err error) {
- finished := int64(len(b)) >= sr.LogicalRemaining()
+ finished := int64(len(b)) >= sr.logicalRemaining()
if finished {
- b = b[:sr.LogicalRemaining()]
+ b = b[:sr.logicalRemaining()]
}
b0 := b
@@ -724,7 +726,7 @@ func (sr *sparseFileReader) Read(b []byte) (n int, err error) {
return n, errMissData // Less data in dense file than sparse file
case err != nil:
return n, err
- case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0:
+ case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0:
return n, errUnrefData // More data in dense file than sparse file
case finished:
return n, io.EOF
@@ -746,7 +748,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
var writeLastByte bool
pos0 := sr.pos
- for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil {
+ for sr.logicalRemaining() > 0 && !writeLastByte && err == nil {
var nf int64 // Size of fragment
holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset()
if sr.pos < holeStart { // In a data fragment
@@ -754,7 +756,7 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
nf, err = io.CopyN(ws, sr.fr, nf)
} else { // In a hole fragment
nf = holeEnd - sr.pos
- if sr.PhysicalRemaining() == 0 {
+ if sr.physicalRemaining() == 0 {
writeLastByte = true
nf--
}
@@ -779,18 +781,18 @@ func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) {
return n, errMissData // Less data in dense file than sparse file
case err != nil:
return n, err
- case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0:
+ case sr.logicalRemaining() == 0 && sr.physicalRemaining() > 0:
return n, errUnrefData // More data in dense file than sparse file
default:
return n, nil
}
}
-func (sr sparseFileReader) LogicalRemaining() int64 {
+func (sr sparseFileReader) logicalRemaining() int64 {
return sr.sp[len(sr.sp)-1].endOffset() - sr.pos
}
-func (sr sparseFileReader) PhysicalRemaining() int64 {
- return sr.fr.PhysicalRemaining()
+func (sr sparseFileReader) physicalRemaining() int64 {
+ return sr.fr.physicalRemaining()
}
type zeroReader struct{}
diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go
index 789ddc1bc0345ae95058f6fafa7311b54c5f6c25..f21a6065b485706f3e2c2825f1fd8c04472fa275 100644
--- a/src/archive/tar/reader_test.go
+++ b/src/archive/tar/reader_test.go
@@ -1021,12 +1021,12 @@ func TestParsePAX(t *testing.T) {
func TestReadOldGNUSparseMap(t *testing.T) {
populateSparseMap := func(sa sparseArray, sps []string) []string {
- for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ {
- copy(sa.Entry(i), sps[0])
+ for i := 0; len(sps) > 0 && i < sa.maxEntries(); i++ {
+ copy(sa.entry(i), sps[0])
sps = sps[1:]
}
if len(sps) > 0 {
- copy(sa.IsExtended(), "\x80")
+ copy(sa.isExtended(), "\x80")
}
return sps
}
@@ -1034,19 +1034,19 @@ func TestReadOldGNUSparseMap(t *testing.T) {
makeInput := func(format Format, size string, sps ...string) (out []byte) {
// Write the initial GNU header.
var blk block
- gnu := blk.GNU()
- sparse := gnu.Sparse()
- copy(gnu.RealSize(), size)
+ gnu := blk.toGNU()
+ sparse := gnu.sparse()
+ copy(gnu.realSize(), size)
sps = populateSparseMap(sparse, sps)
if format != FormatUnknown {
- blk.SetFormat(format)
+ blk.setFormat(format)
}
out = append(out, blk[:]...)
// Write extended sparse blocks.
for len(sps) > 0 {
var blk block
- sps = populateSparseMap(blk.Sparse(), sps)
+ sps = populateSparseMap(blk.toSparse(), sps)
out = append(out, blk[:]...)
}
return out
@@ -1359,11 +1359,11 @@ func TestFileReader(t *testing.T) {
wantCnt int64
wantErr error
}
- testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt
+ testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt
wantLCnt int64
wantPCnt int64
}
- testFnc interface{} // testRead | testWriteTo | testRemaining
+ testFnc any // testRead | testWriteTo | testRemaining
)
type (
@@ -1376,7 +1376,7 @@ func TestFileReader(t *testing.T) {
spd sparseDatas
size int64
}
- fileMaker interface{} // makeReg | makeSparse
+ fileMaker any // makeReg | makeSparse
)
vectors := []struct {
@@ -1596,11 +1596,11 @@ func TestFileReader(t *testing.T) {
t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
}
case testRemaining:
- if got := fr.LogicalRemaining(); got != tf.wantLCnt {
- t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
+ if got := fr.logicalRemaining(); got != tf.wantLCnt {
+ t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
}
- if got := fr.PhysicalRemaining(); got != tf.wantPCnt {
- t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
+ if got := fr.physicalRemaining(); got != tf.wantPCnt {
+ t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
}
default:
t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
diff --git a/src/archive/tar/stat_actime1.go b/src/archive/tar/stat_actime1.go
index 4fdf2a04b3df40d6f1d59c119faa9acb5029f317..c4c2480feeb0c4db635a9b48d56bf2ef9cb910b7 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 5a9a35cbb4e22d9df42f307843730032d7e0b392..f76d6be220f87f89c55e553e88b5e4737484001f 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 3957349d6ef0aa4461062c3203c7bd852d6328ed..717a0b3abc7246a50207378bbba81424ce155dcc 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/tar/strconv.go b/src/archive/tar/strconv.go
index f0b61e6dba69a4fb393c8c92c694713c9defd4d6..275db6f0263f2ce8f165c265bba65a272f951bad 100644
--- a/src/archive/tar/strconv.go
+++ b/src/archive/tar/strconv.go
@@ -14,7 +14,7 @@ import (
// hasNUL reports whether the NUL character exists within s.
func hasNUL(s string) bool {
- return strings.IndexByte(s, 0) >= 0
+ return strings.Contains(s, "\x00")
}
// isASCII reports whether the input is an ASCII C-style string.
@@ -201,10 +201,7 @@ func parsePAXTime(s string) (time.Time, error) {
const maxNanoSecondDigits = 9
// Split string into seconds and sub-seconds parts.
- ss, sn := s, ""
- if pos := strings.IndexByte(s, '.'); pos >= 0 {
- ss, sn = s[:pos], s[pos+1:]
- }
+ ss, sn, _ := strings.Cut(s, ".")
// Parse the seconds.
secs, err := strconv.ParseInt(ss, 10, 64)
@@ -254,48 +251,32 @@ func formatPAXTime(ts time.Time) (s string) {
// return the remainder as r.
func parsePAXRecord(s string) (k, v, r string, err error) {
// The size field ends at the first space.
- sp := strings.IndexByte(s, ' ')
- if sp == -1 {
+ nStr, rest, ok := strings.Cut(s, " ")
+ if !ok {
return "", "", s, ErrHeader
}
// Parse the first token as a decimal integer.
- n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int
- if perr != nil || n < 5 || int64(len(s)) < n {
+ n, perr := strconv.ParseInt(nStr, 10, 0) // Intentionally parse as native int
+ if perr != nil || n < 5 || n > int64(len(s)) {
return "", "", s, ErrHeader
}
-
- afterSpace := int64(sp + 1)
- beforeLastNewLine := n - 1
- // In some cases, "length" was perhaps padded/malformed, and
- // trying to index past where the space supposedly is goes past
- // the end of the actual record.
- // For example:
- // "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319"
- // ^ ^
- // | |
- // | afterSpace=35
- // |
- // beforeLastNewLine=29
- // yet indexOf(firstSpace) MUST BE before endOfRecord.
- //
- // See https://golang.org/issues/40196.
- if afterSpace >= beforeLastNewLine {
+ n -= int64(len(nStr) + 1) // convert from index in s to index in rest
+ if n <= 0 {
return "", "", s, ErrHeader
}
// Extract everything between the space and the final newline.
- rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:]
+ rec, nl, rem := rest[:n-1], rest[n-1:n], rest[n:]
if nl != "\n" {
return "", "", s, ErrHeader
}
// The first equals separates the key from the value.
- eq := strings.IndexByte(rec, '=')
- if eq == -1 {
+ k, v, ok = strings.Cut(rec, "=")
+ if !ok {
return "", "", s, ErrHeader
}
- k, v = rec[:eq], rec[eq+1:]
if !validPAXRecord(k, v) {
return "", "", s, ErrHeader
@@ -333,7 +314,7 @@ func formatPAXRecord(k, v string) (string, error) {
// for the PAX version of the USTAR string fields.
// The key must not contain an '=' character.
func validPAXRecord(k, v string) bool {
- if k == "" || strings.IndexByte(k, '=') >= 0 {
+ if k == "" || strings.Contains(k, "=") {
return false
}
switch k {
diff --git a/src/archive/tar/tar_test.go b/src/archive/tar/tar_test.go
index e9fafc7cc70df5cd5f6308f42c87c6d577f6dabf..a476f5eb010f21d7cc4559bf860a83aa516a0553 100644
--- a/src/archive/tar/tar_test.go
+++ b/src/archive/tar/tar_test.go
@@ -23,7 +23,7 @@ import (
type testError struct{ error }
-type fileOps []interface{} // []T where T is (string | int64)
+type fileOps []any // []T where T is (string | int64)
// testFile is an io.ReadWriteSeeker where the IO operations performed
// on it must match the list of operations in ops.
diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go
index e80498d03e39c0699a4f6d51c91fd4dda471ffa4..3729f7e82c192f685e43398a88d2777bde5c17e0 100644
--- a/src/archive/tar/writer.go
+++ b/src/archive/tar/writer.go
@@ -50,7 +50,7 @@ func (tw *Writer) Flush() error {
if tw.err != nil {
return tw.err
}
- if nb := tw.curr.LogicalRemaining(); nb > 0 {
+ if nb := tw.curr.logicalRemaining(); nb > 0 {
return fmt.Errorf("archive/tar: missed writing %d bytes", nb)
}
if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil {
@@ -117,8 +117,8 @@ func (tw *Writer) writeUSTARHeader(hdr *Header) error {
// Pack the main header.
var f formatter
blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal)
- f.formatString(blk.USTAR().Prefix(), namePrefix)
- blk.SetFormat(FormatUSTAR)
+ f.formatString(blk.toUSTAR().prefix(), namePrefix)
+ blk.setFormat(FormatUSTAR)
if f.err != nil {
return f.err // Should never happen since header is validated
}
@@ -208,7 +208,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
var f formatter // Ignore errors since they are expected
fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) }
blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal)
- blk.SetFormat(FormatPAX)
+ blk.setFormat(FormatPAX)
if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
return err
}
@@ -250,10 +250,10 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error {
var spb []byte
blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric)
if !hdr.AccessTime.IsZero() {
- f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix())
+ f.formatNumeric(blk.toGNU().accessTime(), hdr.AccessTime.Unix())
}
if !hdr.ChangeTime.IsZero() {
- f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix())
+ f.formatNumeric(blk.toGNU().changeTime(), hdr.ChangeTime.Unix())
}
// TODO(dsnet): Re-enable this when adding sparse support.
// See https://golang.org/issue/22735
@@ -293,7 +293,7 @@ func (tw *Writer) writeGNUHeader(hdr *Header) error {
f.formatNumeric(blk.GNU().RealSize(), realSize)
}
*/
- blk.SetFormat(FormatGNU)
+ blk.setFormat(FormatGNU)
if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil {
return err
}
@@ -321,28 +321,28 @@ type (
// The block returned is only valid until the next call to
// templateV7Plus or writeRawFile.
func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block {
- tw.blk.Reset()
+ tw.blk.reset()
modTime := hdr.ModTime
if modTime.IsZero() {
modTime = time.Unix(0, 0)
}
- v7 := tw.blk.V7()
- v7.TypeFlag()[0] = hdr.Typeflag
- fmtStr(v7.Name(), hdr.Name)
- fmtStr(v7.LinkName(), hdr.Linkname)
- fmtNum(v7.Mode(), hdr.Mode)
- fmtNum(v7.UID(), int64(hdr.Uid))
- fmtNum(v7.GID(), int64(hdr.Gid))
- fmtNum(v7.Size(), hdr.Size)
- fmtNum(v7.ModTime(), modTime.Unix())
+ v7 := tw.blk.toV7()
+ v7.typeFlag()[0] = hdr.Typeflag
+ fmtStr(v7.name(), hdr.Name)
+ fmtStr(v7.linkName(), hdr.Linkname)
+ fmtNum(v7.mode(), hdr.Mode)
+ fmtNum(v7.uid(), int64(hdr.Uid))
+ fmtNum(v7.gid(), int64(hdr.Gid))
+ fmtNum(v7.size(), hdr.Size)
+ fmtNum(v7.modTime(), modTime.Unix())
- ustar := tw.blk.USTAR()
- fmtStr(ustar.UserName(), hdr.Uname)
- fmtStr(ustar.GroupName(), hdr.Gname)
- fmtNum(ustar.DevMajor(), hdr.Devmajor)
- fmtNum(ustar.DevMinor(), hdr.Devminor)
+ ustar := tw.blk.toUSTAR()
+ fmtStr(ustar.userName(), hdr.Uname)
+ fmtStr(ustar.groupName(), hdr.Gname)
+ fmtNum(ustar.devMajor(), hdr.Devmajor)
+ fmtNum(ustar.devMinor(), hdr.Devminor)
return &tw.blk
}
@@ -351,7 +351,7 @@ func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum num
// It uses format to encode the header format and will write data as the body.
// It uses default values for all of the other fields (as BSD and GNU tar does).
func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error {
- tw.blk.Reset()
+ tw.blk.reset()
// Best effort for the filename.
name = toASCII(name)
@@ -361,15 +361,15 @@ func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) erro
name = strings.TrimRight(name, "/")
var f formatter
- v7 := tw.blk.V7()
- v7.TypeFlag()[0] = flag
- f.formatString(v7.Name(), name)
- f.formatOctal(v7.Mode(), 0)
- f.formatOctal(v7.UID(), 0)
- f.formatOctal(v7.GID(), 0)
- f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB
- f.formatOctal(v7.ModTime(), 0)
- tw.blk.SetFormat(format)
+ v7 := tw.blk.toV7()
+ v7.typeFlag()[0] = flag
+ f.formatString(v7.name(), name)
+ f.formatOctal(v7.mode(), 0)
+ f.formatOctal(v7.uid(), 0)
+ f.formatOctal(v7.gid(), 0)
+ f.formatOctal(v7.size(), int64(len(data))) // Must be < 8GiB
+ f.formatOctal(v7.modTime(), 0)
+ tw.blk.setFormat(format)
if f.err != nil {
return f.err // Only occurs if size condition is violated
}
@@ -511,10 +511,13 @@ func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) {
return io.Copy(struct{ io.Writer }{fw}, r)
}
-func (fw regFileWriter) LogicalRemaining() int64 {
+// logicalRemaining implements fileState.logicalRemaining.
+func (fw regFileWriter) logicalRemaining() int64 {
return fw.nb
}
-func (fw regFileWriter) PhysicalRemaining() int64 {
+
+// logicalRemaining implements fileState.physicalRemaining.
+func (fw regFileWriter) physicalRemaining() int64 {
return fw.nb
}
@@ -526,9 +529,9 @@ type sparseFileWriter struct {
}
func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
- overwrite := int64(len(b)) > sw.LogicalRemaining()
+ overwrite := int64(len(b)) > sw.logicalRemaining()
if overwrite {
- b = b[:sw.LogicalRemaining()]
+ b = b[:sw.logicalRemaining()]
}
b0 := b
@@ -556,7 +559,7 @@ func (sw *sparseFileWriter) Write(b []byte) (n int, err error) {
return n, errMissData // Not possible; implies bug in validation logic
case err != nil:
return n, err
- case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
+ case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
return n, errUnrefData // Not possible; implies bug in validation logic
case overwrite:
return n, ErrWriteTooLong
@@ -578,12 +581,12 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
var readLastByte bool
pos0 := sw.pos
- for sw.LogicalRemaining() > 0 && !readLastByte && err == nil {
+ for sw.logicalRemaining() > 0 && !readLastByte && err == nil {
var nf int64 // Size of fragment
dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset()
if sw.pos < dataStart { // In a hole fragment
nf = dataStart - sw.pos
- if sw.PhysicalRemaining() == 0 {
+ if sw.physicalRemaining() == 0 {
readLastByte = true
nf--
}
@@ -613,18 +616,18 @@ func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) {
return n, errMissData // Not possible; implies bug in validation logic
case err != nil:
return n, err
- case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0:
+ case sw.logicalRemaining() == 0 && sw.physicalRemaining() > 0:
return n, errUnrefData // Not possible; implies bug in validation logic
default:
return n, ensureEOF(rs)
}
}
-func (sw sparseFileWriter) LogicalRemaining() int64 {
+func (sw sparseFileWriter) logicalRemaining() int64 {
return sw.sp[len(sw.sp)-1].endOffset() - sw.pos
}
-func (sw sparseFileWriter) PhysicalRemaining() int64 {
- return sw.fw.PhysicalRemaining()
+func (sw sparseFileWriter) physicalRemaining() int64 {
+ return sw.fw.physicalRemaining()
}
// zeroWriter may only be written with NULs, otherwise it returns errWriteHole.
diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go
index a00f02d8fab698a588872ade0e5693b0d0dc0660..da3fb89e65e51ecd17627d444af95b68e54f4596 100644
--- a/src/archive/tar/writer_test.go
+++ b/src/archive/tar/writer_test.go
@@ -67,7 +67,7 @@ func TestWriter(t *testing.T) {
testClose struct { // Close() == wantErr
wantErr error
}
- testFnc interface{} // testHeader | testWrite | testReadFrom | testClose
+ testFnc any // testHeader | testWrite | testReadFrom | testClose
)
vectors := []struct {
@@ -987,11 +987,9 @@ func TestIssue12594(t *testing.T) {
// The prefix field should never appear in the GNU format.
var blk block
copy(blk[:], b.Bytes())
- prefix := string(blk.USTAR().Prefix())
- if i := strings.IndexByte(prefix, 0); i >= 0 {
- prefix = prefix[:i] // Truncate at the NUL terminator
- }
- if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
+ prefix := string(blk.toUSTAR().prefix())
+ prefix, _, _ = strings.Cut(prefix, "\x00") // Truncate at the NUL terminator
+ if blk.getFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) {
t.Errorf("test %d, found prefix in GNU format: %s", i, prefix)
}
@@ -1029,11 +1027,11 @@ func TestFileWriter(t *testing.T) {
wantCnt int64
wantErr error
}
- testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt
+ testRemaining struct { // logicalRemaining() == wantLCnt, physicalRemaining() == wantPCnt
wantLCnt int64
wantPCnt int64
}
- testFnc interface{} // testWrite | testReadFrom | testRemaining
+ testFnc any // testWrite | testReadFrom | testRemaining
)
type (
@@ -1046,7 +1044,7 @@ func TestFileWriter(t *testing.T) {
sph sparseHoles
size int64
}
- fileMaker interface{} // makeReg | makeSparse
+ fileMaker any // makeReg | makeSparse
)
vectors := []struct {
@@ -1292,11 +1290,11 @@ func TestFileWriter(t *testing.T) {
t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops))
}
case testRemaining:
- if got := fw.LogicalRemaining(); got != tf.wantLCnt {
- t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
+ if got := fw.logicalRemaining(); got != tf.wantLCnt {
+ t.Errorf("test %d.%d, logicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt)
}
- if got := fw.PhysicalRemaining(); got != tf.wantPCnt {
- t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
+ if got := fw.physicalRemaining(); got != tf.wantPCnt {
+ t.Errorf("test %d.%d, physicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt)
}
default:
t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf)
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 2d53f4c7231653d651b4f3b5bb1a15325b4a33ac..92fd6f6a9252525fb392e982952cfed66ce33e13 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -102,7 +102,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
// indicate it contains up to 1 << 128 - 1 files. Since each file has a
// header which will be _at least_ 30 bytes we can safely preallocate
// if (data size / 30) >= end.directoryRecords.
- if (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
+ if end.directorySize < uint64(size) && (uint64(size)-end.directorySize)/30 >= end.directoryRecords {
z.File = make([]*File, 0, end.directoryRecords)
}
z.Comment = end.comment
@@ -125,7 +125,6 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
if err != nil {
return err
}
- f.readDataDescriptor()
z.File = append(z.File, f)
}
if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
@@ -186,10 +185,15 @@ func (f *File) Open() (io.ReadCloser, error) {
return nil, ErrAlgorithm
}
var rc io.ReadCloser = dcomp(r)
+ var desr io.Reader
+ if f.hasDataDescriptor() {
+ desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
+ }
rc = &checksumReader{
rc: rc,
hash: crc32.NewIEEE(),
f: f,
+ desr: desr,
}
return rc, nil
}
@@ -205,49 +209,13 @@ func (f *File) OpenRaw() (io.Reader, error) {
return r, nil
}
-func (f *File) readDataDescriptor() {
- if !f.hasDataDescriptor() {
- return
- }
-
- bodyOffset, err := f.findBodyOffset()
- if err != nil {
- f.descErr = err
- return
- }
-
- // In section 4.3.9.2 of the spec: "However ZIP64 format MAY be used
- // regardless of the size of a file. When extracting, if the zip64
- // extended information extra field is present for the file the
- // compressed and uncompressed sizes will be 8 byte values."
- //
- // Historically, this package has used the compressed and uncompressed
- // sizes from the central directory to determine if the package is
- // zip64.
- //
- // For this case we allow either the extra field or sizes to determine
- // the data descriptor length.
- zip64 := f.zip64 || f.isZip64()
- n := int64(dataDescriptorLen)
- if zip64 {
- n = dataDescriptor64Len
- }
- size := int64(f.CompressedSize64)
- r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, n)
- dd, err := readDataDescriptor(r, zip64)
- if err != nil {
- f.descErr = err
- return
- }
- f.CRC32 = dd.crc32
-}
-
type checksumReader struct {
rc io.ReadCloser
hash hash.Hash32
nread uint64 // number of bytes read so far
f *File
- err error // sticky error
+ desr io.Reader // if non-nil, where to read the data descriptor
+ err error // sticky error
}
func (r *checksumReader) Stat() (fs.FileInfo, error) {
@@ -268,12 +236,12 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
if r.nread != r.f.UncompressedSize64 {
return 0, io.ErrUnexpectedEOF
}
- if r.f.hasDataDescriptor() {
- if r.f.descErr != nil {
- if r.f.descErr == io.EOF {
+ if r.desr != nil {
+ if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
+ if err1 == io.EOF {
err = io.ErrUnexpectedEOF
} else {
- err = r.f.descErr
+ err = err1
}
} else if r.hash.Sum32() != r.f.CRC32 {
err = ErrChecksum
@@ -485,10 +453,8 @@ parseExtras:
return nil
}
-func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
- // Create enough space for the largest possible size
- var buf [dataDescriptor64Len]byte
-
+func readDataDescriptor(r io.Reader, f *File) error {
+ var buf [dataDescriptorLen]byte
// The spec says: "Although not originally assigned a
// signature, the value 0x08074b50 has commonly been adopted
// as a signature value for the data descriptor record.
@@ -497,9 +463,10 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
// descriptors and should account for either case when reading
// ZIP files to ensure compatibility."
//
- // First read just those 4 bytes to see if the signature exists.
+ // dataDescriptorLen includes the size of the signature but
+ // first read just those 4 bytes to see if it exists.
if _, err := io.ReadFull(r, buf[:4]); err != nil {
- return nil, err
+ return err
}
off := 0
maybeSig := readBuf(buf[:4])
@@ -508,28 +475,21 @@ func readDataDescriptor(r io.Reader, zip64 bool) (*dataDescriptor, error) {
// bytes.
off += 4
}
-
- end := dataDescriptorLen - 4
- if zip64 {
- end = dataDescriptor64Len - 4
+ if _, err := io.ReadFull(r, buf[off:12]); err != nil {
+ return err
}
- if _, err := io.ReadFull(r, buf[off:end]); err != nil {
- return nil, err
+ b := readBuf(buf[:12])
+ if b.uint32() != f.CRC32 {
+ return ErrChecksum
}
- b := readBuf(buf[:end])
- out := &dataDescriptor{
- crc32: b.uint32(),
- }
+ // The two sizes that follow here can be either 32 bits or 64 bits
+ // but the spec is not very clear on this and different
+ // interpretations has been made causing incompatibilities. We
+ // already have the sizes from the central directory so we can
+ // just ignore these.
- if zip64 {
- out.compressedSize = b.uint64()
- out.uncompressedSize = b.uint64()
- } else {
- out.compressedSize = uint64(b.uint32())
- out.uncompressedSize = uint64(b.uint32())
- }
- return out, nil
+ return nil
}
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
@@ -710,7 +670,7 @@ func (f *fileListEntry) Size() int64 { return 0 }
func (f *fileListEntry) Mode() fs.FileMode { return fs.ModeDir | 0555 }
func (f *fileListEntry) Type() fs.FileMode { return fs.ModeDir }
func (f *fileListEntry) IsDir() bool { return true }
-func (f *fileListEntry) Sys() interface{} { return nil }
+func (f *fileListEntry) Sys() any { return nil }
func (f *fileListEntry) ModTime() time.Time {
if f.file == nil {
@@ -741,6 +701,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 +745,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 +763,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 37dafe6c8e7c448c8fa28359e07fc2cdbb73560f..d1a9bdd3346e519cb8afc75fc13475094e7249ad 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,127 +1203,14 @@ func TestCVE202127919(t *testing.T) {
if err != nil {
t.Errorf("Error reading file: %v", err)
}
-}
-
-func TestReadDataDescriptor(t *testing.T) {
- tests := []struct {
- desc string
- in []byte
- zip64 bool
- want *dataDescriptor
- wantErr error
- }{{
- desc: "valid 32 bit with signature",
- in: []byte{
- 0x50, 0x4b, 0x07, 0x08, // signature
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, // compressed size
- 0x08, 0x09, 0x0a, 0x0b, // uncompressed size
- },
- want: &dataDescriptor{
- crc32: 0x03020100,
- compressedSize: 0x07060504,
- uncompressedSize: 0x0b0a0908,
- },
- }, {
- desc: "valid 32 bit without signature",
- in: []byte{
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, // compressed size
- 0x08, 0x09, 0x0a, 0x0b, // uncompressed size
- },
- want: &dataDescriptor{
- crc32: 0x03020100,
- compressedSize: 0x07060504,
- uncompressedSize: 0x0b0a0908,
- },
- }, {
- desc: "valid 64 bit with signature",
- in: []byte{
- 0x50, 0x4b, 0x07, 0x08, // signature
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size
- },
- zip64: true,
- want: &dataDescriptor{
- crc32: 0x03020100,
- compressedSize: 0x0b0a090807060504,
- uncompressedSize: 0x131211100f0e0d0c,
- },
- }, {
- desc: "valid 64 bit without signature",
- in: []byte{
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, // uncompressed size
- },
- zip64: true,
- want: &dataDescriptor{
- crc32: 0x03020100,
- compressedSize: 0x0b0a090807060504,
- uncompressedSize: 0x131211100f0e0d0c,
- },
- }, {
- desc: "invalid 32 bit with signature",
- in: []byte{
- 0x50, 0x4b, 0x07, 0x08, // signature
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, // unexpected end
- },
- wantErr: io.ErrUnexpectedEOF,
- }, {
- desc: "invalid 32 bit without signature",
- in: []byte{
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, // unexpected end
- },
- wantErr: io.ErrUnexpectedEOF,
- }, {
- desc: "invalid 64 bit with signature",
- in: []byte{
- 0x50, 0x4b, 0x07, 0x08, // signature
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end
- },
- zip64: true,
- wantErr: io.ErrUnexpectedEOF,
- }, {
- desc: "invalid 64 bit without signature",
- in: []byte{
- 0x00, 0x01, 0x02, 0x03, // crc32
- 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, // compressed size
- 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, // unexpected end
- },
- zip64: true,
- wantErr: io.ErrUnexpectedEOF,
- }}
-
- for _, test := range tests {
- t.Run(test.desc, func(t *testing.T) {
- r := bytes.NewReader(test.in)
-
- desc, err := readDataDescriptor(r, test.zip64)
- if err != test.wantErr {
- t.Fatalf("got err %v; want nil", err)
- }
- if test.want == nil {
- return
- }
- if desc == nil {
- t.Fatalf("got nil DataDescriptor; want non-nil")
- }
- if desc.crc32 != test.want.crc32 {
- t.Errorf("got CRC32 %#x; want %#x", desc.crc32, test.want.crc32)
- }
- if desc.compressedSize != test.want.compressedSize {
- t.Errorf("got CompressedSize %#x; want %#x", desc.compressedSize, test.want.compressedSize)
- }
- if desc.uncompressedSize != test.want.uncompressedSize {
- t.Errorf("got UncompressedSize %#x; want %#x", desc.uncompressedSize, test.want.uncompressedSize)
- }
- })
+ 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)
}
}
@@ -1384,3 +1272,139 @@ func TestCVE202133196(t *testing.T) {
t.Errorf("Archive has unexpected number of files, got %d, want 5", len(r.File))
}
}
+
+func TestCVE202139293(t *testing.T) {
+ // directory size is so large, that the check in Reader.init
+ // overflows when subtracting from the archive size, causing
+ // the pre-allocation check to be bypassed.
+ data := []byte{
+ 0x50, 0x4b, 0x06, 0x06, 0x05, 0x06, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
+ 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4b,
+ 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0x50, 0xfe, 0x00, 0xff, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xff,
+ }
+ _, err := NewReader(bytes.NewReader(data), int64(len(data)))
+ if err != ErrFormat {
+ 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/archive/zip/struct.go b/src/archive/zip/struct.go
index ff9f605eb697eec319602d455917953b70b906e6..6f73fb8376a859235ff3d589a3a0314bc9311721 100644
--- a/src/archive/zip/struct.go
+++ b/src/archive/zip/struct.go
@@ -163,7 +163,7 @@ func (fi headerFileInfo) ModTime() time.Time {
}
func (fi headerFileInfo) Mode() fs.FileMode { return fi.fh.Mode() }
func (fi headerFileInfo) Type() fs.FileMode { return fi.fh.Mode().Type() }
-func (fi headerFileInfo) Sys() interface{} { return fi.fh }
+func (fi headerFileInfo) Sys() any { return fi.fh }
func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
@@ -390,11 +390,3 @@ func unixModeToFileMode(m uint32) fs.FileMode {
}
return mode
}
-
-// dataDescriptor holds the data descriptor that optionally follows the file
-// contents in the zip file.
-type dataDescriptor struct {
- crc32 uint32
- compressedSize uint64
- uncompressedSize uint64
-}
diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go
index 97c6c5297994684ca7fcfc5ccc492025709d795c..2b73eca814f621a9a0289f24cf0b6bbe8df35c6c 100644
--- a/src/archive/zip/writer_test.go
+++ b/src/archive/zip/writer_test.go
@@ -362,7 +362,7 @@ func TestWriterDirAttributes(t *testing.T) {
}
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
- if bytes.Index(b, sig[:]) != -1 {
+ if bytes.Contains(b, sig[:]) {
t.Error("there should be no data descriptor")
}
}
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index ec928e7ad69ed54d213d3a38d9efb011ff32eb82..7483946fc0aabf3e0dffaa07608369c1f473a0f3 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -68,7 +68,12 @@ func (b *Reader) Size() int { return len(b.buf) }
// Reset discards any buffered data, resets all state, and switches
// the buffered reader to read from r.
+// Calling Reset on the zero value of Reader initializes the internal buffer
+// to the default size.
func (b *Reader) Reset(r io.Reader) {
+ if b.buf == nil {
+ b.buf = make([]byte, defaultBufSize)
+ }
b.reset(b.buf, r)
}
@@ -168,6 +173,10 @@ func (b *Reader) Discard(n int) (discarded int, err error) {
if n == 0 {
return
}
+
+ b.lastByte = -1
+ b.lastRuneSize = -1
+
remain := n
for {
skip := b.Buffered()
@@ -235,6 +244,8 @@ func (b *Reader) Read(p []byte) (n int, err error) {
}
// copy as much as we can
+ // Note: if the slice panics here, it is probably because
+ // the underlying reader returned a bad count. See issue 49795.
n = copy(p, b.buf[b.r:b.w])
b.r += n
b.lastByte = int(b.buf[b.r-1])
@@ -261,8 +272,8 @@ func (b *Reader) ReadByte() (byte, error) {
// UnreadByte unreads the last byte. Only the most recently read byte can be unread.
//
// UnreadByte returns an error if the most recent method called on the
-// Reader was not a read operation. Notably, Peek is not considered a
-// read operation.
+// Reader was not a read operation. Notably, Peek, Discard, and WriteTo are not
+// considered read operations.
func (b *Reader) UnreadByte() error {
if b.lastByte < 0 || b.r == 0 && b.w > 0 {
return ErrInvalidUnreadByte
@@ -497,6 +508,9 @@ func (b *Reader) ReadString(delim byte) (string, error) {
// If the underlying reader supports the WriteTo method,
// this calls the underlying WriteTo without buffering.
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
+ b.lastByte = -1
+ b.lastRuneSize = -1
+
n, err = b.writeBuf(w)
if err != nil {
return
@@ -581,6 +595,8 @@ func NewWriterSize(w io.Writer, size int) *Writer {
}
// NewWriter returns a new Writer whose buffer has the default size.
+// If the argument io.Writer is already a Writer with large enough buffer size,
+// it returns the underlying Writer.
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize)
}
@@ -590,7 +606,12 @@ func (b *Writer) Size() int { return len(b.buf) }
// Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w.
+// Calling Reset on the zero value of Writer initializes the internal buffer
+// to the default size.
func (b *Writer) Reset(w io.Writer) {
+ if b.buf == nil {
+ b.buf = make([]byte, defaultBufSize)
+ }
b.err = nil
b.n = 0
b.wr = w
@@ -623,6 +644,14 @@ func (b *Writer) Flush() error {
// Available returns how many bytes are unused in the buffer.
func (b *Writer) Available() int { return len(b.buf) - b.n }
+// AvailableBuffer returns an empty buffer with b.Available() capacity.
+// This buffer is intended to be appended to and
+// passed to an immediately succeeding Write call.
+// The buffer is only valid until the next write operation on b.
+func (b *Writer) AvailableBuffer() []byte {
+ return b.buf[b.n:][:0]
+}
+
// Buffered returns the number of bytes that have been written into the current buffer.
func (b *Writer) Buffered() int { return b.n }
@@ -720,19 +749,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 {
@@ -740,6 +764,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 ebcc711db9d48cdefa44d6ec9bddd2406c13b59d..4dddfa9085cc4a99aacb5702dfa7f497c82cb48c 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -10,6 +10,8 @@ import (
"errors"
"fmt"
"io"
+ "math/rand"
+ "strconv"
"strings"
"testing"
"testing/iotest"
@@ -302,6 +304,40 @@ func TestNoUnreadByteAfterPeek(t *testing.T) {
}
}
+func TestNoUnreadRuneAfterDiscard(t *testing.T) {
+ br := NewReader(strings.NewReader("example"))
+ br.ReadRune()
+ br.Discard(1)
+ if err := br.UnreadRune(); err == nil {
+ t.Error("UnreadRune didn't fail after Discard")
+ }
+}
+
+func TestNoUnreadByteAfterDiscard(t *testing.T) {
+ br := NewReader(strings.NewReader("example"))
+ br.ReadByte()
+ br.Discard(1)
+ if err := br.UnreadByte(); err == nil {
+ t.Error("UnreadByte didn't fail after Discard")
+ }
+}
+
+func TestNoUnreadRuneAfterWriteTo(t *testing.T) {
+ br := NewReader(strings.NewReader("example"))
+ br.WriteTo(io.Discard)
+ if err := br.UnreadRune(); err == nil {
+ t.Error("UnreadRune didn't fail after WriteTo")
+ }
+}
+
+func TestNoUnreadByteAfterWriteTo(t *testing.T) {
+ br := NewReader(strings.NewReader("example"))
+ br.WriteTo(io.Discard)
+ if err := br.UnreadByte(); err == nil {
+ t.Error("UnreadByte didn't fail after WriteTo")
+ }
+}
+
func TestUnreadByte(t *testing.T) {
segments := []string{"Hello, ", "world"}
r := NewReader(&StringReader{data: segments})
@@ -608,6 +644,37 @@ func TestWriter(t *testing.T) {
}
}
+func TestWriterAppend(t *testing.T) {
+ got := new(bytes.Buffer)
+ var want []byte
+ rn := rand.New(rand.NewSource(0))
+ w := NewWriterSize(got, 64)
+ for i := 0; i < 100; i++ {
+ // Obtain a buffer to append to.
+ b := w.AvailableBuffer()
+ if w.Available() != cap(b) {
+ t.Fatalf("Available() = %v, want %v", w.Available(), cap(b))
+ }
+
+ // While not recommended, it is valid to append to a shifted buffer.
+ // This forces Write to copy the the input.
+ if rn.Intn(8) == 0 && cap(b) > 0 {
+ b = b[1:1:cap(b)]
+ }
+
+ // Append a random integer of varying width.
+ n := int64(rn.Intn(1 << rn.Intn(30)))
+ want = append(strconv.AppendInt(want, n, 10), ' ')
+ b = append(strconv.AppendInt(b, n, 10), ' ')
+ w.Write(b)
+ }
+ w.Flush()
+
+ if !bytes.Equal(got.Bytes(), want) {
+ t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want)
+ }
+}
+
// Check that write errors are returned properly.
type errorWriterTest struct {
@@ -1284,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) {
@@ -1312,6 +1427,7 @@ func TestReaderReset(t *testing.T) {
if string(buf) != "foo" {
t.Errorf("buf = %q; want foo", buf)
}
+
r.Reset(strings.NewReader("bar bar"))
all, err := io.ReadAll(r)
if err != nil {
@@ -1320,12 +1436,23 @@ func TestReaderReset(t *testing.T) {
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
+
+ *r = Reader{} // zero out the Reader
+ r.Reset(strings.NewReader("bar bar"))
+ all, err = io.ReadAll(r)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(all) != "bar bar" {
+ t.Errorf("ReadAll = %q; want bar bar", all)
+ }
}
func TestWriterReset(t *testing.T) {
- var buf1, buf2 bytes.Buffer
+ var buf1, buf2, buf3 bytes.Buffer
w := NewWriter(&buf1)
w.WriteString("foo")
+
w.Reset(&buf2) // and not flushed
w.WriteString("bar")
w.Flush()
@@ -1335,6 +1462,17 @@ func TestWriterReset(t *testing.T) {
if buf2.String() != "bar" {
t.Errorf("buf2 = %q; want bar", buf2.String())
}
+
+ *w = Writer{} // zero out the Writer
+ w.Reset(&buf3) // and not flushed
+ w.WriteString("bar")
+ w.Flush()
+ if buf1.String() != "" {
+ t.Errorf("buf1 = %q; want empty", buf1.String())
+ }
+ if buf3.String() != "bar" {
+ t.Errorf("buf3 = %q; want bar", buf3.String())
+ }
}
func TestReaderDiscard(t *testing.T) {
@@ -1382,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/bufio/example_test.go b/src/bufio/example_test.go
index 8885d40549f1532010a320d00964f19bffbb5846..a864d11012e77f96bc2a24dc692ce967c73270e3 100644
--- a/src/bufio/example_test.go
+++ b/src/bufio/example_test.go
@@ -20,6 +20,18 @@ func ExampleWriter() {
// Output: Hello, world!
}
+func ExampleWriter_AvailableBuffer() {
+ w := bufio.NewWriter(os.Stdout)
+ for _, i := range []int64{1, 2, 3, 4} {
+ b := w.AvailableBuffer()
+ b = strconv.AppendInt(b, i, 10)
+ b = append(b, ' ')
+ w.Write(b)
+ }
+ w.Flush()
+ // Output: 1 2 3 4
+}
+
// The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin)
diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go
index 01190e99002d441da5a477fa9ec7c8f36a0df44b..08ae7ed313add4fb435b154605d143188747f302 100644
--- a/src/builtin/builtin.go
+++ b/src/builtin/builtin.go
@@ -91,6 +91,16 @@ type byte = uint8
// used, by convention, to distinguish character values from integer values.
type rune = int32
+// any is an alias for interface{} and is equivalent to interface{} in all ways.
+type any = interface{}
+
+// comparable is an interface that is implemented by all comparable types
+// (booleans, numbers, strings, pointers, channels, interfaces,
+// arrays of comparable types, structs whose fields are all comparable types).
+// The comparable interface may only be used as a type parameter constraint,
+// not as the type of a variable.
+type comparable comparable
+
// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
@@ -229,7 +239,7 @@ func close(c chan<- Type)
// that point, the program is terminated with a non-zero exit code. This
// termination sequence is called panicking and can be controlled by the
// built-in function recover.
-func panic(v interface{})
+func panic(v any)
// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
@@ -240,7 +250,7 @@ func panic(v interface{})
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
-func recover() interface{}
+func recover() any
// The print built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
diff --git a/src/bytes/boundary_test.go b/src/bytes/boundary_test.go
index 5a47526593b093678b85e0c45c2dfbd5a42d16ed..f9855fcb0520f3bdf9082f71ddf794c923c3d0b5 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 ce52649f132bb9575beacdca5af912f97478bb32..6fdaa49c7344921da2d1c987d714a61884935157 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:])
@@ -746,7 +746,8 @@ func isSeparator(r rune) bool {
// Title treats s as UTF-8-encoded bytes and returns a copy with all Unicode letters that begin
// words mapped to their title case.
//
-// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
+// Deprecated: The rule Title uses for word boundaries does not handle Unicode
+// punctuation properly. Use golang.org/x/text/cases instead.
func Title(s []byte) []byte {
// Use a closure here to remember state.
// Hackish but effective. Depends on Map scanning in order and calling
@@ -867,6 +868,8 @@ func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
// most-significant bit of the highest word, map to the full range of all
// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
// ensuring that any non-ASCII character will be reported as not in the set.
+// This allocates a total of 32 bytes even though the upper half
+// is unused to avoid bounds checks in asciiSet.contains.
type asciiSet [8]uint32
// makeASCIISet creates a set of ASCII characters and reports whether all
@@ -877,53 +880,133 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) {
if c >= utf8.RuneSelf {
return as, false
}
- as[c>>5] |= 1 << uint(c&31)
+ as[c/32] |= 1 << (c % 32)
}
return as, true
}
// contains reports whether c is inside the set.
func (as *asciiSet) contains(c byte) bool {
- return (as[c>>5] & (1 << uint(c&31))) != 0
+ return (as[c/32] & (1 << (c % 32))) != 0
}
-func makeCutsetFunc(cutset string) func(r rune) bool {
- if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
- return func(r rune) bool {
- return r == rune(cutset[0])
- }
- }
- if as, isASCII := makeASCIISet(cutset); isASCII {
- return func(r rune) bool {
- return r < utf8.RuneSelf && as.contains(byte(r))
- }
- }
- return func(r rune) bool {
- for _, c := range cutset {
- if c == r {
- return true
- }
+// containsRune is a simplified version of strings.ContainsRune
+// to avoid importing the strings package.
+// We avoid bytes.ContainsRune to avoid allocating a temporary copy of s.
+func containsRune(s string, r rune) bool {
+ for _, c := range s {
+ if c == r {
+ return true
}
- return false
}
+ return false
}
// Trim returns a subslice of s by slicing off all leading and
// trailing UTF-8-encoded code points contained in cutset.
func Trim(s []byte, cutset string) []byte {
- return TrimFunc(s, makeCutsetFunc(cutset))
+ if len(s) == 0 || cutset == "" {
+ return s
+ }
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimLeftASCII(trimRightASCII(s, &as), &as)
+ }
+ return trimLeftUnicode(trimRightUnicode(s, cutset), cutset)
}
// TrimLeft returns a subslice of s by slicing off all leading
// UTF-8-encoded code points contained in cutset.
func TrimLeft(s []byte, cutset string) []byte {
- return TrimLeftFunc(s, makeCutsetFunc(cutset))
+ if len(s) == 0 || cutset == "" {
+ return s
+ }
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimLeftByte(s, cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimLeftASCII(s, &as)
+ }
+ return trimLeftUnicode(s, cutset)
+}
+
+func trimLeftByte(s []byte, c byte) []byte {
+ for len(s) > 0 && s[0] == c {
+ s = s[1:]
+ }
+ return s
+}
+
+func trimLeftASCII(s []byte, as *asciiSet) []byte {
+ for len(s) > 0 {
+ if !as.contains(s[0]) {
+ break
+ }
+ s = s[1:]
+ }
+ return s
+}
+
+func trimLeftUnicode(s []byte, cutset string) []byte {
+ for len(s) > 0 {
+ r, n := rune(s[0]), 1
+ if r >= utf8.RuneSelf {
+ r, n = utf8.DecodeRune(s)
+ }
+ if !containsRune(cutset, r) {
+ break
+ }
+ s = s[n:]
+ }
+ return s
}
// TrimRight returns a subslice of s by slicing off all trailing
// UTF-8-encoded code points that are contained in cutset.
func TrimRight(s []byte, cutset string) []byte {
- return TrimRightFunc(s, makeCutsetFunc(cutset))
+ if len(s) == 0 || cutset == "" {
+ return s
+ }
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimRightByte(s, cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimRightASCII(s, &as)
+ }
+ return trimRightUnicode(s, cutset)
+}
+
+func trimRightByte(s []byte, c byte) []byte {
+ for len(s) > 0 && s[len(s)-1] == c {
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+func trimRightASCII(s []byte, as *asciiSet) []byte {
+ for len(s) > 0 {
+ if !as.contains(s[len(s)-1]) {
+ break
+ }
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+func trimRightUnicode(s []byte, cutset string) []byte {
+ for len(s) > 0 {
+ r, n := rune(s[len(s)-1]), 1
+ if r >= utf8.RuneSelf {
+ r, n = utf8.DecodeLastRune(s)
+ }
+ if !containsRune(cutset, r) {
+ break
+ }
+ s = s[:len(s)-n]
+ }
+ return s
}
// TrimSpace returns a subslice of s by slicing off all leading and
@@ -1174,3 +1257,16 @@ func Index(s, sep []byte) int {
}
return -1
}
+
+// Cut slices s around the first instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, cut returns s, nil, false.
+//
+// Cut returns slices of the original slice s, not copies.
+func Cut(s, sep []byte) (before, after []byte, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, nil, false
+}
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 544ee46f908860dc969170c26d8f3ad472394eee..3bece6adf0b905b63f580db349c5ee0cf007b89b 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -1251,7 +1251,9 @@ var trimTests = []TrimTest{
{"TrimLeft", "abba", "ab", ""},
{"TrimRight", "abba", "ab", ""},
{"TrimLeft", "abba", "a", "bba"},
+ {"TrimLeft", "abba", "b", "abba"},
{"TrimRight", "abba", "a", "abb"},
+ {"TrimRight", "abba", "b", "abba"},
{"Trim", "", "<>", "tag"},
{"Trim", "* listitem", " *", "listitem"},
{"Trim", `"quote"`, `"`, "quote"},
@@ -1565,6 +1567,29 @@ func TestEqualFold(t *testing.T) {
}
}
+var cutTests = []struct {
+ s, sep string
+ before, after string
+ found bool
+}{
+ {"abc", "b", "a", "c", true},
+ {"abc", "a", "", "bc", true},
+ {"abc", "c", "ab", "", true},
+ {"abc", "abc", "", "", true},
+ {"abc", "", "", "abc", true},
+ {"abc", "d", "abc", "", false},
+ {"", "d", "", "", false},
+ {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+ for _, tt := range cutTests {
+ if before, after, found := Cut([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found {
+ t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
+ }
+ }
+}
+
func TestBufferGrowNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
@@ -1963,6 +1988,13 @@ func BenchmarkTrimASCII(b *testing.B) {
}
}
+func BenchmarkTrimByte(b *testing.B) {
+ x := []byte(" the quick brown fox ")
+ for i := 0; i < b.N; i++ {
+ Trim(x, " ")
+ }
+}
+
func BenchmarkIndexPeriodic(b *testing.B) {
key := []byte{1, 1}
for _, skip := range [...]int{2, 4, 8, 16, 32, 64} {
diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go
index ae93202b5706e12da11cb8866af8f6a9d75b5745..54a7aa6ae6c04960ce88f6b0d9cabbc01b46acbe 100644
--- a/src/bytes/example_test.go
+++ b/src/bytes/example_test.go
@@ -37,6 +37,16 @@ func ExampleBuffer_Bytes() {
// Output: hello world
}
+func ExampleBuffer_Cap() {
+ buf1 := bytes.NewBuffer(make([]byte, 10))
+ buf2 := bytes.NewBuffer(make([]byte, 0, 10))
+ fmt.Println(buf1.Cap())
+ fmt.Println(buf2.Cap())
+ // Output:
+ // 10
+ // 10
+}
+
func ExampleBuffer_Grow() {
var b bytes.Buffer
b.Grow(64)
@@ -54,6 +64,52 @@ func ExampleBuffer_Len() {
// Output: 5
}
+func ExampleBuffer_Next() {
+ var b bytes.Buffer
+ b.Grow(64)
+ b.Write([]byte("abcde"))
+ fmt.Printf("%s\n", string(b.Next(2)))
+ fmt.Printf("%s\n", string(b.Next(2)))
+ fmt.Printf("%s", string(b.Next(2)))
+ // Output:
+ // ab
+ // cd
+ // e
+}
+
+func ExampleBuffer_Read() {
+ var b bytes.Buffer
+ b.Grow(64)
+ b.Write([]byte("abcde"))
+ rdbuf := make([]byte, 1)
+ n, err := b.Read(rdbuf)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(n)
+ fmt.Println(b.String())
+ fmt.Println(string(rdbuf))
+ // Output
+ // 1
+ // bcde
+ // a
+}
+
+func ExampleBuffer_ReadByte() {
+ var b bytes.Buffer
+ b.Grow(64)
+ b.Write([]byte("abcde"))
+ c, err := b.ReadByte()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(c)
+ fmt.Println(b.String())
+ // Output
+ // 97
+ // bcde
+}
+
func ExampleCompare() {
// Interpret Compare's result by comparing it to zero.
var a, b []byte
@@ -92,36 +148,6 @@ func ExampleCompare_search() {
}
}
-func ExampleTrimSuffix() {
- var b = []byte("Hello, goodbye, etc!")
- b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
- b = bytes.TrimSuffix(b, []byte("gopher"))
- b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
- os.Stdout.Write(b)
- // Output: Hello, world!
-}
-
-func ExampleTrimPrefix() {
- var b = []byte("Goodbye,, world!")
- b = bytes.TrimPrefix(b, []byte("Goodbye,"))
- b = bytes.TrimPrefix(b, []byte("See ya,"))
- fmt.Printf("Hello%s", b)
- // Output: Hello, world!
-}
-
-func ExampleFields() {
- fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
- // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
- f := func(c rune) bool {
- return !unicode.IsLetter(c) && !unicode.IsNumber(c)
- }
- fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
- // Output: Fields are: ["foo1" "bar2" "baz3"]
-}
-
func ExampleContains() {
fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo")))
fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar")))
@@ -168,6 +194,22 @@ func ExampleCount() {
// 5
}
+func ExampleCut() {
+ show := func(s, sep string) {
+ before, after, found := bytes.Cut([]byte(s), []byte(sep))
+ fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+ }
+ show("Gopher", "Go")
+ show("Gopher", "ph")
+ show("Gopher", "er")
+ show("Gopher", "Badger")
+ // Output:
+ // Cut("Gopher", "Go") = "", "pher", true
+ // Cut("Gopher", "ph") = "Go", "er", true
+ // Cut("Gopher", "er") = "Goph", "", true
+ // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
func ExampleEqual() {
fmt.Println(bytes.Equal([]byte("Go"), []byte("Go")))
fmt.Println(bytes.Equal([]byte("Go"), []byte("C++")))
@@ -181,6 +223,19 @@ func ExampleEqualFold() {
// Output: true
}
+func ExampleFields() {
+ fmt.Printf("Fields are: %q", bytes.Fields([]byte(" foo bar baz ")))
+ // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+ f := func(c rune) bool {
+ return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+ }
+ fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte(" foo1;bar2,baz3..."), f))
+ // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
func ExampleHasPrefix() {
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
@@ -246,6 +301,12 @@ func ExampleIndexRune() {
// -1
}
+func ExampleJoin() {
+ s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
+ fmt.Printf("%s", bytes.Join(s, []byte(", ")))
+ // Output: foo, bar, baz
+}
+
func ExampleLastIndex() {
fmt.Println(bytes.Index([]byte("go gopher"), []byte("go")))
fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go")))
@@ -286,10 +347,12 @@ func ExampleLastIndexFunc() {
// -1
}
-func ExampleJoin() {
- s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
- fmt.Printf("%s", bytes.Join(s, []byte(", ")))
- // Output: foo, bar, baz
+func ExampleReader_Len() {
+ fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
+ fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+ // Output:
+ // 3
+ // 16
}
func ExampleRepeat() {
@@ -399,20 +462,6 @@ func ExampleTrimFunc() {
// go-gopher!
}
-func ExampleMap() {
- rot13 := func(r rune) rune {
- switch {
- case r >= 'A' && r <= 'Z':
- return 'A' + (r-'A'+13)%26
- case r >= 'a' && r <= 'z':
- return 'a' + (r-'a'+13)%26
- }
- return r
- }
- fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
- // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
-}
-
func ExampleTrimLeft() {
fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789")))
// Output:
@@ -429,11 +478,28 @@ func ExampleTrimLeftFunc() {
// go-gopher!567
}
+func ExampleTrimPrefix() {
+ var b = []byte("Goodbye,, world!")
+ b = bytes.TrimPrefix(b, []byte("Goodbye,"))
+ b = bytes.TrimPrefix(b, []byte("See ya,"))
+ fmt.Printf("Hello%s", b)
+ // Output: Hello, world!
+}
+
func ExampleTrimSpace() {
fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n")))
// Output: a lone gopher
}
+func ExampleTrimSuffix() {
+ var b = []byte("Hello, goodbye, etc!")
+ b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
+ b = bytes.TrimSuffix(b, []byte("gopher"))
+ b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
+ os.Stdout.Write(b)
+ // Output: Hello, world!
+}
+
func ExampleTrimRight() {
fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789")))
// Output:
@@ -450,21 +516,6 @@ func ExampleTrimRightFunc() {
// 1234go-gopher!
}
-func ExampleToUpper() {
- fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
- // Output: GOPHER
-}
-
-func ExampleToUpperSpecial() {
- str := []byte("ahoj vývojári golang")
- totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
- fmt.Println("Original : " + string(str))
- fmt.Println("ToUpper : " + string(totitle))
- // Output:
- // Original : ahoj vývojári golang
- // ToUpper : AHOJ VÝVOJÁRİ GOLANG
-}
-
func ExampleToLower() {
fmt.Printf("%s", bytes.ToLower([]byte("Gopher")))
// Output: gopher
@@ -480,10 +531,17 @@ func ExampleToLowerSpecial() {
// ToLower : ahoj vývojári golang
}
-func ExampleReader_Len() {
- fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
- fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+func ExampleToUpper() {
+ fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
+ // Output: GOPHER
+}
+
+func ExampleToUpperSpecial() {
+ str := []byte("ahoj vývojári golang")
+ totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
+ fmt.Println("Original : " + string(str))
+ fmt.Println("ToUpper : " + string(totitle))
// Output:
- // 3
- // 16
+ // Original : ahoj vývojári golang
+ // ToUpper : AHOJ VÝVOJÁRİ GOLANG
}
diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go
index 8baac5046cbeecf30b6dce346908943de524d1e8..9119c944ace4783ebb9fb3295beb7be23336e009 100644
--- a/src/bytes/reader_test.go
+++ b/src/bytes/reader_test.go
@@ -76,7 +76,7 @@ func TestReaderAt(t *testing.T) {
off int64
n int
want string
- wanterr interface{}
+ wanterr any
}{
{0, 10, "0123456789", nil},
{1, 10, "123456789", io.EOF},
diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go
index b07a238d679a1636657fb7618b8c0d7e7ea34a72..036aefe4d8b3e280f07cdbcb399243147d8993cc 100644
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -460,7 +460,7 @@ type listImports struct {
var listCache sync.Map // map[string]listImports, keyed by contextName
// listSem is a semaphore restricting concurrent invocations of 'go list'.
-var listSem = make(chan semToken, runtime.GOMAXPROCS(0))
+var listSem = make(chan semToken, ((runtime.GOMAXPROCS(0)-1)/2)+1)
type semToken struct{}
@@ -653,10 +653,15 @@ func (w *Walker) ImportFrom(fromPath, fromDir string, mode types.ImportMode) (*t
}
// Type-check package files.
+ var sizes types.Sizes
+ if w.context != nil {
+ sizes = types.SizesFor(w.context.Compiler, w.context.GOARCH)
+ }
conf := types.Config{
IgnoreFuncBodies: true,
FakeImportC: true,
Importer: w,
+ Sizes: sizes,
}
pkg, err = conf.Check(name, fset, files, nil)
if err != nil {
@@ -701,6 +706,36 @@ func sortedMethodNames(typ *types.Interface) []string {
return list
}
+// sortedEmbeddeds returns constraint types embedded in an
+// interface. It does not include embedded interface types or methods.
+func (w *Walker) sortedEmbeddeds(typ *types.Interface) []string {
+ n := typ.NumEmbeddeds()
+ list := make([]string, 0, n)
+ for i := 0; i < n; i++ {
+ emb := typ.EmbeddedType(i)
+ switch emb := emb.(type) {
+ case *types.Interface:
+ list = append(list, w.sortedEmbeddeds(emb)...)
+ case *types.Union:
+ var buf bytes.Buffer
+ nu := emb.Len()
+ for i := 0; i < nu; i++ {
+ if i > 0 {
+ buf.WriteString(" | ")
+ }
+ term := emb.Term(i)
+ if term.Tilde() {
+ buf.WriteByte('~')
+ }
+ w.writeType(&buf, term.Type())
+ }
+ list = append(list, buf.String())
+ }
+ }
+ sort.Strings(list)
+ return list
+}
+
func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
switch typ := typ.(type) {
case *types.Basic:
@@ -758,9 +793,16 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
case *types.Interface:
buf.WriteString("interface{")
- if typ.NumMethods() > 0 {
+ if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 {
buf.WriteByte(' ')
+ }
+ if typ.NumMethods() > 0 {
buf.WriteString(strings.Join(sortedMethodNames(typ), ", "))
+ }
+ if typ.NumEmbeddeds() > 0 {
+ buf.WriteString(strings.Join(w.sortedEmbeddeds(typ), ", "))
+ }
+ if typ.NumMethods() > 0 || typ.NumEmbeddeds() > 0 {
buf.WriteByte(' ')
}
buf.WriteString("}")
@@ -795,12 +837,19 @@ func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
}
buf.WriteString(typ.Obj().Name())
+ case *types.TypeParam:
+ // Type parameter names may change, so use a placeholder instead.
+ fmt.Fprintf(buf, "$%d", typ.Index())
+
default:
panic(fmt.Sprintf("unknown type %T", typ))
}
}
func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
+ if tparams := sig.TypeParams(); tparams != nil {
+ w.writeTypeParams(buf, tparams, true)
+ }
w.writeParams(buf, sig.Params(), sig.Variadic())
switch res := sig.Results(); res.Len() {
case 0:
@@ -814,6 +863,23 @@ func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
}
}
+func (w *Walker) writeTypeParams(buf *bytes.Buffer, tparams *types.TypeParamList, withConstraints bool) {
+ buf.WriteByte('[')
+ c := tparams.Len()
+ for i := 0; i < c; i++ {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ tp := tparams.At(i)
+ w.writeType(buf, tp)
+ if withConstraints {
+ buf.WriteByte(' ')
+ w.writeType(buf, tp.Constraint())
+ }
+ }
+ buf.WriteByte(']')
+}
+
func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) {
buf.WriteByte('(')
for i, n := 0, t.Len(); i < n; i++ {
@@ -867,6 +933,12 @@ func (w *Walker) emitObj(obj types.Object) {
func (w *Walker) emitType(obj *types.TypeName) {
name := obj.Name()
+ if tparams := obj.Type().(*types.Named).TypeParams(); tparams != nil {
+ var buf bytes.Buffer
+ buf.WriteString(name)
+ w.writeTypeParams(&buf, tparams, true)
+ name = buf.String()
+ }
typ := obj.Type()
if obj.IsAlias() {
w.emitf("type %s = %s", name, w.typeString(typ))
@@ -990,10 +1062,16 @@ func (w *Walker) emitMethod(m *types.Selection) {
log.Fatalf("exported method with unexported receiver base type: %s", m)
}
}
- w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig))
+ tps := ""
+ if rtp := sig.RecvTypeParams(); rtp != nil {
+ var buf bytes.Buffer
+ w.writeTypeParams(&buf, rtp, false)
+ tps = buf.String()
+ }
+ w.emitf("method (%s%s) %s%s", w.typeString(recv), tps, m.Obj().Name(), w.signatureString(sig))
}
-func (w *Walker) emitf(format string, args ...interface{}) {
+func (w *Walker) emitf(format string, args ...any) {
f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...)
if strings.Contains(f, "\n") {
panic("feature contains newlines: " + f)
diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go
index 81979de191abc2f13263a467fc7e34aab31b8026..1b94a1b883f4747e6d37e17e99de6fdb5e0f7b39 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/api/testdata/src/issue21181/p/p_generic.go b/src/cmd/api/testdata/src/issue21181/p/p_generic.go
index 4d75809676200c5b6e642cb80d2abeeff781cc5e..ad6df20187e9c0bfbbf3104f4c81067259e8a4ff 100644
--- a/src/cmd/api/testdata/src/issue21181/p/p_generic.go
+++ b/src/cmd/api/testdata/src/issue21181/p/p_generic.go
@@ -1,3 +1,4 @@
+//go:build !amd64
// +build !amd64
package p
diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go
index 65181b248a30388d9901e00eeca9ae1ea71ed769..81826d768b4f8c06111b66f1db7cc3e75ed56002 100644
--- a/src/cmd/api/testdata/src/pkg/p1/p1.go
+++ b/src/cmd/api/testdata/src/pkg/p1/p1.go
@@ -197,7 +197,7 @@ var m map[string]int
var chanVar chan int
-var ifaceVar interface{} = 5
+var ifaceVar any = 5
var assertVar = ifaceVar.(int)
diff --git a/src/cmd/api/testdata/src/pkg/p4/golden.txt b/src/cmd/api/testdata/src/pkg/p4/golden.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7997ab447123e83a5de90a695a7fa271bcb3d0a0
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p4/golden.txt
@@ -0,0 +1,5 @@
+pkg p4, func NewPair[$0 interface{ M }, $1 interface{ ~int }]($0, $1) Pair
+pkg p4, method (Pair[$0, $1]) Second() $1
+pkg p4, method (Pair[$0, $1]) First() $0
+pkg p4, type Pair[$0 interface{ M }, $1 interface{ ~int }] struct
+pkg p4, func Clone[$0 interface{ ~[]$1 }, $1 interface{}]($0) $0
diff --git a/src/cmd/api/testdata/src/pkg/p4/p4.go b/src/cmd/api/testdata/src/pkg/p4/p4.go
new file mode 100644
index 0000000000000000000000000000000000000000..1f90e779dd4b52b540b1d9c6a74d48601be61945
--- /dev/null
+++ b/src/cmd/api/testdata/src/pkg/p4/p4.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 p4
+
+type Pair[T1 interface{ M() }, T2 ~int] struct {
+ f1 T1
+ f2 T2
+}
+
+func NewPair[T1 interface{ M() }, T2 ~int](v1 T1, v2 T2) Pair[T1, T2] {
+ return Pair[T1, T2]{f1: v1, f2: v2}
+}
+
+func (p Pair[X1, _]) First() X1 {
+ return p.f1
+}
+
+func (p Pair[_, X2]) Second() X2 {
+ return p.f2
+}
+
+func Clone[S ~[]T, T any](s S) S {
+ return append(S(nil), s...)
+}
diff --git a/src/cmd/asm/internal/arch/arch.go b/src/cmd/asm/internal/arch/arch.go
index 026d8abf81305f51d953b500c35d3bd2d7c7a087..4d374cb828eadb595727a5f4e2552570a7f39d70 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":
@@ -178,6 +178,10 @@ func archX86(linkArch *obj.LinkArch) *Arch {
instructions["PSLLDQ"] = x86.APSLLO
instructions["PSRLDQ"] = x86.APSRLO
instructions["PADDD"] = x86.APADDL
+ // Spellings originally used in CL 97235.
+ instructions["MOVBELL"] = x86.AMOVBEL
+ instructions["MOVBEQQ"] = x86.AMOVBEQ
+ instructions["MOVBEWW"] = x86.AMOVBEW
return &Arch{
LinkArch: linkArch,
@@ -374,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
@@ -534,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/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go
index 40d828a1fea7485d3389c529f16180ecdd140bc7..24689c5ab17657816a67e7fe0b69a498e40d4c99 100644
--- a/src/cmd/asm/internal/arch/arm64.go
+++ b/src/cmd/asm/internal/arch/arm64.go
@@ -165,27 +165,21 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
}
}
if reg <= arm64.REG_R31 && reg >= arm64.REG_R0 {
+ if !isAmount {
+ return errors.New("invalid register extension")
+ }
switch ext {
case "UXTB":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTB + Rnum
case "UXTH":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTH + Rnum
case "UXTW":
- if !isAmount {
- return errors.New("invalid register extension")
- }
// effective address of memory is a base register value and an offset register value.
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_UXTW + Rnum
@@ -193,48 +187,33 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
a.Reg = arm64.REG_UXTW + Rnum
}
case "UXTX":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTX + Rnum
case "SXTB":
- if !isAmount {
- return errors.New("invalid register extension")
+ if a.Type == obj.TYPE_MEM {
+ return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_SXTB + Rnum
case "SXTH":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_SXTH + Rnum
case "SXTW":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTW + Rnum
} else {
a.Reg = arm64.REG_SXTW + Rnum
}
case "SXTX":
- if !isAmount {
- return errors.New("invalid register extension")
- }
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTX + Rnum
} else {
a.Reg = arm64.REG_SXTX + Rnum
}
case "LSL":
- if !isAmount {
- return errors.New("invalid register extension")
- }
a.Index = arm64.REG_LSL + Rnum
default:
return errors.New("unsupported general register extension type: " + ext)
diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go
index cf0d1550f99f0e3d6f014b1b4782553692348bb2..d0cb6328f16b6e6936b826a7e11e09b914549b76 100644
--- a/src/cmd/asm/internal/asm/asm.go
+++ b/src/cmd/asm/internal/asm/asm.go
@@ -793,6 +793,13 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) {
return
}
}
+ if p.arch.Family == sys.RISCV64 {
+ prog.From = a[0]
+ prog.Reg = p.getRegister(prog, op, &a[1])
+ prog.SetRestArgs([]obj.Addr{a[2]})
+ prog.To = a[3]
+ break
+ }
if p.arch.Family == sys.S390X {
if a[1].Type != obj.TYPE_REG {
p.errorf("second operand must be a register in %s instruction", op)
diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go
index 8ef02b1a0e8acacafb379d12030e1c522de984f0..c1295a0c4267d577e5302c493518aed7c69f47fb 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/amd64enc.s b/src/cmd/asm/internal/asm/testdata/amd64enc.s
index c02f51d125916b84687fc8aba07428a37950ccd8..5bba292dee43f6659b457c8de9177682b0fa5d82 100644
--- a/src/cmd/asm/internal/asm/testdata/amd64enc.s
+++ b/src/cmd/asm/internal/asm/testdata/amd64enc.s
@@ -2495,30 +2495,30 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVAPS X11, (BX) // 440f291b
MOVAPS X2, (R11) // 410f2913
MOVAPS X11, (R11) // 450f291b
- MOVBEWW DX, (BX) // 660f38f113
- MOVBEWW R11, (BX) // 66440f38f11b
- MOVBEWW DX, (R11) // 66410f38f113
- MOVBEWW R11, (R11) // 66450f38f11b
- MOVBEWW (BX), DX // 660f38f013
- MOVBEWW (R11), DX // 66410f38f013
- MOVBEWW (BX), R11 // 66440f38f01b
- MOVBEWW (R11), R11 // 66450f38f01b
- MOVBELL DX, (BX) // 0f38f113
- MOVBELL R11, (BX) // 440f38f11b
- MOVBELL DX, (R11) // 410f38f113
- MOVBELL R11, (R11) // 450f38f11b
- MOVBELL (BX), DX // 0f38f013
- MOVBELL (R11), DX // 410f38f013
- MOVBELL (BX), R11 // 440f38f01b
- MOVBELL (R11), R11 // 450f38f01b
- MOVBEQQ DX, (BX) // 480f38f113
- MOVBEQQ R11, (BX) // 4c0f38f11b
- MOVBEQQ DX, (R11) // 490f38f113
- MOVBEQQ R11, (R11) // 4d0f38f11b
- MOVBEQQ (BX), DX // 480f38f013
- MOVBEQQ (R11), DX // 490f38f013
- MOVBEQQ (BX), R11 // 4c0f38f01b
- MOVBEQQ (R11), R11 // 4d0f38f01b
+ MOVBEW DX, (BX) // 660f38f113
+ MOVBEW R11, (BX) // 66440f38f11b
+ MOVBEW DX, (R11) // 66410f38f113
+ MOVBEW R11, (R11) // 66450f38f11b
+ MOVBEW (BX), DX // 660f38f013
+ MOVBEW (R11), DX // 66410f38f013
+ MOVBEW (BX), R11 // 66440f38f01b
+ MOVBEW (R11), R11 // 66450f38f01b
+ MOVBEL DX, (BX) // 0f38f113
+ MOVBEL R11, (BX) // 440f38f11b
+ MOVBEL DX, (R11) // 410f38f113
+ MOVBEL R11, (R11) // 450f38f11b
+ MOVBEL (BX), DX // 0f38f013
+ MOVBEL (R11), DX // 410f38f013
+ MOVBEL (BX), R11 // 440f38f01b
+ MOVBEL (R11), R11 // 450f38f01b
+ MOVBEQ DX, (BX) // 480f38f113
+ MOVBEQ R11, (BX) // 4c0f38f11b
+ MOVBEQ DX, (R11) // 490f38f113
+ MOVBEQ R11, (R11) // 4d0f38f11b
+ MOVBEQ (BX), DX // 480f38f013
+ MOVBEQ (R11), DX // 490f38f013
+ MOVBEQ (BX), R11 // 4c0f38f01b
+ MOVBEQ (R11), R11 // 4d0f38f01b
MOVQ (BX), M2 // 0f6e13 or 0f6f13 or 480f6e13
MOVQ (R11), M2 // 410f6e13 or 410f6f13 or 490f6e13
MOVQ DX, M2 // 0f6ed2 or 480f6ed2
diff --git a/src/cmd/asm/internal/asm/testdata/arm64.s b/src/cmd/asm/internal/asm/testdata/arm64.s
index d8a20edfc13039d205c10a0579ab9414a54daa54..a4b56b0696b33a8524243df796ac9c7fee0ea4be 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64.s
@@ -334,6 +334,8 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
EONW $0x6006000060060, R5 // EONW $1689262177517664, R5 // 1b0c8052db00a072a5003b4a
ORNW $0x6006000060060, R5 // ORNW $1689262177517664, R5 // 1b0c8052db00a072a5003b2a
BICSW $0x6006000060060, R5 // BICSW $1689262177517664, R5 // 1b0c8052db00a072a5003b6a
+ AND $1, ZR // fb0340b2ff031b8a
+ ANDW $1, ZR // fb030032ff031b0a
// TODO: this could have better encoding
ANDW $-1, R10 // 1b0080124a011b0a
AND $8, R0, RSP // 1f007d92
@@ -369,9 +371,9 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
MOVD $-1, R1 // 01008092
MOVD $0x210000, R0 // MOVD $2162688, R0 // 2004a0d2
MOVD $0xffffffffffffaaaa, R1 // MOVD $-21846, R1 // a1aa8a92
- MOVW $1, ZR
+ MOVW $1, ZR // 3f008052
MOVW $1, R1
- MOVD $1, ZR
+ MOVD $1, ZR // 3f0080d2
MOVD $1, R1
MOVK $1, R1
MOVD $0x1000100010001000, RSP // MOVD $1152939097061330944, RSP // ff8304b2
@@ -386,10 +388,10 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VMOVQ $0x8040201008040202, $0x7040201008040201, V20 // VMOVQ $-9205322385119247870, $8088500183983456769, V20
// mov(to/from sp)
- MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // fb074091610b0091
- MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // fb0740917f231c91
- MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // fb08409161070091
- MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // fbfc7f9161ff3f91
+ MOVD $0x1002(RSP), R1 // MOVD $4098(RSP), R1 // e107409121080091
+ MOVD $0x1708(RSP), RSP // MOVD $5896(RSP), RSP // ff074091ff231c91
+ MOVD $0x2001(R7), R1 // MOVD $8193(R7), R1 // e108409121040091
+ MOVD $0xffffff(R7), R1 // MOVD $16777215(R7), R1 // e1fc7f9121fc3f91
MOVD $-0x1(R7), R1 // MOVD $-1(R7), R1 // e10400d1
MOVD $-0x30(R7), R1 // MOVD $-48(R7), R1 // e1c000d1
MOVD $-0x708(R7), R1 // MOVD $-1800(R7), R1 // e1201cd1
diff --git a/src/cmd/asm/internal/asm/testdata/arm64error.s b/src/cmd/asm/internal/asm/testdata/arm64error.s
index cf57179e43016156cf148dcbd001c54d5620b00e..3d3de1d9b13770ba9efbd403359a7b0a1abac0d6 100644
--- a/src/cmd/asm/internal/asm/testdata/arm64error.s
+++ b/src/cmd/asm/internal/asm/testdata/arm64error.s
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
TEXT errors(SB),$0
- AND $1, RSP // ERROR "illegal combination"
+ AND $1, RSP // ERROR "illegal source register"
ANDS $1, R0, RSP // ERROR "illegal combination"
ADDSW R7->32, R14, R13 // ERROR "shift amount out of range 0 to 31"
ADD R1.UXTB<<5, R2, R3 // ERROR "shift amount out of range 0 to 4"
@@ -406,12 +406,12 @@ TEXT errors(SB),$0
VBIF V0.D2, V1.D2, V2.D2 // ERROR "invalid arrangement"
VUADDW V9.B8, V12.H8, V14.B8 // ERROR "invalid arrangement"
VUADDW2 V9.B8, V12.S4, V14.S4 // ERROR "operand mismatch"
- VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
- VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
+ VUMAX V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
+ VUMIN V1.D2, V2.D2, V3.D2 // ERROR "invalid arrangement"
VUMAX V1.B8, V2.B8, V3.B16 // ERROR "operand mismatch"
VUMIN V1.H4, V2.S4, V3.H4 // ERROR "operand mismatch"
VSLI $64, V7.D2, V8.D2 // ERROR "shift out of range"
- VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range"
+ VUSRA $0, V7.D2, V8.D2 // ERROR "shift out of range"
CASPD (R3, R4), (R2), (R8, R9) // ERROR "source register pair must start from even register"
CASPD (R2, R3), (R2), (R9, R10) // ERROR "destination register pair must start from even register"
CASPD (R2, R4), (R2), (R8, R9) // ERROR "source register pair must be contiguous"
@@ -419,4 +419,17 @@ TEXT errors(SB),$0
ADD R1>>2, RSP, R3 // ERROR "illegal combination"
ADDS R2<<3, R3, RSP // ERROR "unexpected SP reference"
CMP R1<<5, RSP // ERROR "the left shift amount out of range 0 to 4"
+ MOVD.P y+8(FP), R1 // ERROR "illegal combination"
+ MOVD.W x-8(SP), R1 // ERROR "illegal combination"
+ LDP.P x+8(FP), (R0, R1) // ERROR "illegal combination"
+ LDP.W x+8(SP), (R0, R1) // ERROR "illegal combination"
+ ADD $0x1234567, R27, R3 // ERROR "cannot use REGTMP as source"
+ ADD $0x3fffffffc000, R27, R5 // ERROR "cannot use REGTMP as source"
+ AND $0x22220000, R27, R4 // ERROR "cannot use REGTMP as source"
+ ANDW $0x6006000060060, R27, R5 // ERROR "cannot use REGTMP as source"
+ STP (R3, R4), 0x1234567(R27) // ERROR "REGTMP used in large offset store"
+ LDP 0x1234567(R27), (R3, R4) // ERROR "REGTMP used in large offset load"
+ STP (R26, R27), 700(R2) // ERROR "cannot use REGTMP as source"
+ MOVK $0, R10 // ERROR "zero shifts cannot be handled correctly"
+ MOVK $(0<<32), R10 // ERROR "zero shifts cannot be handled correctly"
RET
diff --git a/src/cmd/asm/internal/asm/testdata/ppc64.s b/src/cmd/asm/internal/asm/testdata/ppc64.s
index b6c0aa5035c013652dcd7df91505d78cfd9ac6a7..c140fd025a64e8117754db20842bffe3e714b3ff 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
@@ -649,6 +649,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
LXVB16X (R3)(R4), VS1 // 7c241ed8
LXVW4X (R3)(R4), VS1 // 7c241e18
LXV 16(R3), VS1 // f4230011
+ LXV 16(R3), VS33 // f4230019
+ LXV 16(R3), V1 // f4230019
LXVL R3, R4, VS1 // 7c23221a
LXVLL R3, R4, VS1 // 7c23225a
LXVX R3, R4, VS1 // 7c232218
@@ -668,8 +670,13 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MTFPRD R3, F0 // 7c030166
MFVRD V0, R3 // 7c030067
MFVSRLD VS63,R4 // 7fe40267
+ MFVSRLD V31,R4 // 7fe40267
MFVSRWZ VS33,R4 // 7c2400e7
+ MFVSRWZ V1,R4 // 7c2400e7
MTVSRD R3, VS1 // 7c230166
+ MTVSRDD R3, R4, VS1 // 7c232366
+ MTVSRDD R3, R4, VS33 // 7c232367
+ MTVSRDD R3, R4, V1 // 7c232367
MTVRD R3, V13 // 7da30167
MTVSRWA R4, VS31 // 7fe401a6
MTVSRWS R4, VS32 // 7c040327
@@ -678,6 +685,8 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
XXBRW VS1, VS2 // f04f0f6c
XXBRH VS2, VS3 // f067176c
XXLAND VS1, VS2, VS3 // f0611410
+ XXLAND V1, V2, V3 // f0611417
+ XXLAND VS33, VS34, VS35 // f0611417
XXLANDC VS1, VS2, VS3 // f0611450
XXLEQV VS0, VS1, VS2 // f0400dd0
XXLNAND VS0, VS1, VS2 // f0400d90
@@ -687,11 +696,17 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
XXLORQ VS1, VS2, VS3 // f0611490
XXLXOR VS1, VS2, VS3 // f06114d0
XXSEL VS1, VS2, VS3, VS4 // f08110f0
+ XXSEL VS33, VS34, VS35, VS36 // f08110ff
+ XXSEL V1, V2, V3, V4 // f08110ff
XXMRGHW VS1, VS2, VS3 // f0611090
XXMRGLW VS1, VS2, VS3 // f0611190
XXSPLTW VS1, $1, VS2 // f0410a90
+ XXSPLTW VS33, $1, VS34 // f0410a93
+ XXSPLTW V1, $1, V2 // f0410a93
XXPERM VS1, VS2, VS3 // f06110d0
XXSLDWI VS1, VS2, $1, VS3 // f0611110
+ XXSLDWI V1, V2, $1, V3 // f0611117
+ XXSLDWI VS33, VS34, $1, VS35 // f0611117
XSCVDPSP VS1, VS2 // f0400c24
XVCVDPSP VS1, VS2 // f0400e24
XSCVSXDDP VS1, VS2 // f0400de0
@@ -736,4 +751,17 @@ TEXT asmtest(SB),DUPOK|NOSPLIT,$0
MOVD XER, R3 // 7c6102a6
MOVFL CR3, CR1 // 4c8c0000
+ MOVW CR0, R1 // 7c380026
+ MOVW CR7, R1 // 7c301026
+ MOVW CR, R1 // 7c200026
+
+ MOVW R1, CR // 7c2ff120
+ MOVFL R1, CR // 7c2ff120
+ MOVW R1, CR2 // 7c320120
+ MOVFL R1, CR2 // 7c320120
+ MOVFL R1, $255 // 7c2ff120
+ MOVFL R1, $1 // 7c301120
+ MOVFL R1, $128 // 7c380120
+ MOVFL R1, $3 // 7c203120
+
RET
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s
index 77c0764c48117a78132e0407837c45dc7194b208..fe911a74f5da6d85c1bf95a7c50678c3ca9cdf37 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64.s
@@ -10,20 +10,35 @@ start:
// 2.4: Integer Computational Instructions
- ADDI $2047, X5, X6 // 1383f27f
- ADDI $-2048, X5, X6 // 13830280
ADDI $2047, X5 // 9382f27f
ADDI $-2048, X5 // 93820280
+ ADDI $2048, X5 // 9382024093820240
+ ADDI $-2049, X5 // 938202c09382f2bf
+ ADDI $4094, X5 // 9382f27f9382f27f
+ ADDI $-4096, X5 // 9382028093820280
+ ADDI $4095, X5 // b71f00009b8fffffb382f201
+ ADDI $-4097, X5 // b7ffffff9b8fffffb382f201
+ ADDI $2047, X5, X6 // 1383f27f
+ ADDI $-2048, X5, X6 // 13830280
+ ADDI $2048, X5, X6 // 1383024013030340
+ ADDI $-2049, X5, X6 // 138302c01303f3bf
+ ADDI $4094, X5, X6 // 1383f27f1303f37f
+ ADDI $-4096, X5, X6 // 1383028013030380
+ ADDI $4095, X5, X6 // b71f00009b8fffff3383f201
+ ADDI $-4097, X5, X6 // b7ffffff9b8fffff3383f201
SLTI $55, X5, X7 // 93a37203
SLTIU $55, X5, X7 // 93b37203
ANDI $1, X5, X6 // 13f31200
ANDI $1, X5 // 93f21200
+ ANDI $2048, X5 // b71f00009b8f0f80b3f2f201
ORI $1, X5, X6 // 13e31200
ORI $1, X5 // 93e21200
+ ORI $2048, X5 // b71f00009b8f0f80b3e2f201
XORI $1, X5, X6 // 13c31200
XORI $1, X5 // 93c21200
+ XORI $2048, X5 // b71f00009b8f0f80b3c2f201
SLLI $1, X5, X6 // 13931200
SLLI $1, X5 // 93921200
@@ -86,20 +101,15 @@ start:
SRA $1, X5 // 93d21240
// 2.5: Control Transfer Instructions
-
- // These jumps and branches get printed as a jump or branch
- // to 2 because they transfer control to the second instruction
- // in the function (the first instruction being an invisible
- // stack pointer adjustment).
- JAL X5, start // JAL X5, 2 // eff25ff0
+ JAL X5, 2(PC) // ef028000
JALR X6, (X5) // 67830200
JALR X6, 4(X5) // 67834200
- BEQ X5, X6, start // BEQ X5, X6, 2 // e38c62ee
- BNE X5, X6, start // BNE X5, X6, 2 // e39a62ee
- BLT X5, X6, start // BLT X5, X6, 2 // e3c862ee
- BLTU X5, X6, start // BLTU X5, X6, 2 // e3e662ee
- BGE X5, X6, start // BGE X5, X6, 2 // e3d462ee
- BGEU X5, X6, start // BGEU X5, X6, 2 // e3f262ee
+ BEQ X5, X6, 2(PC) // 63846200
+ BNE X5, X6, 2(PC) // 63946200
+ BLT X5, X6, 2(PC) // 63c46200
+ BLTU X5, X6, 2(PC) // 63e46200
+ BGE X5, X6, 2(PC) // 63d46200
+ BGEU X5, X6, 2(PC) // 63f46200
// 2.6: Load and Store Instructions
LW (X5), X6 // 03a30200
@@ -219,6 +229,10 @@ start:
FMVSX X5, F0 // 538002f0
FMVXW F0, X5 // d30200e0
FMVWX X5, F0 // 538002f0
+ FMADDS F1, F2, F3, F4 // 43822018
+ FMSUBS F1, F2, F3, F4 // 47822018
+ FNMSUBS F1, F2, F3, F4 // 4b822018
+ FNMADDS F1, F2, F3, F4 // 4f822018
// 11.8: Single-Precision Floating-Point Compare Instructions
FEQS F0, F1, X7 // d3a300a0
@@ -259,6 +273,10 @@ start:
FSGNJXD F1, F0, F2 // 53211022
FMVXD F0, X5 // d30200e2
FMVDX X5, F0 // 538002f2
+ FMADDD F1, F2, F3, F4 // 4382201a
+ FMSUBD F1, F2, F3, F4 // 4782201a
+ FNMSUBD F1, F2, F3, F4 // 4b82201a
+ FNMADDD F1, F2, F3, F4 // 4f82201a
// 12.6: Double-Precision Floating-Point Classify Instruction
FCLASSD F0, X5 // d31200e2
@@ -277,11 +295,17 @@ start:
// MOV pseudo-instructions
MOV X5, X6 // 13830200
- MOV $2047, X5 // 9b02f07f
- MOV $-2048, X5 // 9b020080
-
- // Converted to load of symbol.
- MOV $4294967296, X5 // 97020000
+ MOV $2047, X5 // 9302f07f
+ MOV $-2048, X5 // 93020080
+ MOV $2048, X5 // b71200009b820280
+ MOV $-2049, X5 // b7f2ffff9b82f27f
+ MOV $4096, X5 // b7120000
+ MOV $2147479552, X5 // b7f2ff7f
+ MOV $2147483647, X5 // b70200809b82f2ff
+ MOV $-2147483647, X5 // b70200809b821200
+
+ // Converted to load of symbol (AUIPC + LD)
+ MOV $4294967296, X5 // 9702000083b20200
MOV (X5), X6 // 03b30200
MOV 4(X5), X6 // 03b34200
@@ -325,42 +349,44 @@ start:
NEGW X5 // bb025040
NEGW X5, X6 // 3b035040
- // These jumps can get printed as jumps to 2 because they go to the
- // second instruction in the function (the first instruction is an
- // invisible stack pointer adjustment).
- JMP start // JMP 2 // 6ff01fc2
+ // This jumps to the second instruction in the function (the
+ // first instruction is an invisible stack pointer adjustment).
+ JMP start // JMP 2
+
+ JMP 2(PC) // 6f008000
JMP (X5) // 67800200
JMP 4(X5) // 67804200
- // JMP and CALL to symbol are encoded as:
- // AUIPC $0, TMP
- // JALR $0, TMP
- // with a R_RISCV_PCREL_ITYPE relocation - the linker resolves the
- // real address and updates the immediates for both instructions.
- CALL asmtest(SB) // 970f0000
- JMP asmtest(SB) // 970f0000
+ // CALL and JMP to symbol are encoded as JAL (using LR or ZERO
+ // respectively), with a R_RISCV_CALL relocation. The linker resolves
+ // the real address and updates the immediate, using a trampoline in
+ // the case where the address is not directly reachable.
+ CALL asmtest(SB) // ef000000
+ JMP asmtest(SB) // 6f000000
// Branch pseudo-instructions
- BEQZ X5, start // BEQZ X5, 2 // e38202c0
- BGEZ X5, start // BGEZ X5, 2 // e3d002c0
- BGT X5, X6, start // BGT X5, X6, 2 // e34e53be
- BGTU X5, X6, start // BGTU X5, X6, 2 // e36c53be
- BGTZ X5, start // BGTZ X5, 2 // e34a50be
- BLE X5, X6, start // BLE X5, X6, 2 // e35853be
- BLEU X5, X6, start // BLEU X5, X6, 2 // e37653be
- BLEZ X5, start // BLEZ X5, 2 // e35450be
- BLTZ X5, start // BLTZ X5, 2 // e3c202be
- BNEZ X5, start // BNEZ X5, 2 // e39002be
+ BEQZ X5, 2(PC) // 63840200
+ BGEZ X5, 2(PC) // 63d40200
+ BGT X5, X6, 2(PC) // 63445300
+ BGTU X5, X6, 2(PC) // 63645300
+ BGTZ X5, 2(PC) // 63445000
+ BLE X5, X6, 2(PC) // 63545300
+ BLEU X5, X6, 2(PC) // 63745300
+ BLEZ X5, 2(PC) // 63545000
+ BLTZ X5, 2(PC) // 63c40200
+ BNEZ X5, 2(PC) // 63940200
// Set pseudo-instructions
SEQZ X15, X15 // 93b71700
SNEZ X15, X15 // b337f000
// F extension
+ FABSS F0, F1 // d3200020
FNEGS F0, F1 // d3100020
FNES F0, F1, X7 // d3a300a093c31300
// D extension
+ FABSD F0, F1 // d3200022
FNEGD F0, F1 // d3100022
FNED F0, F1, X5 // d3a200a293c21200
FLTD F0, F1, X5 // d39200a2
diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s
index fb43e68fc1740b131328778f0612ccf99455674f..238552565bdd2f6fe1fe1f9769f7020403a6a5a7 100644
--- a/src/cmd/asm/internal/asm/testdata/riscv64error.s
+++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s
@@ -3,6 +3,14 @@
// license that can be found in the LICENSE file.
TEXT errors(SB),$0
+ MOV $errors(SB), (X5) // ERROR "address load must target register"
+ MOV $8(SP), (X5) // ERROR "address load must target register"
+ MOVB $8(SP), X5 // ERROR "unsupported address load"
+ MOVH $8(SP), X5 // ERROR "unsupported address load"
+ MOVW $8(SP), X5 // ERROR "unsupported address load"
+ MOVF $8(SP), X5 // ERROR "unsupported address load"
+ MOV $1234, 0(SP) // ERROR "constant load must target register"
+ MOV $1234, 8(SP) // ERROR "constant load must target register"
MOV $0, 0(SP) // ERROR "constant load must target register"
MOV $0, 8(SP) // ERROR "constant load must target register"
MOV $1234, 0(SP) // ERROR "constant load must target register"
@@ -11,4 +19,8 @@ TEXT errors(SB),$0
MOVH $1, X5 // ERROR "unsupported constant load"
MOVW $1, X5 // ERROR "unsupported constant load"
MOVF $1, X5 // ERROR "unsupported constant load"
+ MOVBU X5, (X6) // ERROR "unsupported unsigned store"
+ MOVHU X5, (X6) // ERROR "unsupported unsigned store"
+ MOVWU X5, (X6) // ERROR "unsupported unsigned store"
+
RET
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index dd947c7b5ba5b805d4319d78fe432cafdbf1faad..607166e664d90fef67494671f45038e80e9fb6ed 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 043bc696e58ae2fb0fdd74bcc0e5cdcfe9bb7a39..3683527f5b8e582fdecac1fb1dbfa315de5956e9 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/ast.go b/src/cmd/cgo/ast.go
index a073407a961e15af2a7b4cbe92afd925712e82b3..28879e349c49cf371b40a7fb0d09f2e4b285f549 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -338,8 +338,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
// everything else just recurs
default:
- error_(token.NoPos, "unexpected type %T in walk", x)
- panic("unexpected type")
+ f.walkUnexpected(x, context, visit)
case nil:
diff --git a/src/cmd/cgo/ast_go1.go b/src/cmd/cgo/ast_go1.go
new file mode 100644
index 0000000000000000000000000000000000000000..f52bf00d7cb946e9844f485352daec989435a2db
--- /dev/null
+++ b/src/cmd/cgo/ast_go1.go
@@ -0,0 +1,17 @@
+// 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.
+
+//go:build compiler_bootstrap
+// +build compiler_bootstrap
+
+package main
+
+import (
+ "go/token"
+)
+
+func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
+ error_(token.NoPos, "unexpected type %T in walk", x)
+ panic("unexpected type")
+}
diff --git a/src/cmd/cgo/ast_go118.go b/src/cmd/cgo/ast_go118.go
new file mode 100644
index 0000000000000000000000000000000000000000..db0108ed7268366a5dc0543645645bed443a3ecb
--- /dev/null
+++ b/src/cmd/cgo/ast_go118.go
@@ -0,0 +1,25 @@
+// 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.
+
+//go:build !compiler_bootstrap
+// +build !compiler_bootstrap
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
+ switch n := x.(type) {
+ default:
+ error_(token.NoPos, "unexpected type %T in walk", x)
+ panic("unexpected type")
+
+ case *ast.IndexListExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(n.Indices, ctxExpr, visit)
+ }
+}
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index a73e998877af812762afcc4716c17b78adf7f065..997a830994f09bdd6118299e31e20125cadff3ff 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -23,10 +23,13 @@ import (
"internal/xcoff"
"math"
"os"
+ "os/exec"
"strconv"
"strings"
"unicode"
"unicode/utf8"
+
+ "cmd/internal/quoted"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
@@ -382,7 +385,7 @@ func (p *Package) guessKinds(f *File) []*Name {
stderr = p.gccErrors(b.Bytes())
}
if stderr == "" {
- fatalf("%s produced no output\non input:\n%s", p.gccBaseCmd()[0], b.Bytes())
+ fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes())
}
completed := false
@@ -457,7 +460,7 @@ func (p *Package) guessKinds(f *File) []*Name {
}
if !completed {
- fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr)
+ fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr)
}
for i, n := range names {
@@ -488,7 +491,7 @@ func (p *Package) guessKinds(f *File) []*Name {
// to users debugging preamble mistakes. See issue 8442.
preambleErrors := p.gccErrors([]byte(f.Preamble))
if len(preambleErrors) > 0 {
- error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors)
+ error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors)
}
fatalf("unresolved names")
@@ -1503,7 +1506,7 @@ func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr {
Args: []ast.Expr{getNewIdent(name.Mangle)},
}
case "type":
- // Okay - might be new(T)
+ // Okay - might be new(T), T(x), Generic[T], etc.
if r.Name.Type == nil {
error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
}
@@ -1545,20 +1548,37 @@ func gofmtPos(n ast.Expr, pos token.Pos) string {
return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s)
}
-// gccBaseCmd returns the start of the compiler command line.
+// checkGCCBaseCmd returns the start of the compiler command line.
// It uses $CC if set, or else $GCC, or else the compiler recorded
// during the initial build as defaultCC.
// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
-func (p *Package) gccBaseCmd() []string {
+//
+// The compiler command line is split into arguments on whitespace. Quotes
+// are understood, so arguments may contain whitespace.
+//
+// checkGCCBaseCmd confirms that the compiler exists in PATH, returning
+// an error if it does not.
+func checkGCCBaseCmd() ([]string, error) {
// Use $CC if set, since that's what the build uses.
- if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 {
- return ret
+ value := os.Getenv("CC")
+ if value == "" {
+ // Try $GCC if set, since that's what we used to use.
+ value = os.Getenv("GCC")
+ }
+ if value == "" {
+ value = defaultCC(goos, goarch)
}
- // Try $GCC if set, since that's what we used to use.
- if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
- return ret
+ args, err := quoted.Split(value)
+ if err != nil {
+ return nil, err
}
- return strings.Fields(defaultCC(goos, goarch))
+ if len(args) == 0 {
+ return nil, errors.New("CC not set and no default found")
+ }
+ if _, err := exec.LookPath(args[0]); err != nil {
+ return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err)
+ }
+ return args[:len(args):len(args)], nil
}
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
@@ -1604,7 +1624,7 @@ func gccTmp() string {
// gccCmd returns the gcc command line to use for compiling
// the input.
func (p *Package) gccCmd() []string {
- c := append(p.gccBaseCmd(),
+ c := append(gccBaseCmd,
"-w", // no warnings
"-Wno-error", // warnings are not errors
"-o"+gccTmp(), // write object to tmp
@@ -2005,7 +2025,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
// #defines that gcc encountered while processing the input
// and its included files.
func (p *Package) gccDefines(stdin []byte) string {
- base := append(p.gccBaseCmd(), "-E", "-dM", "-xc")
+ base := append(gccBaseCmd, "-E", "-dM", "-xc")
base = append(base, p.gccMachine()...)
stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
return stdout
@@ -2086,6 +2106,9 @@ type typeConv struct {
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
getTypeIDs map[string]bool
+ // badStructs contains C structs that should be marked NotInHeap.
+ notInHeapStructs map[string]bool
+
// Predeclared types.
bool ast.Expr
byte ast.Expr // denotes padding
@@ -2097,6 +2120,7 @@ type typeConv struct {
string ast.Expr
goVoid ast.Expr // _Ctype_void, denotes C's void
goVoidPtr ast.Expr // unsafe.Pointer or *byte
+ goVoidPtrNoHeap ast.Expr // *_Ctype_void_notinheap, like goVoidPtr but marked NotInHeap
ptrSize int64
intSize int64
@@ -2120,6 +2144,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.m = make(map[string]*Type)
c.ptrs = make(map[string][]*Type)
c.getTypeIDs = make(map[string]bool)
+ c.notInHeapStructs = make(map[string]bool)
c.bool = c.Ident("bool")
c.byte = c.Ident("byte")
c.int8 = c.Ident("int8")
@@ -2138,6 +2163,7 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.void = c.Ident("void")
c.string = c.Ident("string")
c.goVoid = c.Ident("_Ctype_void")
+ c.goVoidPtrNoHeap = c.Ident("*_Ctype_void_notinheap")
// Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -godefs uses *byte instead.
@@ -2518,6 +2544,7 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
}
tt.Go = g
+ tt.NotInHeap = c.notInHeapStructs[tag]
typedef[name.Name] = &tt
}
@@ -2561,6 +2588,30 @@ func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Typ
oldType.BadPointer = true
}
}
+ if c.badVoidPointerTypedef(dt) {
+ // Treat this typedef as a pointer to a NotInHeap void.
+ s := *sub
+ s.Go = c.goVoidPtrNoHeap
+ sub = &s
+ // Make sure we update any previously computed type.
+ if oldType := typedef[name.Name]; oldType != nil {
+ oldType.Go = sub.Go
+ }
+ }
+ // Check for non-pointer "struct {...}; typedef struct *"
+ // typedefs that should be marked NotInHeap.
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if strct, ok := ptr.Type.(*dwarf.StructType); ok {
+ if c.badStructPointerTypedef(dt.Name, strct) {
+ c.notInHeapStructs[strct.StructName] = true
+ // Make sure we update any previously computed type.
+ name := "_Ctype_struct_" + strct.StructName
+ if oldType := typedef[name]; oldType != nil {
+ oldType.NotInHeap = true
+ }
+ }
+ }
+ }
t.Go = name
t.BadPointer = sub.BadPointer
t.NotInHeap = sub.NotInHeap
@@ -3010,6 +3061,31 @@ func upper(s string) string {
// so that all fields are exported.
func godefsFields(fld []*ast.Field) {
prefix := fieldPrefix(fld)
+
+ // Issue 48396: check for duplicate field names.
+ if prefix != "" {
+ names := make(map[string]bool)
+ fldLoop:
+ for _, f := range fld {
+ for _, n := range f.Names {
+ name := n.Name
+ if name == "_" {
+ continue
+ }
+ if name != prefix {
+ name = strings.TrimPrefix(n.Name, prefix)
+ }
+ name = upper(name)
+ if names[name] {
+ // Field name conflict: don't remove prefix.
+ prefix = ""
+ break fldLoop
+ }
+ names[name] = true
+ }
+ }
+ }
+
npad := 0
for _, f := range fld {
for _, n := range f.Names {
@@ -3087,6 +3163,48 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
return false
}
+// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be NotInHeap.
+func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool {
+ // Match the Windows HANDLE type (#42018).
+ if goos != "windows" || dt.Name != "HANDLE" {
+ return false
+ }
+ // Check that the typedef is "typedef void *".
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if _, ok := ptr.Type.(*dwarf.VoidType); ok {
+ return true
+ }
+ }
+ return false
+}
+
+// badStructPointerTypedef is like badVoidPointerTypedefs but for structs.
+func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool {
+ // Windows handle types can all potentially contain non-pointers.
+ // badVoidPointerTypedef handles the "void *" HANDLE type, but other
+ // handles are defined as
+ //
+ // struct __{int unused;}; typedef struct __ *name;
+ //
+ // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in
+ // the Windows ntdef.h header,
+ //
+ // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779
+ if goos != "windows" {
+ return false
+ }
+ if len(dt.Field) != 1 {
+ return false
+ }
+ if dt.StructName != name+"__" {
+ return false
+ }
+ if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" {
+ return false
+ }
+ return true
+}
+
// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
// as badPointerTypedef reports.
func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index c6a0c525e64f212bd51bb7a5eb317e46a085ffbc..14642b7576b0122e0252960c498fe9ed11a21826 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -21,7 +21,6 @@ import (
"io"
"io/ioutil"
"os"
- "os/exec"
"path/filepath"
"reflect"
"runtime"
@@ -248,6 +247,7 @@ var importSyscall = flag.Bool("import_syscall", true, "import syscall in generat
var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
var goarch, goos, gomips, gomips64 string
+var gccBaseCmd []string
func main() {
objabi.AddVersionFlag() // -V
@@ -305,10 +305,10 @@ func main() {
p := newPackage(args[:i])
// We need a C compiler to be available. Check this.
- gccName := p.gccBaseCmd()[0]
- _, err := exec.LookPath(gccName)
+ var err error
+ gccBaseCmd, err = checkGCCBaseCmd()
if err != nil {
- fatalf("C compiler %q not found: %v", gccName, err)
+ fatalf("%v", err)
os.Exit(2)
}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 94152f4278cb04b44325a936c50d8d11887f4b3d..4968f7059d9bd6e13789d84798f7b38d7deb4d71 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -59,9 +59,9 @@ func (p *Package) writeDefs() {
// Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n")
if *importRuntimeCgo {
- fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, __SIZE_TYPE__ ctxt) { }\n")
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), __SIZE_TYPE__ ctxt __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void) { return 0; }\n")
- fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n")
+ fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n")
} else {
// If we're not importing runtime/cgo, we *are* runtime/cgo,
@@ -70,8 +70,8 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done(void);\n")
fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n")
}
- fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
- fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
+ fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
+ fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n")
// Write second Go output: definitions of _C_xxx.
@@ -135,6 +135,7 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fgo2, "%s", buf.Bytes())
fmt.Fprintf(fgo2, "\n\n")
}
+ fmt.Fprintf(fgo2, "//go:notinheap\ntype _Ctype_void_notinheap struct{}\n\n")
if *gccgo {
fmt.Fprintf(fgo2, "type _Ctype_void byte\n")
} else {
@@ -1054,9 +1055,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
+ fmt.Fprintf(fgo2, "\t")
+
if gccResult != "void" {
// Write results back to frame.
- fmt.Fprintf(fgo2, "\t")
forFieldList(fntype.Results,
func(i int, aname string, atype ast.Expr) {
if i > 0 {
@@ -1458,10 +1460,10 @@ const gccProlog = `
(have a negative array count) and an inscrutable error will come
out of the compiler and hopefully mention "name".
*/
-#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
+#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL];
/* Check at compile time that the sizes we use match our expectations. */
-#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), n, _cgo_sizeof_##t##_is_not_##n)
+#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n)
__cgo_size_assert(char, 1)
__cgo_size_assert(short, 2)
diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md
index 2bb4055083263044df3c400a52a3cf6dd27dde40..7fe446366568124dc9d9c4559d28b6198cdb4d1e 100644
--- a/src/cmd/compile/abi-internal.md
+++ b/src/cmd/compile/abi-internal.md
@@ -410,7 +410,11 @@ Special-purpose registers are as follows:
| R13 | Scratch | Scratch | Scratch |
| R14 | Current goroutine | Same | Same |
| R15 | GOT reference temporary if dynlink | Same | Same |
-| X15 | Zero value | Same | Scratch |
+| X15 | Zero value (*) | Same | Scratch |
+
+(*) Except on Plan 9, where X15 is a scratch register because SSE
+registers cannot be used in note handlers (so the compiler avoids
+using them except when absolutely necessary).
*Rationale*: These register meanings are compatible with Go’s
stack-based calling convention except for R14 and X15, which will have
@@ -505,6 +509,227 @@ control bits specified by the ELF AMD64 ABI.
The x87 floating-point control word is not used by Go on amd64.
+### arm64 architecture
+
+The arm64 architecture uses R0 – R15 for integer arguments and results.
+
+It uses F0 – F15 for floating-point arguments and results.
+
+*Rationale*: 16 integer registers and 16 floating-point registers are
+more than enough for passing arguments and results for practically all
+functions (see Appendix). While there are more registers available,
+using more registers provides little benefit. Additionally, it will add
+overhead on code paths where the number of arguments are not statically
+known (e.g. reflect call), and will consume more stack space when there
+is only limited stack space available to fit in the nosplit limit.
+
+Registers R16 and R17 are permanent scratch registers. They are also
+used as scratch registers by the linker (Go linker and external
+linker) in trampolines.
+
+Register R18 is reserved and never used. It is reserved for the OS
+on some platforms (e.g. macOS).
+
+Registers R19 – R25 are permanent scratch registers. In addition,
+R27 is a permanent scratch register used by the assembler when
+expanding instructions.
+
+Floating-point registers F16 – F31 are also permanent scratch
+registers.
+
+Special-purpose registers are as follows:
+
+| Register | Call meaning | Return meaning | Body meaning |
+| --- | --- | --- | --- |
+| RSP | Stack pointer | Same | Same |
+| R30 | Link register | Same | Scratch (non-leaf functions) |
+| R29 | Frame pointer | Same | Same |
+| R28 | Current goroutine | Same | Same |
+| R27 | Scratch | Scratch | Scratch |
+| R26 | Closure context pointer | Scratch | Scratch |
+| R18 | Reserved (not used) | Same | Same |
+| ZR | Zero value | Same | Same |
+
+*Rationale*: These register meanings are compatible with Go’s
+stack-based calling convention.
+
+*Rationale*: The link register, R30, holds the function return
+address at the function entry. For functions that have frames
+(including most non-leaf functions), R30 is saved to stack in the
+function prologue and restored in the epilogue. Within the function
+body, R30 can be used as a scratch register.
+
+*Implementation note*: Registers with fixed meaning at calls but not
+in function bodies must be initialized by "injected" calls such as
+signal-based panics.
+
+#### Stack layout
+
+The stack pointer, RSP, grows down and is always aligned to 16 bytes.
+
+*Rationale*: The arm64 architecture requires the stack pointer to be
+16-byte aligned.
+
+A function's stack frame, after the frame is created, is laid out as
+follows:
+
+ +------------------------------+
+ | ... locals ... |
+ | ... outgoing arguments ... |
+ | return PC | ← RSP points to
+ | frame pointer on entry |
+ +------------------------------+ ↓ lower addresses
+
+The "return PC" is loaded to the link register, R30, as part of the
+arm64 `CALL` operation.
+
+On entry, a function subtracts from RSP to open its stack frame, and
+saves the values of R30 and R29 at the bottom of the frame.
+Specifically, R30 is saved at 0(RSP) and R29 is saved at -8(RSP),
+after RSP is updated.
+
+A leaf function that does not require any stack space may omit the
+saved R30 and R29.
+
+The Go ABI's use of R29 as a frame pointer register is compatible with
+arm64 architecture requirement so that Go can inter-operate with platform
+debuggers and profilers.
+
+This stack layout is used by both register-based (ABIInternal) and
+stack-based (ABI0) calling conventions.
+
+#### Flags
+
+The arithmetic status flags (NZCV) are treated like scratch registers
+and not preserved across calls.
+All other bits in PSTATE are system flags and are not modified by Go.
+
+The floating-point status register (FPSR) is treated like scratch
+registers and not preserved across calls.
+
+At calls, the floating-point control register (FPCR) bits are always
+set as follows:
+
+| Flag | Bit | Value | Meaning |
+| --- | --- | --- | --- |
+| DN | 25 | 0 | Propagate NaN operands |
+| FZ | 24 | 0 | Do not flush to zero |
+| RC | 23/22 | 0 (RN) | Round to nearest, choose even if tied |
+| IDE | 15 | 0 | Denormal operations trap disabled |
+| IXE | 12 | 0 | Inexact trap disabled |
+| UFE | 11 | 0 | Underflow trap disabled |
+| OFE | 10 | 0 | Overflow trap disabled |
+| DZE | 9 | 0 | Divide-by-zero trap disabled |
+| IOE | 8 | 0 | Invalid operations trap disabled |
+| NEP | 2 | 0 | Scalar operations do not affect higher elements in vector registers |
+| AH | 1 | 0 | No alternate handling of de-normal inputs |
+| FIZ | 0 | 0 | Do not zero de-normals |
+
+*Rationale*: Having a fixed FPCR control configuration allows Go
+functions to use floating-point and vector (SIMD) operations without
+modifying or saving the FPCR.
+Functions are allowed to modify it between calls (as long as they
+restore it), but as of this writing Go code never does.
+
+### ppc64 architecture
+
+The ppc64 architecture uses R3 – R10 and R14 – R17 for integer arguments
+and results.
+
+It uses F1 – F12 for floating-point arguments and results.
+
+Register R31 is a permanent scratch register in Go.
+
+Special-purpose registers used within Go generated code and Go
+assembly code are as follows:
+
+| Register | Call meaning | Return meaning | Body meaning |
+| --- | --- | --- | --- |
+| R0 | Zero value | Same | Same |
+| R1 | Stack pointer | Same | Same |
+| R2 | TOC register | Same | Same |
+| R11 | Closure context pointer | Scratch | Scratch |
+| R12 | Function address on indirect calls | Scratch | Scratch |
+| R13 | TLS pointer | Same | Same |
+| R20,R21 | Scratch | Scratch | Used by duffcopy, duffzero |
+| R30 | Current goroutine | Same | Same |
+| R31 | Scratch | Scratch | Scratch |
+| LR | Link register | Link register | Scratch |
+*Rationale*: These register meanings are compatible with Go’s
+stack-based calling convention.
+
+The link register, LR, holds the function return
+address at the function entry and is set to the correct return
+address before exiting the function. It is also used
+in some cases as the function address when doing an indirect call.
+
+The register R2 contains the address of the TOC (table of contents) which
+contains data or code addresses used when generating position independent
+code. Non-Go code generated when using cgo contains TOC-relative addresses
+which depend on R2 holding a valid TOC. Go code compiled with -shared or
+-dynlink initializes and maintains R2 and uses it in some cases for
+function calls; Go code compiled without these options does not modify R2.
+
+When making a function call R12 contains the function address for use by the
+code to generate R2 at the beginning of the function. R12 can be used for
+other purposes within the body of the function, such as trampoline generation.
+
+R20 and R21 are used in duffcopy and duffzero which could be generated
+before arguments are saved so should not be used for register arguments.
+
+The Count register CTR can be used as the call target for some branch instructions.
+It holds the return address when preemption has occurred.
+
+On PPC64 when a float32 is loaded it becomes a float64 in the register, which is
+different from other platforms and that needs to be recognized by the internal
+implementation of reflection so that float32 arguments are passed correctly.
+
+Registers R18 - R29 and F13 - F31 are considered scratch registers.
+
+#### Stack layout
+
+The stack pointer, R1, grows down and is aligned to 8 bytes in Go, but changed
+to 16 bytes when calling cgo.
+
+A function's stack frame, after the frame is created, is laid out as
+follows:
+
+ +------------------------------+
+ | ... locals ... |
+ | ... outgoing arguments ... |
+ | 24 TOC register R2 save | When compiled with -shared/-dynlink
+ | 16 Unused in Go | Not used in Go
+ | 8 CR save | nonvolatile CR fields
+ | 0 return PC | ← R1 points to
+ +------------------------------+ ↓ lower addresses
+
+The "return PC" is loaded to the link register, LR, as part of the
+ppc64 `BL` operations.
+
+On entry to a non-leaf function, the stack frame size is subtracted from R1 to
+create its stack frame, and saves the value of LR at the bottom of the frame.
+
+A leaf function that does not require any stack space does not modify R1 and
+does not save LR.
+
+*NOTE*: We might need to save the frame pointer on the stack as
+in the PPC64 ELF v2 ABI so Go can inter-operate with platform debuggers
+and profilers.
+
+This stack layout is used by both register-based (ABIInternal) and
+stack-based (ABI0) calling conventions.
+
+#### Flags
+
+The condition register consists of 8 condition code register fields
+CR0-CR7. Go generated code only sets and uses CR0, commonly set by
+compare functions and use to determine the target of a conditional
+branch. The generated code does not set or use CR1-CR7.
+
+The floating point status and control register (FPSCR) is initialized
+to 0 by the kernel at startup of the Go program and not changed by
+the Go generated code.
+
## Future directions
### Spill path improvements
diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go
index b68ef274f379e0fa932b54307afafb0b8db1cfdf..ef7fa86749f0137d03c949a08dcf504cb58bb56f 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 d657ddc867bad00f44a00a3e9af54b7507b99683..529150a390b1d577e93bfa554bb2a42553c77549 100644
--- a/src/cmd/compile/internal/abi/abiutils.go
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -144,7 +144,7 @@ func (pa *ABIParamAssignment) RegisterTypesAndOffsets() ([]*types.Type, []int64)
}
func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
- w := t.Width
+ w := t.Size()
if w == 0 {
return rts
}
@@ -193,12 +193,12 @@ func appendParamTypes(rts []*types.Type, t *types.Type) []*types.Type {
// to input offsets, and returns the longer slice and the next unused offset.
func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int64) {
at = align(at, t)
- w := t.Width
+ w := t.Size()
if w == 0 {
return offsets, at
}
if t.IsScalar() || t.IsPtrShaped() {
- if t.IsComplex() || int(t.Width) > types.RegSize { // complex and *int64 on 32-bit
+ if t.IsComplex() || int(t.Size()) > types.RegSize { // complex and *int64 on 32-bit
s := w / 2
return append(offsets, at, at+s), at + w
} else {
@@ -214,7 +214,7 @@ func appendParamOffsets(offsets []int64, at int64, t *types.Type) ([]int64, int6
case types.TSTRUCT:
for i, f := range t.FieldSlice() {
offsets, at = appendParamOffsets(offsets, at, f.Type)
- if f.Type.Width == 0 && i == t.NumFields()-1 {
+ if f.Type.Size() == 0 && i == t.NumFields()-1 {
at++ // last field has zero width
}
}
@@ -531,7 +531,7 @@ type assignState struct {
// align returns a rounded up to t's alignment
func align(a int64, t *types.Type) int64 {
- return alignTo(a, int(t.Align))
+ return alignTo(a, int(uint8(t.Alignment())))
}
// alignTo returns a rounded up to t, where t must be 0 or a power of 2.
@@ -546,7 +546,7 @@ func alignTo(a int64, t int) int64 {
// specified type.
func (state *assignState) stackSlot(t *types.Type) int64 {
rv := align(state.stackOffset, t)
- state.stackOffset = rv + t.Width
+ state.stackOffset = rv + t.Size()
return rv
}
@@ -554,7 +554,7 @@ func (state *assignState) stackSlot(t *types.Type) int64 {
// that we've just determined to be register-assignable. The number of registers
// needed is assumed to be stored in state.pUsed.
func (state *assignState) allocateRegs(regs []RegIndex, t *types.Type) []RegIndex {
- if t.Width == 0 {
+ if t.Size() == 0 {
return regs
}
ri := state.rUsed.intRegs
@@ -647,7 +647,7 @@ func (state *assignState) floatUsed() int {
// can register allocate, FALSE otherwise (and updates state
// accordingly).
func (state *assignState) regassignIntegral(t *types.Type) bool {
- regsNeeded := int(types.Rnd(t.Width, int64(types.PtrSize)) / int64(types.PtrSize))
+ regsNeeded := int(types.Rnd(t.Size(), int64(types.PtrSize)) / int64(types.PtrSize))
if t.IsComplex() {
regsNeeded = 2
}
@@ -715,21 +715,25 @@ func setup() {
synthOnce.Do(func() {
fname := types.BuiltinPkg.Lookup
nxp := src.NoXPos
- unsp := types.Types[types.TUNSAFEPTR]
- ui := types.Types[types.TUINTPTR]
+ bp := types.NewPtr(types.Types[types.TUINT8])
+ it := types.Types[types.TINT]
synthSlice = types.NewStruct(types.NoPkg, []*types.Field{
- types.NewField(nxp, fname("ptr"), unsp),
- types.NewField(nxp, fname("len"), ui),
- types.NewField(nxp, fname("cap"), ui),
+ types.NewField(nxp, fname("ptr"), bp),
+ types.NewField(nxp, fname("len"), it),
+ types.NewField(nxp, fname("cap"), it),
})
+ types.CalcStructSize(synthSlice)
synthString = types.NewStruct(types.NoPkg, []*types.Field{
- types.NewField(nxp, fname("data"), unsp),
- types.NewField(nxp, fname("len"), ui),
+ types.NewField(nxp, fname("data"), bp),
+ types.NewField(nxp, fname("len"), it),
})
+ types.CalcStructSize(synthString)
+ unsp := types.Types[types.TUNSAFEPTR]
synthIface = types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(nxp, fname("f1"), unsp),
types.NewField(nxp, fname("f2"), unsp),
})
+ types.CalcStructSize(synthIface)
})
}
@@ -764,10 +768,10 @@ func (state *assignState) regassign(pt *types.Type) bool {
// ABIParamResultInfo held in 'state'.
func (state *assignState) assignParamOrReturn(pt *types.Type, n types.Object, isReturn bool) ABIParamAssignment {
state.pUsed = RegAmounts{}
- if pt.Width == types.BADWIDTH {
+ if pt.Size() == types.BADWIDTH {
base.Fatalf("should never happen")
panic("unreachable")
- } else if pt.Width == 0 {
+ } else if pt.Size() == 0 {
return state.stackAllocate(pt, n)
} else if state.regassign(pt) {
return state.regAllocate(pt, n, isReturn)
@@ -777,11 +781,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 {
@@ -793,8 +797,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/galign.go b/src/cmd/compile/internal/amd64/galign.go
index 2785aa03368b782c3d5d9b5c8f983d0dac19b1d6..ca44263afc476c4eef177df64562920b13e7afdc 100644
--- a/src/cmd/compile/internal/amd64/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -18,11 +18,10 @@ func Init(arch *ssagen.ArchInfo) {
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
- arch.LoadRegResults = loadRegResults
+ arch.LoadRegResult = loadRegResult
arch.SpillArgReg = spillArgReg
}
diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
index 1484ad5404b4797552195b597fd0e22cab00e85b..b8dce81a92d2cc9013e075b6698e8acc2b8b2fbf 100644
--- a/src/cmd/compile/internal/amd64/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -57,7 +57,6 @@ func dzDI(b int64) int64 {
func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.Prog {
const (
r13 = 1 << iota // if R13 is already zeroed.
- x15 // if X15 is already zeroed. Note: in new ABI, X15 is always zero.
)
if cnt == 0 {
@@ -85,11 +84,6 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.
}
p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_R13, 0, obj.TYPE_MEM, x86.REG_SP, off)
} else if !isPlan9 && cnt <= int64(8*types.RegSize) {
- if !buildcfg.Experiment.RegabiG && *state&x15 == 0 {
- p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0)
- *state |= x15
- }
-
for i := int64(0); i < cnt/16; i++ {
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
}
@@ -98,10 +92,6 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.
p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
}
} else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
- if !buildcfg.Experiment.RegabiG && *state&x15 == 0 {
- p = pp.Append(p, x86.AXORPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_REG, x86.REG_X15, 0)
- *state |= x15
- }
// Save DI to r12. With the amd64 Go register abi, DI can contain
// an incoming parameter, whereas R12 is always scratch.
p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_DI, 0, obj.TYPE_REG, x86.REG_R12, 0)
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index ca5f36e77598c6ff54b6c3288b086f40a9f999d4..b0e5c34030f32b9d6b62159a7fef03e4dc3a5e80 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -263,6 +263,24 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = lo
p.SetFrom3Reg(hi)
+ case ssa.OpAMD64BLSIQ, ssa.OpAMD64BLSIL,
+ ssa.OpAMD64BLSMSKQ, ssa.OpAMD64BLSMSKL,
+ ssa.OpAMD64BLSRQ, ssa.OpAMD64BLSRL,
+ ssa.OpAMD64TZCNTQ, ssa.OpAMD64TZCNTL:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = v.Args[0].Reg()
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+
+ case ssa.OpAMD64ANDNQ, ssa.OpAMD64ANDNL:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = v.Args[0].Reg()
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+ p.SetFrom3Reg(v.Args[1].Reg())
+
case ssa.OpAMD64DIVQU, ssa.OpAMD64DIVLU, ssa.OpAMD64DIVWU:
// Arg[0] (the dividend) is in AX.
// Arg[1] (the divisor) can be in any other register.
@@ -600,8 +618,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = r
p.SetFrom3Reg(v.Args[0].Reg())
+ case ssa.OpAMD64ANDQconst:
+ asm := v.Op.Asm()
+ // If the constant is positive and fits into 32 bits, use ANDL.
+ // This saves a few bytes of encoding.
+ if 0 <= v.AuxInt && v.AuxInt <= (1<<32-1) {
+ asm = x86.AANDL
+ }
+ p := s.Prog(asm)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
+
case ssa.OpAMD64SUBQconst, ssa.OpAMD64SUBLconst,
- ssa.OpAMD64ANDQconst, ssa.OpAMD64ANDLconst,
+ ssa.OpAMD64ANDLconst,
ssa.OpAMD64ORQconst, ssa.OpAMD64ORLconst,
ssa.OpAMD64XORQconst, ssa.OpAMD64XORLconst,
ssa.OpAMD64SHLQconst, ssa.OpAMD64SHLLconst,
@@ -741,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()
@@ -757,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()
@@ -822,8 +856,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux2(&p.To, v, sc.Off64())
- case ssa.OpAMD64MOVOstorezero:
- if !buildcfg.Experiment.RegabiG || s.ABI != obj.ABIInternal {
+ case ssa.OpAMD64MOVOstoreconst:
+ sc := v.AuxValAndOff()
+ if sc.Val() != 0 {
+ v.Fatalf("MOVO for non zero constants not implemented: %s", v.LongString())
+ }
+
+ if s.ABI != obj.ABIInternal {
// zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
@@ -832,7 +871,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = x86.REG_X15
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
- ssagen.AddAux(&p.To, v)
+ ssagen.AddAux2(&p.To, v, sc.Off64())
+
case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4, ssa.OpAMD64MOVWstoreconstidx1, ssa.OpAMD64MOVWstoreconstidx2, ssa.OpAMD64MOVBstoreconstidx1,
ssa.OpAMD64ADDLconstmodifyidx1, ssa.OpAMD64ADDLconstmodifyidx4, ssa.OpAMD64ADDLconstmodifyidx8, ssa.OpAMD64ADDQconstmodifyidx1, ssa.OpAMD64ADDQconstmodifyidx8,
ssa.OpAMD64ANDLconstmodifyidx1, ssa.OpAMD64ANDLconstmodifyidx4, ssa.OpAMD64ANDLconstmodifyidx8, ssa.OpAMD64ANDQconstmodifyidx1, ssa.OpAMD64ANDQconstmodifyidx8,
@@ -914,7 +954,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64DUFFZERO:
- if !buildcfg.Experiment.RegabiG || s.ABI != obj.ABIInternal {
+ if s.ABI != obj.ABIInternal {
// zero X15 manually
opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
}
@@ -997,22 +1037,30 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// Closure pointer is DX.
ssagen.CheckLoweredGetClosurePtr(v)
case ssa.OpAMD64LoweredGetG:
- if buildcfg.Experiment.RegabiG && s.ABI == obj.ABIInternal {
+ if s.ABI == obj.ABIInternal {
v.Fatalf("LoweredGetG should not appear in ABIInternal")
}
r := v.Reg()
getgFromTLS(s, r)
- case ssa.OpAMD64CALLstatic:
- if buildcfg.Experiment.RegabiG && s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
+ case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
+ if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
// zeroing X15 when entering ABIInternal from ABI0
- opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ }
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
+ if v.Op == ssa.OpAMD64CALLtail {
+ s.TailCall(v)
+ break
+ }
s.Call(v)
- if buildcfg.Experiment.RegabiG && s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
+ if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
// zeroing X15 when entering ABIInternal from ABI0
- opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
+ opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
+ }
// set G register from TLS
getgFromTLS(s, x86.REG_R14)
}
@@ -1093,7 +1141,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
if v.Args[0].Reg() != v.Reg() {
// POPCNT on Intel has a false dependency on the destination register.
// Xor register with itself to break the dependency.
- p := s.Prog(x86.AXORQ)
+ p := s.Prog(x86.AXORL)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Reg()
p.To.Type = obj.TYPE_REG
@@ -1221,6 +1269,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
ssagen.AddAux(&p.To, v)
+ case ssa.OpAMD64PrefetchT0, ssa.OpAMD64PrefetchNTA:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
case ssa.OpClobber:
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_CONST
@@ -1300,20 +1352,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- if buildcfg.Experiment.RegabiG && s.ABI == obj.ABI0 && b.Aux.(*obj.LSym).ABI() == obj.ABIInternal {
- // zeroing X15 when entering ABIInternal from ABI0
- opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
- // set G register from TLS
- getgFromTLS(s, x86.REG_R14)
- }
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockAMD64EQF:
s.CombJump(b, next, &eqfJumps)
@@ -1348,20 +1389,15 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
}
}
-func loadRegResults(s *ssagen.State, f *ssa.Func) {
- for _, o := range f.OwnAux.ABIInfo().OutParams() {
- n := o.Name.(*ir.Name)
- rts, offs := o.RegisterTypesAndOffsets()
- for i := range o.Registers {
- p := s.Prog(loadByType(rts[i]))
- p.From.Type = obj.TYPE_MEM
- p.From.Name = obj.NAME_AUTO
- p.From.Sym = n.Linksym()
- p.From.Offset = n.FrameOffset() + offs[i]
- p.To.Type = obj.TYPE_REG
- p.To.Reg = ssa.ObjRegForAbiReg(o.Registers[i], f.Config)
- }
- }
+func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
+ p := s.Prog(loadByType(t))
+ p.From.Type = obj.TYPE_MEM
+ p.From.Name = obj.NAME_AUTO
+ p.From.Sym = n.Linksym()
+ p.From.Offset = n.FrameOffset() + off
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = reg
+ return p
}
func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7aa697b811442b3367f83998f1bdfb7b056701b8
--- /dev/null
+++ b/src/cmd/compile/internal/amd64/versions_test.go
@@ -0,0 +1,393 @@
+// 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 amd64_test
+
+import (
+ "bufio"
+ "debug/elf"
+ "debug/macho"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "math"
+ "math/bits"
+ "os"
+ "os/exec"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+// Test to make sure that when building for GOAMD64=v1, we don't
+// use any >v1 instructions.
+func TestGoAMD64v1(t *testing.T) {
+ if runtime.GOARCH != "amd64" {
+ t.Skip("amd64-only test")
+ }
+ if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
+ t.Skip("test only works on elf or macho platforms")
+ }
+ if v := os.Getenv("GOAMD64"); v != "" && v != "v1" {
+ // Test runs only on v1 (which is the default).
+ // TODO: use build tags from #45454 instead.
+ t.Skip("GOAMD64 already set")
+ }
+ if os.Getenv("TESTGOAMD64V1") != "" {
+ t.Skip("recursive call")
+ }
+
+ // Make a binary which will be a modified version of the
+ // currently running binary.
+ dst, err := os.CreateTemp("", "TestGoAMD64v1")
+ if err != nil {
+ t.Fatalf("failed to create temp file: %v", err)
+ }
+ defer os.Remove(dst.Name())
+ dst.Chmod(0500) // make executable
+
+ // Clobber all the non-v1 opcodes.
+ opcodes := map[string]bool{}
+ var features []string
+ for feature, opcodeList := range featureToOpcodes {
+ if runtimeFeatures[feature] {
+ features = append(features, fmt.Sprintf("cpu.%s=off", feature))
+ }
+ for _, op := range opcodeList {
+ opcodes[op] = true
+ }
+ }
+ clobber(t, os.Args[0], dst, opcodes)
+ if err = dst.Close(); err != nil {
+ t.Fatalf("can't close binary: %v", err)
+ }
+
+ // Run the resulting binary.
+ cmd := exec.Command(dst.Name())
+ testenv.CleanCmdEnv(cmd)
+ cmd.Env = append(cmd.Env, "TESTGOAMD64V1=yes")
+ cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=%s", strings.Join(features, ",")))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("couldn't execute test: %s", err)
+ }
+ // Expect to see output of the form "PASS\n", unless the test binary
+ // was compiled for coverage (in which case there will be an extra line).
+ success := false
+ lines := strings.Split(string(out), "\n")
+ if len(lines) == 2 {
+ success = lines[0] == "PASS" && lines[1] == ""
+ } else if len(lines) == 3 {
+ success = lines[0] == "PASS" &&
+ strings.HasPrefix(lines[1], "coverage") && lines[2] == ""
+ }
+ if !success {
+ t.Fatalf("test reported error: %s lines=%+v", string(out), lines)
+ }
+}
+
+// Clobber copies the binary src to dst, replacing all the instructions in opcodes with
+// faulting instructions.
+func clobber(t *testing.T, src string, dst *os.File, opcodes map[string]bool) {
+ // Run objdump to get disassembly.
+ var re *regexp.Regexp
+ var disasm io.Reader
+ if false {
+ // TODO: go tool objdump doesn't disassemble the bmi1 instructions
+ // in question correctly. See issue 48584.
+ cmd := exec.Command("go", "tool", "objdump", src)
+ var err error
+ disasm, err = cmd.StdoutPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+ re = regexp.MustCompile(`^[^:]*:[-0-9]+\s+0x([0-9a-f]+)\s+([0-9a-f]+)\s+([A-Z]+)`)
+ } else {
+ // TODO: we're depending on platform-native objdump here. Hence the Skipf
+ // below if it doesn't run for some reason.
+ cmd := exec.Command("objdump", "-d", src)
+ var err error
+ disasm, err = cmd.StdoutPipe()
+ if err != nil {
+ t.Skipf("can't run test due to missing objdump: %s", err)
+ }
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+ re = regexp.MustCompile(`^\s*([0-9a-f]+):\s*((?:[0-9a-f][0-9a-f] )+)\s*([a-z0-9]+)`)
+ }
+
+ // Find all the instruction addresses we need to edit.
+ virtualEdits := map[uint64]bool{}
+ scanner := bufio.NewScanner(disasm)
+ for scanner.Scan() {
+ line := scanner.Text()
+ parts := re.FindStringSubmatch(line)
+ if len(parts) == 0 {
+ continue
+ }
+ addr, err := strconv.ParseUint(parts[1], 16, 64)
+ if err != nil {
+ continue // not a hex address
+ }
+ opcode := strings.ToLower(parts[3])
+ if !opcodes[opcode] {
+ continue
+ }
+ t.Logf("clobbering instruction %s", line)
+ n := (len(parts[2]) - strings.Count(parts[2], " ")) / 2 // number of bytes in instruction encoding
+ for i := 0; i < n; i++ {
+ // Only really need to make the first byte faulting, but might
+ // as well make all the bytes faulting.
+ virtualEdits[addr+uint64(i)] = true
+ }
+ }
+
+ // Figure out where in the binary the edits must be done.
+ physicalEdits := map[uint64]bool{}
+ if e, err := elf.Open(src); err == nil {
+ for _, sec := range e.Sections {
+ vaddr := sec.Addr
+ paddr := sec.Offset
+ size := sec.Size
+ for a := range virtualEdits {
+ if a >= vaddr && a < vaddr+size {
+ physicalEdits[paddr+(a-vaddr)] = true
+ }
+ }
+ }
+ } else if m, err2 := macho.Open(src); err2 == nil {
+ for _, sec := range m.Sections {
+ vaddr := sec.Addr
+ paddr := uint64(sec.Offset)
+ size := sec.Size
+ for a := range virtualEdits {
+ if a >= vaddr && a < vaddr+size {
+ physicalEdits[paddr+(a-vaddr)] = true
+ }
+ }
+ }
+ } else {
+ t.Log(err)
+ t.Log(err2)
+ t.Fatal("executable format not elf or macho")
+ }
+ if len(virtualEdits) != len(physicalEdits) {
+ t.Fatal("couldn't find an instruction in text sections")
+ }
+
+ // Copy source to destination, making edits along the way.
+ f, err := os.Open(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ r := bufio.NewReader(f)
+ w := bufio.NewWriter(dst)
+ a := uint64(0)
+ done := 0
+ for {
+ b, err := r.ReadByte()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal("can't read")
+ }
+ if physicalEdits[a] {
+ b = 0xcc // INT3 opcode
+ done++
+ }
+ err = w.WriteByte(b)
+ if err != nil {
+ t.Fatal("can't write")
+ }
+ a++
+ }
+ if done != len(physicalEdits) {
+ t.Fatal("physical edits remaining")
+ }
+ w.Flush()
+ 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": {"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
+func TestPopCnt(t *testing.T) {
+ for _, tt := range []struct {
+ x uint64
+ want int
+ }{
+ {0b00001111, 4},
+ {0b00001110, 3},
+ {0b00001100, 2},
+ {0b00000000, 0},
+ } {
+ if got := bits.OnesCount64(tt.x); got != tt.want {
+ t.Errorf("OnesCount64(%#x) = %d, want %d", tt.x, got, tt.want)
+ }
+ if got := bits.OnesCount32(uint32(tt.x)); got != tt.want {
+ t.Errorf("OnesCount32(%#x) = %d, want %d", tt.x, got, tt.want)
+ }
+ }
+}
+
+// Test to use ANDN, if available
+func TestAndNot(t *testing.T) {
+ for _, tt := range []struct {
+ x, y, want uint64
+ }{
+ {0b00001111, 0b00000011, 0b1100},
+ {0b00001111, 0b00001100, 0b0011},
+ {0b00000000, 0b00000000, 0b0000},
+ } {
+ if got := tt.x &^ tt.y; got != tt.want {
+ t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want)
+ }
+ if got := uint32(tt.x) &^ uint32(tt.y); got != uint32(tt.want) {
+ t.Errorf("%#x &^ %#x = %#x, want %#x", tt.x, tt.y, got, tt.want)
+ }
+ }
+}
+
+// Test to use BLSI, if available
+func TestBLSI(t *testing.T) {
+ for _, tt := range []struct {
+ x, want uint64
+ }{
+ {0b00001111, 0b001},
+ {0b00001110, 0b010},
+ {0b00001100, 0b100},
+ {0b11000110, 0b010},
+ {0b00000000, 0b000},
+ } {
+ if got := tt.x & -tt.x; got != tt.want {
+ t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want)
+ }
+ if got := uint32(tt.x) & -uint32(tt.x); got != uint32(tt.want) {
+ t.Errorf("%#x & (-%#x) = %#x, want %#x", tt.x, tt.x, got, tt.want)
+ }
+ }
+}
+
+// Test to use BLSMSK, if available
+func TestBLSMSK(t *testing.T) {
+ for _, tt := range []struct {
+ x, want uint64
+ }{
+ {0b00001111, 0b001},
+ {0b00001110, 0b011},
+ {0b00001100, 0b111},
+ {0b11000110, 0b011},
+ {0b00000000, 1<<64 - 1},
+ } {
+ if got := tt.x ^ (tt.x - 1); got != tt.want {
+ t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
+ }
+ if got := uint32(tt.x) ^ (uint32(tt.x) - 1); got != uint32(tt.want) {
+ t.Errorf("%#x ^ (%#x-1) = %#x, want %#x", tt.x, tt.x, got, uint32(tt.want))
+ }
+ }
+}
+
+// Test to use BLSR, if available
+func TestBLSR(t *testing.T) {
+ for _, tt := range []struct {
+ x, want uint64
+ }{
+ {0b00001111, 0b00001110},
+ {0b00001110, 0b00001100},
+ {0b00001100, 0b00001000},
+ {0b11000110, 0b11000100},
+ {0b00000000, 0b00000000},
+ } {
+ if got := tt.x & (tt.x - 1); got != tt.want {
+ t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
+ }
+ if got := uint32(tt.x) & (uint32(tt.x) - 1); got != uint32(tt.want) {
+ t.Errorf("%#x & (%#x-1) = %#x, want %#x", tt.x, tt.x, got, tt.want)
+ }
+ }
+}
+
+func TestTrailingZeros(t *testing.T) {
+ for _, tt := range []struct {
+ x uint64
+ want int
+ }{
+ {0b00001111, 0},
+ {0b00001110, 1},
+ {0b00001100, 2},
+ {0b00001000, 3},
+ {0b00000000, 64},
+ } {
+ if got := bits.TrailingZeros64(tt.x); got != tt.want {
+ t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, tt.want)
+ }
+ want := tt.want
+ if want == 64 {
+ want = 32
+ }
+ if got := bits.TrailingZeros32(uint32(tt.x)); got != want {
+ t.Errorf("TrailingZeros64(%#x) = %d, want %d", tt.x, got, want)
+ }
+ }
+}
+
+func TestRound(t *testing.T) {
+ for _, tt := range []struct {
+ x, want float64
+ }{
+ {1.4, 1},
+ {1.5, 2},
+ {1.6, 2},
+ {2.4, 2},
+ {2.5, 2},
+ {2.6, 3},
+ } {
+ if got := math.RoundToEven(tt.x); got != tt.want {
+ t.Errorf("RoundToEven(%f) = %f, want %f", tt.x, got, tt.want)
+ }
+ }
+}
+
+func TestFMA(t *testing.T) {
+ for _, tt := range []struct {
+ x, y, z, want float64
+ }{
+ {2, 3, 4, 10},
+ {3, 4, 5, 17},
+ } {
+ if got := math.FMA(tt.x, tt.y, tt.z); got != tt.want {
+ 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/arm/galign.go b/src/cmd/compile/internal/arm/galign.go
index d68500280d00b79376d976a39fab34286faa65a8..23e52bacbf2a3d077d7fc267859c0167283707a2 100644
--- a/src/cmd/compile/internal/arm/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -18,7 +18,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.SoftFloat = buildcfg.GOARM == 5
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 4b083cec46b4a5cff59981de626a75f62fc0e1b7..063fb65b33ab90bf7e56f5f681a1a32ec959487e 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -88,15 +88,18 @@ func (v shift) String() string {
}
// makeshift encodes a register shifted by a constant
-func makeshift(reg int16, typ int64, s int64) shift {
+func makeshift(v *ssa.Value, reg int16, typ int64, s int64) shift {
+ if s < 0 || s >= 32 {
+ v.Fatalf("shift out of range: %d", s)
+ }
return shift(int64(reg&0xf) | typ | (s&31)<<7)
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
-func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
+func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
- p.From.Offset = int64(makeshift(r1, typ, n))
+ p.From.Offset = int64(makeshift(v, r1, typ, n))
p.Reg = r0
if r != 0 {
p.To.Type = obj.TYPE_REG
@@ -335,7 +338,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpARMSRRconst:
- genshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
+ genshift(s, v, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
case ssa.OpARMADDshiftLL,
ssa.OpARMADCshiftLL,
ssa.OpARMSUBshiftLL,
@@ -346,11 +349,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftLL,
ssa.OpARMXORshiftLL,
ssa.OpARMBICshiftLL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMADDSshiftLL,
ssa.OpARMSUBSshiftLL,
ssa.OpARMRSBSshiftLL:
- p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt)
p.Scond = arm.C_SBIT
case ssa.OpARMADDshiftRL,
ssa.OpARMADCshiftRL,
@@ -362,11 +365,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftRL,
ssa.OpARMXORshiftRL,
ssa.OpARMBICshiftRL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMADDSshiftRL,
ssa.OpARMSUBSshiftRL,
ssa.OpARMRSBSshiftRL:
- p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt)
p.Scond = arm.C_SBIT
case ssa.OpARMADDshiftRA,
ssa.OpARMADCshiftRA,
@@ -378,20 +381,20 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARMORshiftRA,
ssa.OpARMXORshiftRA,
ssa.OpARMBICshiftRA:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMADDSshiftRA,
ssa.OpARMSUBSshiftRA,
ssa.OpARMRSBSshiftRA:
- p := genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt)
p.Scond = arm.C_SBIT
case ssa.OpARMXORshiftRR:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt)
case ssa.OpARMMVNshiftLL:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMMVNshiftRL:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMMVNshiftRA:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMMVNshiftLLreg:
genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL)
case ssa.OpARMMVNshiftRLreg:
@@ -513,11 +516,11 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt)
case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt)
case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt)
case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg:
genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL)
case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg:
@@ -583,13 +586,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// this is just shift 0 bits
fallthrough
case ssa.OpARMMOVWloadshiftLL:
- p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt)
p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWloadshiftRL:
- p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt)
p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWloadshiftRA:
- p := genshift(s, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
+ p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt)
p.From.Reg = v.Args[0].Reg()
case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx:
// this is just shift 0 bits
@@ -600,21 +603,21 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg()
- p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
+ p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt))
case ssa.OpARMMOVWstoreshiftRL:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg()
- p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
+ p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt))
case ssa.OpARMMOVWstoreshiftRA:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
p.To.Type = obj.TYPE_SHIFT
p.To.Reg = v.Args[0].Reg()
- p.To.Offset = int64(makeshift(v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
+ p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt))
case ssa.OpARMMOVBreg,
ssa.OpARMMOVBUreg,
ssa.OpARMMOVHreg,
@@ -645,7 +648,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
}
if buildcfg.GOARM >= 6 {
// generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0)
return
}
fallthrough
@@ -696,6 +699,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter:
s.Call(v)
+ case ssa.OpARMCALLtail:
+ s.TailCall(v)
case ssa.OpARMCALLudiv:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -936,17 +941,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
-
case ssa.BlockARMEQ, ssa.BlockARMNE,
ssa.BlockARMLT, ssa.BlockARMGE,
ssa.BlockARMLE, ssa.BlockARMGT,
diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go
index d3db37e16f43bcbbb261d75078df6c8d126b4a72..3ebd860de8f887c4c0b4dbc3934b7ed297995398 100644
--- a/src/cmd/compile/internal/arm64/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -18,9 +18,10 @@ func Init(arch *ssagen.ArchInfo) {
arch.PadFrame = padframe
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
+ arch.LoadRegResult = loadRegResult
+ arch.SpillArgReg = spillArgReg
}
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index 0c997bc4b3e82adaee8e20ea5bafaf29f6a01c7b..96a29224bf71d50b0201457ae965f54a55381eb6 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -10,6 +10,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
+ "cmd/compile/internal/objw"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
@@ -78,15 +79,18 @@ func storeByType(t *types.Type) obj.As {
}
// makeshift encodes a register shifted by a constant, used as an Offset in Prog
-func makeshift(reg int16, typ int64, s int64) int64 {
+func makeshift(v *ssa.Value, reg int16, typ int64, s int64) int64 {
+ if s < 0 || s >= 64 {
+ v.Fatalf("shift out of range: %d", s)
+ }
return int64(reg&31)<<16 | typ | (s&63)<<10
}
// genshift generates a Prog for r = r0 op (r1 shifted by n)
-func genshift(s *ssagen.State, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
+func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog {
p := s.Prog(as)
p.From.Type = obj.TYPE_SHIFT
- p.From.Offset = makeshift(r1, typ, n)
+ p.From.Offset = makeshift(v, r1, typ, n)
p.Reg = r0
if r != 0 {
p.To.Type = obj.TYPE_REG
@@ -161,6 +165,18 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
ssagen.AddrAuto(&p.To, v)
+ case ssa.OpArgIntReg, ssa.OpArgFloatReg:
+ // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
+ // The loop only runs once.
+ for _, a := range v.Block.Func.RegArgs {
+ // Pass the spill/unspill information along to the assembler, offset by size of
+ // the saved LR slot.
+ addr := ssagen.SpillSlotAddr(a, arm64.REGSP, base.Ctxt.FixedFrameSize())
+ s.FuncInfo().AddSpill(
+ obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
+ }
+ v.Block.Func.RegArgs = nil
+ ssagen.CheckArgReg(v)
case ssa.OpARM64ADD,
ssa.OpARM64SUB,
ssa.OpARM64AND,
@@ -297,11 +313,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpARM64MVNshiftLL, ssa.OpARM64NEGshiftLL:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64MVNshiftRL, ssa.OpARM64NEGshiftRL:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64MVNshiftRA, ssa.OpARM64NEGshiftRA:
- genshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+ case ssa.OpARM64MVNshiftRO:
+ genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64ADDshiftLL,
ssa.OpARM64SUBshiftLL,
ssa.OpARM64ANDshiftLL,
@@ -310,7 +328,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftLL,
ssa.OpARM64ORNshiftLL,
ssa.OpARM64BICshiftLL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64ADDshiftRL,
ssa.OpARM64SUBshiftRL,
ssa.OpARM64ANDshiftRL,
@@ -319,7 +337,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftRL,
ssa.OpARM64ORNshiftRL,
ssa.OpARM64BICshiftRL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64ADDshiftRA,
ssa.OpARM64SUBshiftRA,
ssa.OpARM64ANDshiftRA,
@@ -328,7 +346,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpARM64EONshiftRA,
ssa.OpARM64ORNshiftRA,
ssa.OpARM64BICshiftRA:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_AR, v.AuxInt)
+ case ssa.OpARM64ANDshiftRO,
+ ssa.OpARM64ORshiftRO,
+ ssa.OpARM64XORshiftRO,
+ ssa.OpARM64EONshiftRO,
+ ssa.OpARM64ORNshiftRO,
+ ssa.OpARM64BICshiftRO:
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64MOVDconst:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
@@ -371,11 +396,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Offset = v.AuxInt
p.Reg = v.Args[0].Reg()
case ssa.OpARM64CMPshiftLL, ssa.OpARM64CMNshiftLL, ssa.OpARM64TSTshiftLL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LL, v.AuxInt)
case ssa.OpARM64CMPshiftRL, ssa.OpARM64CMNshiftRL, ssa.OpARM64TSTshiftRL:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_LR, v.AuxInt)
case ssa.OpARM64CMPshiftRA, ssa.OpARM64CMNshiftRA, ssa.OpARM64TSTshiftRA:
- genshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_AR, v.AuxInt)
+ case ssa.OpARM64TSTshiftRO:
+ genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm64.SHIFT_ROR, v.AuxInt)
case ssa.OpARM64MOVDaddr:
p := s.Prog(arm64.AMOVD)
p.From.Type = obj.TYPE_ADDR
@@ -1033,6 +1060,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p4.To.SetTarget(p)
case ssa.OpARM64CALLstatic, ssa.OpARM64CALLclosure, ssa.OpARM64CALLinter:
s.Call(v)
+ case ssa.OpARM64CALLtail:
+ s.TailCall(v)
case ssa.OpARM64LoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -1082,6 +1111,12 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = condBits[v.Op]
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+ case ssa.OpARM64PRFM:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ p.To.Type = obj.TYPE_CONST
+ p.To.Offset = v.AuxInt
case ssa.OpARM64LoweredGetClosurePtr:
// Closure pointer is R26 (arm64.REGCTXT).
ssagen.CheckLoweredGetClosurePtr(v)
@@ -1097,12 +1132,42 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+ case ssa.OpARM64DMB:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = v.AuxInt
case ssa.OpARM64FlagConstant:
v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString())
case ssa.OpARM64InvertFlags:
v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString())
- case ssa.OpClobber, ssa.OpClobberReg:
- // TODO: implement for clobberdead experiment. Nop is ok for now.
+ case ssa.OpClobber:
+ // MOVW $0xdeaddead, REGTMP
+ // MOVW REGTMP, (slot)
+ // MOVW REGTMP, 4(slot)
+ p := s.Prog(arm64.AMOVW)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = 0xdeaddead
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = arm64.REGTMP
+ p = s.Prog(arm64.AMOVW)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = arm64.REGTMP
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = arm64.REGSP
+ ssagen.AddAux(&p.To, v)
+ p = s.Prog(arm64.AMOVW)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = arm64.REGTMP
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = arm64.REGSP
+ ssagen.AddAux2(&p.To, v, v.AuxInt+4)
+ case ssa.OpClobberReg:
+ x := uint64(0xdeaddeaddeaddead)
+ p := s.Prog(arm64.AMOVD)
+ p.From.Type = obj.TYPE_CONST
+ p.From.Offset = int64(x)
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg()
default:
v.Fatalf("genValue not implemented: %s", v.LongString())
}
@@ -1196,17 +1261,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
-
case ssa.BlockARM64EQ, ssa.BlockARM64NE,
ssa.BlockARM64LT, ssa.BlockARM64GE,
ssa.BlockARM64LE, ssa.BlockARM64GT,
@@ -1266,3 +1325,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
b.Fatalf("branch not implemented: %s", b.LongString())
}
}
+
+func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
+ p := s.Prog(loadByType(t))
+ p.From.Type = obj.TYPE_MEM
+ p.From.Name = obj.NAME_AUTO
+ p.From.Sym = n.Linksym()
+ p.From.Offset = n.FrameOffset() + off
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = reg
+ return p
+}
+
+func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
+ p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
+ p.To.Name = obj.NAME_PARAM
+ p.To.Sym = n.Linksym()
+ p.Pos = p.Pos.WithNotStmt()
+ return p
+}
diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go
index 4c2516f60e364b5d177707daaa8584c0e1920777..be6d49fac766db1d846ad5a376e4e0d8297ece0e 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/bootstrap_false.go b/src/cmd/compile/internal/base/bootstrap_false.go
new file mode 100644
index 0000000000000000000000000000000000000000..c77fcd730812d824c061d329ca0ed353d82a8b16
--- /dev/null
+++ b/src/cmd/compile/internal/base/bootstrap_false.go
@@ -0,0 +1,12 @@
+// 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.
+
+//go:build !compiler_bootstrap
+// +build !compiler_bootstrap
+
+package base
+
+// CompilerBootstrap reports whether the current compiler binary was
+// built with -tags=compiler_bootstrap.
+const CompilerBootstrap = false
diff --git a/src/cmd/compile/internal/base/bootstrap_true.go b/src/cmd/compile/internal/base/bootstrap_true.go
new file mode 100644
index 0000000000000000000000000000000000000000..1eb58b2f9dc1ce86ca9fb095715e685e667cbb3e
--- /dev/null
+++ b/src/cmd/compile/internal/base/bootstrap_true.go
@@ -0,0 +1,12 @@
+// 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.
+
+//go:build compiler_bootstrap
+// +build compiler_bootstrap
+
+package base
+
+// CompilerBootstrap reports whether the current compiler binary was
+// built with -tags=compiler_bootstrap.
+const CompilerBootstrap = true
diff --git a/src/cmd/compile/internal/base/debug.go b/src/cmd/compile/internal/base/debug.go
index 71712ab1a56fd281f52d3befede9b833caa70fbd..b105e46e353cb4767b610e6dfe6f1293f2c2a526 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,150 +31,23 @@ 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"`
+ SyncFrames int `help:"how many writer stack frames to include at sync points in unified export data"`
TypeAssert int `help:"print information about type assertion inlining"`
TypecheckInl int `help:"eager typechecking of inline function bodies"`
+ Unified int `help:"enable unified IR construction"`
+ 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 42c0c1b94b559c17ae593b9592b7ec5152e3a5f8..d78f93b343fa118670f5703cefddd2f700f1d94e 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\""
@@ -140,10 +141,11 @@ type CmdFlags struct {
// ParseFlags parses the command-line flags into Flag.
func ParseFlags() {
+ Flag.G = 3
Flag.I = addImportDir
Flag.LowerC = 1
- Flag.LowerD = parseDebug
+ Flag.LowerD = objabi.NewDebugFlag(&Debug, DebugSSA)
Flag.LowerP = &Ctxt.Pkgpath
Flag.LowerV = &Ctxt.Debugvlog
@@ -159,7 +161,11 @@ func ParseFlags() {
Flag.LinkShared = &Ctxt.Flag_linkshared
Flag.Shared = &Ctxt.Flag_shared
Flag.WB = true
+
Debug.InlFuncsWithClosures = 1
+ if buildcfg.Experiment.Unified {
+ Debug.Unified = 1
+ }
Debug.Checkptr = -1 // so we can tell whether it is set explicitly
@@ -172,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)
}
@@ -183,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()
@@ -212,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
}
@@ -317,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))
+ }
}
}
}
@@ -344,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.
@@ -352,7 +372,7 @@ func concurrentBackendAllowed() bool {
return false
}
// TODO: fix races and enable the following flags
- if Ctxt.Flag_shared || Ctxt.Flag_dynlink || Flag.Race {
+ if Ctxt.Flag_dynlink || Flag.Race {
return false
}
return true
diff --git a/src/cmd/compile/internal/typecheck/mapfile_mmap.go b/src/cmd/compile/internal/base/mapfile_mmap.go
similarity index 93%
rename from src/cmd/compile/internal/typecheck/mapfile_mmap.go
rename to src/cmd/compile/internal/base/mapfile_mmap.go
index 298b385bcb0f762413c6cf6f7402f0817240d005..c1616db8e9dd684027400eea36481f209d98d2b6 100644
--- a/src/cmd/compile/internal/typecheck/mapfile_mmap.go
+++ b/src/cmd/compile/internal/base/mapfile_mmap.go
@@ -5,7 +5,7 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build darwin dragonfly freebsd linux netbsd openbsd
-package typecheck
+package base
import (
"os"
@@ -19,7 +19,7 @@ import (
// mapFile returns length bytes from the file starting at the
// specified offset as a string.
-func mapFile(f *os.File, offset, length int64) (string, error) {
+func MapFile(f *os.File, offset, length int64) (string, error) {
// POSIX mmap: "The implementation may require that off is a
// multiple of the page size."
x := offset & int64(os.Getpagesize()-1)
diff --git a/src/cmd/compile/internal/typecheck/mapfile_read.go b/src/cmd/compile/internal/base/mapfile_read.go
similarity index 85%
rename from src/cmd/compile/internal/typecheck/mapfile_read.go
rename to src/cmd/compile/internal/base/mapfile_read.go
index 9637ab97abe458dc19e0ead21ed0759532fc7d47..01796a9bab7c9978f0b22211d714efff01782c58 100644
--- a/src/cmd/compile/internal/typecheck/mapfile_read.go
+++ b/src/cmd/compile/internal/base/mapfile_read.go
@@ -5,14 +5,14 @@
//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd
-package typecheck
+package base
import (
"io"
"os"
)
-func mapFile(f *os.File, offset, length int64) (string, error) {
+func MapFile(f *os.File, offset, length int64) (string, error) {
buf := make([]byte, length)
_, err := io.ReadFull(io.NewSectionReader(f, offset, length), buf)
if err != nil {
diff --git a/src/cmd/compile/internal/base/print.go b/src/cmd/compile/internal/base/print.go
index b095fd704daad807b3847bbd2916024356161af2..4afe2eb9eea3154f83456ed4ed282f04d7d1ab4a 100644
--- a/src/cmd/compile/internal/base/print.go
+++ b/src/cmd/compile/internal/base/print.go
@@ -233,6 +233,27 @@ func FatalfAt(pos src.XPos, format string, args ...interface{}) {
ErrorExit()
}
+// Assert reports "assertion failed" with Fatalf, unless b is true.
+func Assert(b bool) {
+ if !b {
+ Fatalf("assertion failed")
+ }
+}
+
+// Assertf reports a fatal error with Fatalf, unless b is true.
+func Assertf(b bool, format string, args ...interface{}) {
+ if !b {
+ Fatalf(format, args...)
+ }
+}
+
+// AssertfAt reports a fatal error with FatalfAt, unless b is true.
+func AssertfAt(b bool, pos src.XPos, format string, args ...interface{}) {
+ if !b {
+ FatalfAt(pos, format, args...)
+ }
+}
+
// hcrash crashes the compiler when -h is set, to find out where a message is generated.
func hcrash() {
if Flag.LowerH != 0 {
diff --git a/src/cmd/compile/internal/bitvec/bv.go b/src/cmd/compile/internal/bitvec/bv.go
index bcac1fe351fac13c7d06850495cc13364f3d0ddd..ad7ed0a1965e9c064e2dddf0f19125dfd4fe7ed4 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 {
+ tagKs = append(tagKs, e.heapHole().shift(x))
+ }
+
+ if ks != nil {
+ for i := 0; i < numEscResults; i++ {
+ if x := esc.Result(i); x >= 0 {
+ tagKs = append(tagKs, ks[i].shift(x))
+ }
+ }
+ }
+
+ return e.teeHole(tagKs...)
+}
diff --git a/src/cmd/compile/internal/escape/desugar.go b/src/cmd/compile/internal/escape/desugar.go
new file mode 100644
index 0000000000000000000000000000000000000000..8b3cc25cf9a4a684810107e1124546d6e9b3b187
--- /dev/null
+++ b/src/cmd/compile/internal/escape/desugar.go
@@ -0,0 +1,37 @@
+// 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 escape
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+)
+
+// TODO(mdempsky): Desugaring doesn't belong during escape analysis,
+// but for now it's the most convenient place for some rewrites.
+
+// fixRecoverCall rewrites an ORECOVER call into ORECOVERFP,
+// adding an explicit frame pointer argument.
+// If call is not an ORECOVER call, it's left unmodified.
+func fixRecoverCall(call *ir.CallExpr) {
+ if call.Op() != ir.ORECOVER {
+ return
+ }
+
+ pos := call.Pos()
+
+ // FP is equal to caller's SP plus FixedFrameSize().
+ var fp ir.Node = ir.NewCallExpr(pos, ir.OGETCALLERSP, nil, nil)
+ if off := base.Ctxt.FixedFrameSize(); off != 0 {
+ fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off))
+ }
+ // TODO(mdempsky): Replace *int32 with unsafe.Pointer, without upsetting checkptr.
+ fp = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewPtr(types.Types[types.TINT32]), fp)
+
+ call.SetOp(ir.ORECOVERFP)
+ call.Args = []ir.Node{typecheck.Expr(fp)}
+}
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index cd56f07b6147a431cf0347e65c5c91df2d98d29d..61e0121a40aec2ea54b6829932eecd3641e00ba6 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -6,15 +6,11 @@ package escape
import (
"fmt"
- "math"
- "strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
- "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/internal/src"
)
// Escape analysis.
@@ -118,90 +114,8 @@ type escape struct {
loopDepth int
}
-// An location represents an abstract location that stores a Go
-// variable.
-type location struct {
- n ir.Node // represented variable or expression, if any
- curfn *ir.Func // enclosing function
- edges []edge // incoming edges
- loopDepth int // loopDepth at declaration
-
- // resultIndex records the tuple index (starting at 1) for
- // PPARAMOUT variables within their function's result type.
- // For non-PPARAMOUT variables it's 0.
- resultIndex int
-
- // derefs and walkgen are used during walkOne to track the
- // minimal dereferences from the walk root.
- derefs int // >= -1
- walkgen uint32
-
- // dst and dstEdgeindex track the next immediate assignment
- // destination location during walkone, along with the index
- // of the edge pointing back to this location.
- dst *location
- dstEdgeIdx int
-
- // queued is used by walkAll to track whether this location is
- // in the walk queue.
- queued bool
-
- // escapes reports whether the represented variable's address
- // escapes; that is, whether the variable must be heap
- // allocated.
- escapes bool
-
- // transient reports whether the represented expression's
- // address does not outlive the statement; that is, whether
- // its storage can be immediately reused.
- transient bool
-
- // paramEsc records the represented parameter's leak set.
- paramEsc leaks
-
- captured bool // has a closure captured this variable?
- reassigned bool // has this variable been reassigned?
- addrtaken bool // has this variable's address been taken?
-}
-
-// An edge represents an assignment edge between two Go variables.
-type edge struct {
- src *location
- derefs int // >= -1
- notes *note
-}
-
-// Fmt is called from node printing to print information about escape analysis results.
-func Fmt(n ir.Node) string {
- text := ""
- switch n.Esc() {
- case ir.EscUnknown:
- break
-
- case ir.EscHeap:
- text = "esc(h)"
-
- case ir.EscNone:
- text = "esc(no)"
-
- case ir.EscNever:
- text = "esc(N)"
-
- default:
- text = fmt.Sprintf("esc(%d)", n.Esc())
- }
-
- if n.Op() == ir.ONAME {
- n := n.(*ir.Name)
- if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
- if text != "" {
- text += " "
- }
- text += fmt.Sprintf("ld(%d)", loc.loopDepth)
- }
- }
-
- return text
+func Funcs(all []ir.Node) {
+ ir.VisitFuncsBottomUp(all, Batch)
}
// Batch performs escape analysis on a minimal batch of
@@ -269,8 +183,14 @@ func (b *batch) initFunc(fn *ir.Func) {
// Allocate locations for local variables.
for _, n := range fn.Dcl {
- if n.Op() == ir.ONAME {
- e.newLoc(n, false)
+ e.newLoc(n, false)
+ }
+
+ // Also for hidden parameters (e.g., the ".this" parameter to a
+ // method value wrapper).
+ if fn.OClosure == nil {
+ for _, n := range fn.ClosureVars {
+ e.newLoc(n.Canonical(), false)
}
}
@@ -342,1771 +262,173 @@ func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
}
}
-// Below we implement the methods for walking the AST and recording
-// data flow edges. Note that because a sub-expression might have
-// side-effects, it's important to always visit the entire AST.
-//
-// For example, write either:
-//
-// if x {
-// e.discard(n.Left)
-// } else {
-// e.value(k, n.Left)
-// }
-//
-// or
-//
-// if x {
-// k = e.discardHole()
-// }
-// e.value(k, n.Left)
-//
-// Do NOT write:
-//
-// // BAD: possibly loses side-effects within n.Left
-// if !x {
-// e.value(k, n.Left)
-// }
-
-// stmt evaluates a single Go statement.
-func (e *escape) stmt(n ir.Node) {
- if n == nil {
- return
- }
-
- lno := ir.SetPos(n)
- defer func() {
- base.Pos = lno
- }()
-
- if base.Flag.LowerM > 2 {
- fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n)
- }
-
- e.stmts(n.Init())
-
- switch n.Op() {
- default:
- base.Fatalf("unexpected stmt: %v", n)
-
- case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK:
- // nop
-
- case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
- // TODO(mdempsky): Handle dead code?
-
- case ir.OBLOCK:
- n := n.(*ir.BlockStmt)
- e.stmts(n.List)
-
- case ir.ODCL:
- // Record loop depth at declaration.
- n := n.(*ir.Decl)
- if !ir.IsBlank(n.X) {
- e.dcl(n.X)
- }
-
- case ir.OLABEL:
- n := n.(*ir.LabelStmt)
- switch e.labels[n.Label] {
- case nonlooping:
- if base.Flag.LowerM > 2 {
- fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
- }
- case looping:
- if base.Flag.LowerM > 2 {
- fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n)
- }
- e.loopDepth++
- default:
- base.Fatalf("label missing tag")
- }
- delete(e.labels, n.Label)
-
- case ir.OIF:
- n := n.(*ir.IfStmt)
- e.discard(n.Cond)
- e.block(n.Body)
- e.block(n.Else)
-
- case ir.OFOR, ir.OFORUNTIL:
- n := n.(*ir.ForStmt)
- e.loopDepth++
- e.discard(n.Cond)
- e.stmt(n.Post)
- e.block(n.Body)
- e.loopDepth--
-
- case ir.ORANGE:
- // for Key, Value = range X { Body }
- n := n.(*ir.RangeStmt)
-
- // X is evaluated outside the loop.
- tmp := e.newLoc(nil, false)
- e.expr(tmp.asHole(), n.X)
-
- e.loopDepth++
- ks := e.addrs([]ir.Node{n.Key, n.Value})
- if n.X.Type().IsArray() {
- e.flow(ks[1].note(n, "range"), tmp)
- } else {
- e.flow(ks[1].deref(n, "range-deref"), tmp)
- }
- e.reassigned(ks, n)
-
- e.block(n.Body)
- e.loopDepth--
-
- case ir.OSWITCH:
- n := n.(*ir.SwitchStmt)
+func (b *batch) finish(fns []*ir.Func) {
+ // Record parameter tags for package export data.
+ for _, fn := range fns {
+ fn.SetEsc(escFuncTagged)
- if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
- var ks []hole
- if guard.Tag != nil {
- for _, cas := range n.Cases {
- cv := cas.Var
- k := e.dcl(cv) // type switch variables have no ODCL.
- if cv.Type().HasPointers() {
- ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
- }
- }
+ narg := 0
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(fn.Type()).Fields().Slice() {
+ narg++
+ f.Note = b.paramTag(fn, narg, f)
}
- e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
- } else {
- e.discard(n.Tag)
- }
-
- for _, cas := range n.Cases {
- e.discards(cas.List)
- e.block(cas.Body)
- }
-
- case ir.OSELECT:
- n := n.(*ir.SelectStmt)
- for _, cas := range n.Cases {
- e.stmt(cas.Comm)
- e.block(cas.Body)
- }
- case ir.ORECV:
- // TODO(mdempsky): Consider e.discard(n.Left).
- n := n.(*ir.UnaryExpr)
- e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
- case ir.OSEND:
- n := n.(*ir.SendStmt)
- e.discard(n.Chan)
- e.assignHeap(n.Value, "send", n)
-
- case ir.OAS:
- n := n.(*ir.AssignStmt)
- e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
- case ir.OASOP:
- n := n.(*ir.AssignOpStmt)
- // TODO(mdempsky): Worry about OLSH/ORSH?
- e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
- case ir.OAS2:
- n := n.(*ir.AssignListStmt)
- e.assignList(n.Lhs, n.Rhs, "assign-pair", n)
-
- case ir.OAS2DOTTYPE: // v, ok = x.(type)
- n := n.(*ir.AssignListStmt)
- e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
- case ir.OAS2MAPR: // v, ok = m[k]
- n := n.(*ir.AssignListStmt)
- e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
- case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch
- n := n.(*ir.AssignListStmt)
- e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)
-
- case ir.OAS2FUNC:
- n := n.(*ir.AssignListStmt)
- e.stmts(n.Rhs[0].Init())
- ks := e.addrs(n.Lhs)
- e.call(ks, n.Rhs[0], nil)
- e.reassigned(ks, n)
- case ir.ORETURN:
- n := n.(*ir.ReturnStmt)
- results := e.curfn.Type().Results().FieldSlice()
- dsts := make([]ir.Node, len(results))
- for i, res := range results {
- dsts[i] = res.Nname.(*ir.Name)
}
- e.assignList(dsts, n.Results, "return", n)
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
- e.call(nil, n, nil)
- case ir.OGO, ir.ODEFER:
- n := n.(*ir.GoDeferStmt)
- e.stmts(n.Call.Init())
- e.call(nil, n.Call, n)
-
- case ir.OTAILCALL:
- // TODO(mdempsky): Treat like a normal call? esc.go used to just ignore it.
- }
-}
-
-func (e *escape) stmts(l ir.Nodes) {
- for _, n := range l {
- e.stmt(n)
- }
-}
-
-// block is like stmts, but preserves loopDepth.
-func (e *escape) block(l ir.Nodes) {
- old := e.loopDepth
- e.stmts(l)
- e.loopDepth = old
-}
-
-// expr models evaluating an expression n and flowing the result into
-// hole k.
-func (e *escape) expr(k hole, n ir.Node) {
- if n == nil {
- return
- }
- e.stmts(n.Init())
- e.exprSkipInit(k, n)
-}
-
-func (e *escape) exprSkipInit(k hole, n ir.Node) {
- if n == nil {
- return
- }
-
- lno := ir.SetPos(n)
- defer func() {
- base.Pos = lno
- }()
-
- uintptrEscapesHack := k.uintptrEscapesHack
- k.uintptrEscapesHack = false
-
- if uintptrEscapesHack && n.Op() == ir.OCONVNOP && n.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
- // nop
- } else if k.derefs >= 0 && !n.Type().HasPointers() {
- k.dst = &e.blankLoc
}
- switch n.Op() {
- default:
- base.Fatalf("unexpected expr: %s %v", n.Op().String(), n)
-
- case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET:
- // nop
-
- case ir.ONAME:
- n := n.(*ir.Name)
- if n.Class == ir.PFUNC || n.Class == ir.PEXTERN {
- return
- }
- if n.IsClosureVar() && n.Defn == nil {
- return // ".this" from method value wrapper
- }
- e.flow(k, e.oldLoc(n))
-
- case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
- n := n.(*ir.UnaryExpr)
- e.discard(n.X)
- case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
- n := n.(*ir.BinaryExpr)
- e.discard(n.X)
- e.discard(n.Y)
- case ir.OANDAND, ir.OOROR:
- n := n.(*ir.LogicalExpr)
- e.discard(n.X)
- e.discard(n.Y)
- case ir.OADDR:
- n := n.(*ir.AddrExpr)
- e.expr(k.addr(n, "address-of"), n.X) // "address-of"
- case ir.ODEREF:
- n := n.(*ir.StarExpr)
- e.expr(k.deref(n, "indirection"), n.X) // "indirection"
- case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
- n := n.(*ir.SelectorExpr)
- e.expr(k.note(n, "dot"), n.X)
- case ir.ODOTPTR:
- n := n.(*ir.SelectorExpr)
- e.expr(k.deref(n, "dot of pointer"), n.X) // "dot of pointer"
- case ir.ODOTTYPE, ir.ODOTTYPE2:
- n := n.(*ir.TypeAssertExpr)
- e.expr(k.dotType(n.Type(), n, "dot"), n.X)
- case ir.OINDEX:
- n := n.(*ir.IndexExpr)
- if n.X.Type().IsArray() {
- e.expr(k.note(n, "fixed-array-index-of"), n.X)
- } else {
- // TODO(mdempsky): Fix why reason text.
- e.expr(k.deref(n, "dot of pointer"), n.X)
- }
- e.discard(n.Index)
- case ir.OINDEXMAP:
- n := n.(*ir.IndexExpr)
- e.discard(n.X)
- e.discard(n.Index)
- case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR:
- n := n.(*ir.SliceExpr)
- e.expr(k.note(n, "slice"), n.X)
- e.discard(n.Low)
- e.discard(n.High)
- e.discard(n.Max)
-
- case ir.OCONV, ir.OCONVNOP:
- n := n.(*ir.ConvExpr)
- if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
- // When -d=checkptr=2 is enabled, treat
- // conversions to unsafe.Pointer as an
- // escaping operation. This allows better
- // runtime instrumentation, since we can more
- // easily detect object boundaries on the heap
- // than the stack.
- e.assignHeap(n.X, "conversion to unsafe.Pointer", n)
- } else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() {
- e.unsafeValue(k, n.X)
- } else {
- e.expr(k, n.X)
- }
- case ir.OCONVIFACE:
- n := n.(*ir.ConvExpr)
- if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) {
- k = e.spill(k, n)
- }
- e.expr(k.note(n, "interface-converted"), n.X)
- case ir.OSLICE2ARRPTR:
- // the slice pointer flows directly to the result
- n := n.(*ir.ConvExpr)
- e.expr(k, n.X)
- case ir.ORECV:
- n := n.(*ir.UnaryExpr)
- e.discard(n.X)
-
- case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
- e.call([]hole{k}, n, nil)
-
- case ir.ONEW:
- n := n.(*ir.UnaryExpr)
- e.spill(k, n)
-
- case ir.OMAKESLICE:
- n := n.(*ir.MakeExpr)
- e.spill(k, n)
- e.discard(n.Len)
- e.discard(n.Cap)
- case ir.OMAKECHAN:
- n := n.(*ir.MakeExpr)
- e.discard(n.Len)
- case ir.OMAKEMAP:
- n := n.(*ir.MakeExpr)
- e.spill(k, n)
- e.discard(n.Len)
-
- case ir.ORECOVER:
- // nop
-
- case ir.OCALLPART:
- // Flow the receiver argument to both the closure and
- // to the receiver parameter.
-
- n := n.(*ir.SelectorExpr)
- closureK := e.spill(k, n)
-
- m := n.Selection
-
- // We don't know how the method value will be called
- // later, so conservatively assume the result
- // parameters all flow to the heap.
- //
- // TODO(mdempsky): Change ks into a callback, so that
- // we don't have to create this slice?
- var ks []hole
- for i := m.Type.NumResults(); i > 0; i-- {
- ks = append(ks, e.heapHole())
- }
- name, _ := m.Nname.(*ir.Name)
- paramK := e.tagHole(ks, name, m.Type.Recv())
-
- e.expr(e.teeHole(paramK, closureK), n.X)
-
- case ir.OPTRLIT:
- n := n.(*ir.AddrExpr)
- e.expr(e.spill(k, n), n.X)
-
- case ir.OARRAYLIT:
- n := n.(*ir.CompLitExpr)
- for _, elt := range n.List {
- if elt.Op() == ir.OKEY {
- elt = elt.(*ir.KeyExpr).Value
- }
- e.expr(k.note(n, "array literal element"), elt)
- }
-
- case ir.OSLICELIT:
- n := n.(*ir.CompLitExpr)
- k = e.spill(k, n)
- k.uintptrEscapesHack = uintptrEscapesHack // for ...uintptr parameters
-
- for _, elt := range n.List {
- if elt.Op() == ir.OKEY {
- elt = elt.(*ir.KeyExpr).Value
- }
- e.expr(k.note(n, "slice-literal-element"), elt)
- }
-
- case ir.OSTRUCTLIT:
- n := n.(*ir.CompLitExpr)
- for _, elt := range n.List {
- e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value)
+ for _, loc := range b.allLocs {
+ n := loc.n
+ if n == nil {
+ continue
}
-
- case ir.OMAPLIT:
- n := n.(*ir.CompLitExpr)
- e.spill(k, n)
-
- // Map keys and values are always stored in the heap.
- for _, elt := range n.List {
- elt := elt.(*ir.KeyExpr)
- e.assignHeap(elt.Key, "map literal key", n)
- e.assignHeap(elt.Value, "map literal value", n)
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ n.Opt = nil
}
- case ir.OCLOSURE:
- n := n.(*ir.ClosureExpr)
- k = e.spill(k, n)
- e.closures = append(e.closures, closure{k, n})
+ // Update n.Esc based on escape analysis results.
- if fn := n.Func; fn.IsHiddenClosure() {
- for _, cv := range fn.ClosureVars {
- if loc := e.oldLoc(cv); !loc.captured {
- loc.captured = true
+ // Omit escape diagnostics for go/defer wrappers, at least for now.
+ // Historically, we haven't printed them, and test cases don't expect them.
+ // TODO(mdempsky): Update tests to expect this.
+ goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
- // Ignore reassignments to the variable in straightline code
- // preceding the first capture by a closure.
- if loc.loopDepth == e.loopDepth {
- loc.reassigned = false
- }
+ if loc.escapes {
+ if n.Op() == ir.ONAME {
+ if base.Flag.CompilingRuntime {
+ base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n)
+ }
+ if base.Flag.LowerM != 0 {
+ base.WarnfAt(n.Pos(), "moved to heap: %v", n)
+ }
+ } else {
+ if base.Flag.LowerM != 0 && !goDeferWrapper {
+ base.WarnfAt(n.Pos(), "%v escapes to heap", n)
+ }
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
}
}
-
- for _, n := range fn.Dcl {
- // Add locations for local variables of the
- // closure, if needed, in case we're not including
- // the closure func in the batch for escape
- // analysis (happens for escape analysis called
- // from reflectdata.methodWrapper)
- if n.Op() == ir.ONAME && n.Opt == nil {
- e.with(fn).newLoc(n, false)
+ n.SetEsc(ir.EscHeap)
+ } else {
+ if base.Flag.LowerM != 0 && n.Op() != ir.ONAME && !goDeferWrapper {
+ base.WarnfAt(n.Pos(), "%v does not escape", n)
+ }
+ n.SetEsc(ir.EscNone)
+ if loc.transient {
+ switch n.Op() {
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ n.SetTransient(true)
+ case ir.OMETHVALUE:
+ n := n.(*ir.SelectorExpr)
+ n.SetTransient(true)
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ n.SetTransient(true)
}
}
- e.walkFunc(fn)
}
-
- case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR:
- n := n.(*ir.ConvExpr)
- e.spill(k, n)
- e.discard(n.X)
-
- case ir.OADDSTR:
- n := n.(*ir.AddStringExpr)
- e.spill(k, n)
-
- // Arguments of OADDSTR never escape;
- // runtime.concatstrings makes sure of that.
- e.discards(n.List)
}
}
-// unsafeValue evaluates a uintptr-typed arithmetic expression looking
-// for conversions from an unsafe.Pointer.
-func (e *escape) unsafeValue(k hole, n ir.Node) {
- if n.Type().Kind() != types.TUINTPTR {
- base.Fatalf("unexpected type %v for %v", n.Type(), n)
- }
- if k.addrtaken {
- base.Fatalf("unexpected addrtaken")
- }
-
- e.stmts(n.Init())
-
- switch n.Op() {
- case ir.OCONV, ir.OCONVNOP:
- n := n.(*ir.ConvExpr)
- if n.X.Type().IsUnsafePtr() {
- e.expr(k, n.X)
- } else {
- e.discard(n.X)
- }
- case ir.ODOTPTR:
- n := n.(*ir.SelectorExpr)
- if ir.IsReflectHeaderDataField(n) {
- e.expr(k.deref(n, "reflect.Header.Data"), n.X)
- } else {
- e.discard(n.X)
+// inMutualBatch reports whether function fn is in the batch of
+// mutually recursive functions being analyzed. When this is true,
+// fn has not yet been analyzed, so its parameters and results
+// should be incorporated directly into the flow graph instead of
+// relying on its escape analysis tagging.
+func (e *escape) inMutualBatch(fn *ir.Name) bool {
+ if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
+ if fn.Defn.Esc() == escFuncUnknown {
+ base.Fatalf("graph inconsistency: %v", fn)
}
- case ir.OPLUS, ir.ONEG, ir.OBITNOT:
- n := n.(*ir.UnaryExpr)
- e.unsafeValue(k, n.X)
- case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT:
- n := n.(*ir.BinaryExpr)
- e.unsafeValue(k, n.X)
- e.unsafeValue(k, n.Y)
- case ir.OLSH, ir.ORSH:
- n := n.(*ir.BinaryExpr)
- e.unsafeValue(k, n.X)
- // RHS need not be uintptr-typed (#32959) and can't meaningfully
- // flow pointers anyway.
- e.discard(n.Y)
- default:
- e.exprSkipInit(e.discardHole(), n)
+ return true
}
+ return false
}
-// discard evaluates an expression n for side-effects, but discards
-// its value.
-func (e *escape) discard(n ir.Node) {
- e.expr(e.discardHole(), n)
-}
-
-func (e *escape) discards(l ir.Nodes) {
- for _, n := range l {
- e.discard(n)
- }
-}
+const (
+ escFuncUnknown = 0 + iota
+ escFuncPlanned
+ escFuncStarted
+ escFuncTagged
+)
-// addr evaluates an addressable expression n and returns a hole
-// that represents storing into the represented location.
-func (e *escape) addr(n ir.Node) hole {
- if n == nil || ir.IsBlank(n) {
- // Can happen in select case, range, maybe others.
- return e.discardHole()
- }
+// Mark labels that have no backjumps to them as not increasing e.loopdepth.
+type labelState int
- k := e.heapHole()
+const (
+ looping labelState = 1 + iota
+ nonlooping
+)
- switch n.Op() {
- default:
- base.Fatalf("unexpected addr: %v", n)
- case ir.ONAME:
- n := n.(*ir.Name)
- if n.Class == ir.PEXTERN {
- break
- }
- k = e.oldLoc(n).asHole()
- case ir.OLINKSYMOFFSET:
- break
- case ir.ODOT:
- n := n.(*ir.SelectorExpr)
- k = e.addr(n.X)
- case ir.OINDEX:
- n := n.(*ir.IndexExpr)
- e.discard(n.Index)
- if n.X.Type().IsArray() {
- k = e.addr(n.X)
- } else {
- e.discard(n.X)
+func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
+ name := func() string {
+ if f.Sym != nil {
+ return f.Sym.Name
}
- case ir.ODEREF, ir.ODOTPTR:
- e.discard(n)
- case ir.OINDEXMAP:
- n := n.(*ir.IndexExpr)
- e.discard(n.X)
- e.assignHeap(n.Index, "key of map put", n)
+ return fmt.Sprintf("arg#%d", narg)
}
- return k
-}
+ // Only report diagnostics for user code;
+ // not for wrappers generated around them.
+ // TODO(mdempsky): Generalize this.
+ diagnose := base.Flag.LowerM != 0 && !(fn.Wrapper() || fn.Dupok())
-func (e *escape) addrs(l ir.Nodes) []hole {
- var ks []hole
- for _, n := range l {
- ks = append(ks, e.addr(n))
- }
- return ks
-}
+ if len(fn.Body) == 0 {
+ // Assume that uintptr arguments must be held live across the call.
+ // This is most important for syscall.Syscall.
+ // See golang.org/issue/13372.
+ // This really doesn't have much to do with escape analysis per se,
+ // but we are reusing the ability to annotate an individual function
+ // argument and pass those annotations along to importing code.
+ fn.Pragma |= ir.UintptrKeepAlive
-// reassigned marks the locations associated with the given holes as
-// reassigned, unless the location represents a variable declared and
-// assigned exactly once by where.
-func (e *escape) reassigned(ks []hole, where ir.Node) {
- if as, ok := where.(*ir.AssignStmt); ok && as.Op() == ir.OAS && as.Y == nil {
- if dst, ok := as.X.(*ir.Name); ok && dst.Op() == ir.ONAME && dst.Defn == nil {
- // Zero-value assignment for variable declared without an
- // explicit initial value. Assume this is its initialization
- // statement.
- return
+ if f.Type.IsUintptr() {
+ if diagnose {
+ base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
+ }
+ return ""
}
- }
- for _, k := range ks {
- loc := k.dst
- // Variables declared by range statements are assigned on every iteration.
- if n, ok := loc.n.(*ir.Name); ok && n.Defn == where && where.Op() != ir.ORANGE {
- continue
+ if !f.Type.HasPointers() { // don't bother tagging for scalars
+ return ""
}
- loc.reassigned = true
- }
-}
-// assignList evaluates the assignment dsts... = srcs....
-func (e *escape) assignList(dsts, srcs []ir.Node, why string, where ir.Node) {
- ks := e.addrs(dsts)
- for i, k := range ks {
- var src ir.Node
- if i < len(srcs) {
- src = srcs[i]
- }
+ var esc leaks
- if dst := dsts[i]; dst != nil {
- // Detect implicit conversion of uintptr to unsafe.Pointer when
- // storing into reflect.{Slice,String}Header.
- if dst.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(dst) {
- e.unsafeValue(e.heapHole().note(where, why), src)
- continue
+ // External functions are assumed unsafe, unless
+ // //go:noescape is given before the declaration.
+ if fn.Pragma&ir.Noescape != 0 {
+ if diagnose && f.Sym != nil {
+ base.WarnfAt(f.Pos, "%v does not escape", name())
}
-
- // Filter out some no-op assignments for escape analysis.
- if src != nil && isSelfAssign(dst, src) {
- if base.Flag.LowerM != 0 {
- base.WarnfAt(where.Pos(), "%v ignoring self-assignment in %v", e.curfn, where)
- }
- k = e.discardHole()
+ } else {
+ if diagnose && f.Sym != nil {
+ base.WarnfAt(f.Pos, "leaking param: %v", name())
}
+ esc.AddHeap(0)
}
- e.expr(k.note(where, why), src)
- }
-
- e.reassigned(ks, where)
-}
-
-func (e *escape) assignHeap(src ir.Node, why string, where ir.Node) {
- e.expr(e.heapHole().note(where, why), src)
-}
-
-// call evaluates a call expressions, including builtin calls. ks
-// should contain the holes representing where the function callee's
-// results flows; where is the OGO/ODEFER context of the call, if any.
-func (e *escape) call(ks []hole, call, where ir.Node) {
- topLevelDefer := where != nil && where.Op() == ir.ODEFER && e.loopDepth == 1
- if topLevelDefer {
- // force stack allocation of defer record, unless
- // open-coded defers are used (see ssa.go)
- where.SetEsc(ir.EscNever)
- }
-
- argument := func(k hole, arg ir.Node) {
- if topLevelDefer {
- // Top level defers arguments don't escape to
- // heap, but they do need to last until end of
- // function.
- k = e.later(k)
- } else if where != nil {
- k = e.heapHole()
- }
-
- e.expr(k.note(call, "call parameter"), arg)
+ return esc.Encode()
}
- switch call.Op() {
- default:
- ir.Dump("esc", call)
- base.Fatalf("unexpected call op: %v", call.Op())
-
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
- call := call.(*ir.CallExpr)
- typecheck.FixVariadicCall(call)
+ if fn.Pragma&ir.UintptrEscapes != 0 {
+ fn.Pragma |= ir.UintptrKeepAlive
- // Pick out the function callee, if statically known.
- var fn *ir.Name
- switch call.Op() {
- case ir.OCALLFUNC:
- switch v := ir.StaticValue(call.X); {
- case v.Op() == ir.ONAME && v.(*ir.Name).Class == ir.PFUNC:
- fn = v.(*ir.Name)
- case v.Op() == ir.OCLOSURE:
- fn = v.(*ir.ClosureExpr).Func.Nname
+ if f.Type.IsUintptr() {
+ if diagnose {
+ base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
}
- case ir.OCALLMETH:
- fn = ir.MethodExprName(call.X)
- }
-
- fntype := call.X.Type()
- if fn != nil {
- fntype = fn.Type()
- }
-
- if ks != nil && fn != nil && e.inMutualBatch(fn) {
- for i, result := range fn.Type().Results().FieldSlice() {
- e.expr(ks[i], ir.AsNode(result.Nname))
- }
- }
-
- if r := fntype.Recv(); r != nil {
- argument(e.tagHole(ks, fn, r), call.X.(*ir.SelectorExpr).X)
- } else {
- // Evaluate callee function expression.
- argument(e.discardHole(), call.X)
- }
-
- args := call.Args
- for i, param := range fntype.Params().FieldSlice() {
- argument(e.tagHole(ks, fn, param), args[i])
- }
-
- case ir.OAPPEND:
- call := call.(*ir.CallExpr)
- args := call.Args
-
- // Appendee slice may flow directly to the result, if
- // it has enough capacity. Alternatively, a new heap
- // slice might be allocated, and all slice elements
- // might flow to heap.
- appendeeK := ks[0]
- if args[0].Type().Elem().HasPointers() {
- appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
- }
- argument(appendeeK, args[0])
-
- if call.IsDDD {
- appendedK := e.discardHole()
- if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
- appendedK = e.heapHole().deref(call, "appended slice...")
- }
- argument(appendedK, args[1])
- } else {
- for _, arg := range args[1:] {
- argument(e.heapHole(), arg)
- }
- }
-
- case ir.OCOPY:
- call := call.(*ir.BinaryExpr)
- argument(e.discardHole(), call.X)
-
- copiedK := e.discardHole()
- if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
- copiedK = e.heapHole().deref(call, "copied slice")
- }
- argument(copiedK, call.Y)
-
- case ir.OPANIC:
- call := call.(*ir.UnaryExpr)
- argument(e.heapHole(), call.X)
-
- case ir.OCOMPLEX:
- call := call.(*ir.BinaryExpr)
- argument(e.discardHole(), call.X)
- argument(e.discardHole(), call.Y)
- case ir.ODELETE, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
- call := call.(*ir.CallExpr)
- for _, arg := range call.Args {
- argument(e.discardHole(), arg)
- }
- case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
- call := call.(*ir.UnaryExpr)
- argument(e.discardHole(), call.X)
-
- case ir.OUNSAFEADD, ir.OUNSAFESLICE:
- call := call.(*ir.BinaryExpr)
- argument(ks[0], call.X)
- argument(e.discardHole(), call.Y)
- }
-}
-
-// tagHole returns a hole for evaluating an argument passed to param.
-// ks should contain the holes representing where the function
-// callee's results flows. fn is the statically-known callee function,
-// if any.
-func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
- // If this is a dynamic call, we can't rely on param.Note.
- if fn == nil {
- return e.heapHole()
- }
-
- if e.inMutualBatch(fn) {
- return e.addr(ir.AsNode(param.Nname))
- }
-
- // Call to previously tagged function.
-
- if param.Note == UintptrEscapesNote {
- k := e.heapHole()
- k.uintptrEscapesHack = true
- return k
- }
-
- var tagKs []hole
-
- esc := parseLeaks(param.Note)
- if x := esc.Heap(); x >= 0 {
- tagKs = append(tagKs, e.heapHole().shift(x))
- }
-
- if ks != nil {
- for i := 0; i < numEscResults; i++ {
- if x := esc.Result(i); x >= 0 {
- tagKs = append(tagKs, ks[i].shift(x))
- }
- }
- }
-
- return e.teeHole(tagKs...)
-}
-
-// inMutualBatch reports whether function fn is in the batch of
-// mutually recursive functions being analyzed. When this is true,
-// fn has not yet been analyzed, so its parameters and results
-// should be incorporated directly into the flow graph instead of
-// relying on its escape analysis tagging.
-func (e *escape) inMutualBatch(fn *ir.Name) bool {
- if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
- if fn.Defn.Esc() == escFuncUnknown {
- base.Fatalf("graph inconsistency: %v", fn)
- }
- return true
- }
- return false
-}
-
-// An hole represents a context for evaluation a Go
-// expression. E.g., when evaluating p in "x = **p", we'd have a hole
-// with dst==x and derefs==2.
-type hole struct {
- dst *location
- derefs int // >= -1
- notes *note
-
- // addrtaken indicates whether this context is taking the address of
- // the expression, independent of whether the address will actually
- // be stored into a variable.
- addrtaken bool
-
- // uintptrEscapesHack indicates this context is evaluating an
- // argument for a //go:uintptrescapes function.
- uintptrEscapesHack bool
-}
-
-type note struct {
- next *note
- where ir.Node
- why string
-}
-
-func (k hole) note(where ir.Node, why string) hole {
- if where == nil || why == "" {
- base.Fatalf("note: missing where/why")
- }
- if base.Flag.LowerM >= 2 || logopt.Enabled() {
- k.notes = ¬e{
- next: k.notes,
- where: where,
- why: why,
- }
- }
- return k
-}
-
-func (k hole) shift(delta int) hole {
- k.derefs += delta
- if k.derefs < -1 {
- base.Fatalf("derefs underflow: %v", k.derefs)
- }
- k.addrtaken = delta < 0
- return k
-}
-
-func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
-func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
-
-func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
- if !t.IsInterface() && !types.IsDirectIface(t) {
- k = k.shift(1)
- }
- return k.note(where, why)
-}
-
-// teeHole returns a new hole that flows into each hole of ks,
-// similar to the Unix tee(1) command.
-func (e *escape) teeHole(ks ...hole) hole {
- if len(ks) == 0 {
- return e.discardHole()
- }
- if len(ks) == 1 {
- return ks[0]
- }
- // TODO(mdempsky): Optimize if there's only one non-discard hole?
-
- // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a
- // new temporary location ltmp, wire it into place, and return
- // a hole for "ltmp = _".
- loc := e.newLoc(nil, true)
- for _, k := range ks {
- // N.B., "p = &q" and "p = &tmp; tmp = q" are not
- // semantically equivalent. To combine holes like "l1
- // = _" and "l2 = &_", we'd need to wire them as "l1 =
- // *ltmp" and "l2 = ltmp" and return "ltmp = &_"
- // instead.
- if k.derefs < 0 {
- base.Fatalf("teeHole: negative derefs")
- }
-
- e.flow(k, loc)
- }
- return loc.asHole()
-}
-
-func (e *escape) dcl(n *ir.Name) hole {
- if n.Curfn != e.curfn || n.IsClosureVar() {
- base.Fatalf("bad declaration of %v", n)
- }
- loc := e.oldLoc(n)
- loc.loopDepth = e.loopDepth
- return loc.asHole()
-}
-
-// spill allocates a new location associated with expression n, flows
-// its address to k, and returns a hole that flows values to it. It's
-// intended for use with most expressions that allocate storage.
-func (e *escape) spill(k hole, n ir.Node) hole {
- loc := e.newLoc(n, true)
- e.flow(k.addr(n, "spill"), loc)
- return loc.asHole()
-}
-
-// later returns a new hole that flows into k, but some time later.
-// Its main effect is to prevent immediate reuse of temporary
-// variables introduced during Order.
-func (e *escape) later(k hole) hole {
- loc := e.newLoc(nil, false)
- e.flow(k, loc)
- return loc.asHole()
-}
-
-func (e *escape) newLoc(n ir.Node, transient bool) *location {
- if e.curfn == nil {
- base.Fatalf("e.curfn isn't set")
- }
- if n != nil && n.Type() != nil && n.Type().NotInHeap() {
- base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
- }
-
- if n != nil && n.Op() == ir.ONAME {
- n = n.(*ir.Name).Canonical()
- }
- loc := &location{
- n: n,
- curfn: e.curfn,
- loopDepth: e.loopDepth,
- transient: transient,
- }
- e.allLocs = append(e.allLocs, loc)
- if n != nil {
- if n.Op() == ir.ONAME {
- n := n.(*ir.Name)
- if n.Curfn != e.curfn {
- base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
- }
-
- if n.Opt != nil {
- base.Fatalf("%v already has a location", n)
- }
- n.Opt = loc
- }
- }
- return loc
-}
-
-func (b *batch) oldLoc(n *ir.Name) *location {
- if n.Canonical().Opt == nil {
- base.Fatalf("%v has no location", n)
- }
- return n.Canonical().Opt.(*location)
-}
-
-func (l *location) asHole() hole {
- return hole{dst: l}
-}
-
-func (b *batch) flow(k hole, src *location) {
- if k.addrtaken {
- src.addrtaken = true
- }
-
- dst := k.dst
- if dst == &b.blankLoc {
- return
- }
- if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
- return
- }
- if dst.escapes && k.derefs < 0 { // dst = &src
- if base.Flag.LowerM >= 2 || logopt.Enabled() {
- pos := base.FmtPos(src.n.Pos())
- if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
- }
- explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
- }
-
- }
- src.escapes = true
- return
- }
-
- // TODO(mdempsky): Deduplicate edges?
- dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
-}
-
-func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
-func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
-
-// walkAll computes the minimal dereferences between all pairs of
-// locations.
-func (b *batch) walkAll() {
- // We use a work queue to keep track of locations that we need
- // to visit, and repeatedly walk until we reach a fixed point.
- //
- // We walk once from each location (including the heap), and
- // then re-enqueue each location on its transition from
- // transient->!transient and !escapes->escapes, which can each
- // happen at most once. So we take Θ(len(e.allLocs)) walks.
-
- // LIFO queue, has enough room for e.allLocs and e.heapLoc.
- todo := make([]*location, 0, len(b.allLocs)+1)
- enqueue := func(loc *location) {
- if !loc.queued {
- todo = append(todo, loc)
- loc.queued = true
- }
- }
-
- for _, loc := range b.allLocs {
- enqueue(loc)
- }
- enqueue(&b.heapLoc)
-
- var walkgen uint32
- for len(todo) > 0 {
- root := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
- root.queued = false
-
- walkgen++
- b.walkOne(root, walkgen, enqueue)
- }
-}
-
-// walkOne computes the minimal number of dereferences from root to
-// all other locations.
-func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
- // The data flow graph has negative edges (from addressing
- // operations), so we use the Bellman-Ford algorithm. However,
- // we don't have to worry about infinite negative cycles since
- // we bound intermediate dereference counts to 0.
-
- root.walkgen = walkgen
- root.derefs = 0
- root.dst = nil
-
- todo := []*location{root} // LIFO queue
- for len(todo) > 0 {
- l := todo[len(todo)-1]
- todo = todo[:len(todo)-1]
-
- derefs := l.derefs
-
- // If l.derefs < 0, then l's address flows to root.
- addressOf := derefs < 0
- if addressOf {
- // For a flow path like "root = &l; l = x",
- // l's address flows to root, but x's does
- // not. We recognize this by lower bounding
- // derefs at 0.
- derefs = 0
-
- // If l's address flows to a non-transient
- // location, then l can't be transiently
- // allocated.
- if !root.transient && l.transient {
- l.transient = false
- enqueue(l)
- }
- }
-
- if b.outlives(root, l) {
- // l's value flows to root. If l is a function
- // parameter and root is the heap or a
- // corresponding result parameter, then record
- // that value flow for tagging the function
- // later.
- if l.isName(ir.PPARAM) {
- if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
- if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
- }
- explanation := b.explainPath(root, l)
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
- fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
- }
- }
- l.leakTo(root, derefs)
- }
-
- // If l's address flows somewhere that
- // outlives it, then l needs to be heap
- // allocated.
- if addressOf && !l.escapes {
- if logopt.Enabled() || base.Flag.LowerM >= 2 {
- if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
- }
- explanation := b.explainPath(root, l)
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
- }
- }
- l.escapes = true
- enqueue(l)
- continue
- }
- }
-
- for i, edge := range l.edges {
- if edge.src.escapes {
- continue
- }
- d := derefs + edge.derefs
- if edge.src.walkgen != walkgen || edge.src.derefs > d {
- edge.src.walkgen = walkgen
- edge.src.derefs = d
- edge.src.dst = l
- edge.src.dstEdgeIdx = i
- todo = append(todo, edge.src)
- }
- }
- }
-}
-
-// explainPath prints an explanation of how src flows to the walk root.
-func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
- visited := make(map[*location]bool)
- pos := base.FmtPos(src.n.Pos())
- var explanation []*logopt.LoggedOpt
- for {
- // Prevent infinite loop.
- if visited[src] {
- if base.Flag.LowerM >= 2 {
- fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
- }
- break
- }
- visited[src] = true
- dst := src.dst
- edge := &dst.edges[src.dstEdgeIdx]
- if edge.src != src {
- base.Fatalf("path inconsistency: %v != %v", edge.src, src)
- }
-
- explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
-
- if dst == root {
- break
- }
- src = dst
- }
-
- return explanation
-}
-
-func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
- ops := "&"
- if derefs >= 0 {
- ops = strings.Repeat("*", derefs)
- }
- print := base.Flag.LowerM >= 2
-
- flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
- if print {
- fmt.Printf("%s:%s\n", pos, flow)
- }
- if logopt.Enabled() {
- var epos src.XPos
- if notes != nil {
- epos = notes.where.Pos()
- } else if srcloc != nil && srcloc.n != nil {
- epos = srcloc.n.Pos()
- }
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
- }
-
- for note := notes; note != nil; note = note.next {
- if print {
- fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
- }
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
- fmt.Sprintf(" from %v (%v)", note.where, note.why)))
- }
- }
- return explanation
-}
-
-func (b *batch) explainLoc(l *location) string {
- if l == &b.heapLoc {
- return "{heap}"
- }
- if l.n == nil {
- // TODO(mdempsky): Omit entirely.
- return "{temp}"
- }
- if l.n.Op() == ir.ONAME {
- return fmt.Sprintf("%v", l.n)
- }
- return fmt.Sprintf("{storage for %v}", l.n)
-}
-
-// outlives reports whether values stored in l may survive beyond
-// other's lifetime if stack allocated.
-func (b *batch) outlives(l, other *location) bool {
- // The heap outlives everything.
- if l.escapes {
- return true
- }
-
- // We don't know what callers do with returned values, so
- // pessimistically we need to assume they flow to the heap and
- // outlive everything too.
- if l.isName(ir.PPARAMOUT) {
- // Exception: Directly called closures can return
- // locations allocated outside of them without forcing
- // them to the heap. For example:
- //
- // var u int // okay to stack allocate
- // *(func() *int { return &u }()) = 42
- if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
- return false
- }
-
- return true
- }
-
- // If l and other are within the same function, then l
- // outlives other if it was declared outside other's loop
- // scope. For example:
- //
- // var l *int
- // for {
- // l = new(int)
- // }
- if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
- return true
- }
-
- // If other is declared within a child closure of where l is
- // declared, then l outlives it. For example:
- //
- // var l *int
- // func() {
- // l = new(int)
- // }
- if containsClosure(l.curfn, other.curfn) {
- return true
- }
-
- return false
-}
-
-// containsClosure reports whether c is a closure contained within f.
-func containsClosure(f, c *ir.Func) bool {
- // Common case.
- if f == c {
- return false
- }
-
- // Closures within function Foo are named like "Foo.funcN..."
- // TODO(mdempsky): Better way to recognize this.
- fn := f.Sym().Name
- cn := c.Sym().Name
- return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
-}
-
-// leak records that parameter l leaks to sink.
-func (l *location) leakTo(sink *location, derefs int) {
- // If sink is a result parameter that doesn't escape (#44614)
- // and we can fit return bits into the escape analysis tag,
- // then record as a result leak.
- if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
- ri := sink.resultIndex - 1
- if ri < numEscResults {
- // Leak to result parameter.
- l.paramEsc.AddResult(ri, derefs)
- return
- }
- }
-
- // Otherwise, record as heap leak.
- l.paramEsc.AddHeap(derefs)
-}
-
-func (b *batch) finish(fns []*ir.Func) {
- // Record parameter tags for package export data.
- for _, fn := range fns {
- fn.SetEsc(escFuncTagged)
-
- narg := 0
- for _, fs := range &types.RecvsParams {
- for _, f := range fs(fn.Type()).Fields().Slice() {
- narg++
- f.Note = b.paramTag(fn, narg, f)
- }
- }
- }
-
- for _, loc := range b.allLocs {
- n := loc.n
- if n == nil {
- continue
- }
- if n.Op() == ir.ONAME {
- n := n.(*ir.Name)
- n.Opt = nil
- }
-
- // Update n.Esc based on escape analysis results.
-
- if loc.escapes {
- if n.Op() == ir.ONAME {
- if base.Flag.CompilingRuntime {
- base.ErrorfAt(n.Pos(), "%v escapes to heap, not allowed in runtime", n)
- }
- if base.Flag.LowerM != 0 {
- base.WarnfAt(n.Pos(), "moved to heap: %v", n)
- }
- } else {
- if base.Flag.LowerM != 0 {
- base.WarnfAt(n.Pos(), "%v escapes to heap", n)
- }
- if logopt.Enabled() {
- var e_curfn *ir.Func // TODO(mdempsky): Fix.
- logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
- }
- }
- n.SetEsc(ir.EscHeap)
- } else {
- if base.Flag.LowerM != 0 && n.Op() != ir.ONAME {
- base.WarnfAt(n.Pos(), "%v does not escape", n)
- }
- n.SetEsc(ir.EscNone)
- if loc.transient {
- switch n.Op() {
- case ir.OCLOSURE:
- n := n.(*ir.ClosureExpr)
- n.SetTransient(true)
- case ir.OCALLPART:
- n := n.(*ir.SelectorExpr)
- n.SetTransient(true)
- case ir.OSLICELIT:
- n := n.(*ir.CompLitExpr)
- n.SetTransient(true)
- }
- }
- }
- }
-}
-
-func (l *location) isName(c ir.Class) bool {
- return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
-}
-
-const numEscResults = 7
-
-// An leaks represents a set of assignment flows from a parameter
-// to the heap or to any of its function's (first numEscResults)
-// result parameters.
-type leaks [1 + numEscResults]uint8
-
-// Empty reports whether l is an empty set (i.e., no assignment flows).
-func (l leaks) Empty() bool { return l == leaks{} }
-
-// Heap returns the minimum deref count of any assignment flow from l
-// to the heap. If no such flows exist, Heap returns -1.
-func (l leaks) Heap() int { return l.get(0) }
-
-// Result returns the minimum deref count of any assignment flow from
-// l to its function's i'th result parameter. If no such flows exist,
-// Result returns -1.
-func (l leaks) Result(i int) int { return l.get(1 + i) }
-
-// AddHeap adds an assignment flow from l to the heap.
-func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) }
-
-// AddResult adds an assignment flow from l to its function's i'th
-// result parameter.
-func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) }
-
-func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) }
-
-func (l leaks) get(i int) int { return int(l[i]) - 1 }
-
-func (l *leaks) add(i, derefs int) {
- if old := l.get(i); old < 0 || derefs < old {
- l.set(i, derefs)
- }
-}
-
-func (l *leaks) set(i, derefs int) {
- v := derefs + 1
- if v < 0 {
- base.Fatalf("invalid derefs count: %v", derefs)
- }
- if v > math.MaxUint8 {
- v = math.MaxUint8
- }
-
- l[i] = uint8(v)
-}
-
-// Optimize removes result flow paths that are equal in length or
-// longer than the shortest heap flow path.
-func (l *leaks) Optimize() {
- // If we have a path to the heap, then there's no use in
- // keeping equal or longer paths elsewhere.
- if x := l.Heap(); x >= 0 {
- for i := 0; i < numEscResults; i++ {
- if l.Result(i) >= x {
- l.setResult(i, -1)
- }
- }
- }
-}
-
-var leakTagCache = map[leaks]string{}
-
-// Encode converts l into a binary string for export data.
-func (l leaks) Encode() string {
- if l.Heap() == 0 {
- // Space optimization: empty string encodes more
- // efficiently in export data.
- return ""
- }
- if s, ok := leakTagCache[l]; ok {
- return s
- }
-
- n := len(l)
- for n > 0 && l[n-1] == 0 {
- n--
- }
- s := "esc:" + string(l[:n])
- leakTagCache[l] = s
- return s
-}
-
-// parseLeaks parses a binary string representing a leaks
-func parseLeaks(s string) leaks {
- var l leaks
- if !strings.HasPrefix(s, "esc:") {
- l.AddHeap(0)
- return l
- }
- copy(l[:], s[4:])
- return l
-}
-
-func Funcs(all []ir.Node) {
- ir.VisitFuncsBottomUp(all, Batch)
-}
-
-const (
- escFuncUnknown = 0 + iota
- escFuncPlanned
- escFuncStarted
- escFuncTagged
-)
-
-// Mark labels that have no backjumps to them as not increasing e.loopdepth.
-type labelState int
-
-const (
- looping labelState = 1 + iota
- nonlooping
-)
-
-func isSliceSelfAssign(dst, src ir.Node) bool {
- // Detect the following special case.
- //
- // func (b *Buffer) Foo() {
- // n, m := ...
- // b.buf = b.buf[n:m]
- // }
- //
- // This assignment is a no-op for escape analysis,
- // it does not store any new pointers into b that were not already there.
- // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
- // Here we assume that the statement will not contain calls,
- // that is, that order will move any calls to init.
- // Otherwise base ONAME value could change between the moments
- // when we evaluate it for dst and for src.
-
- // dst is ONAME dereference.
- var dstX ir.Node
- switch dst.Op() {
- default:
- return false
- case ir.ODEREF:
- dst := dst.(*ir.StarExpr)
- dstX = dst.X
- case ir.ODOTPTR:
- dst := dst.(*ir.SelectorExpr)
- dstX = dst.X
- }
- if dstX.Op() != ir.ONAME {
- return false
- }
- // src is a slice operation.
- switch src.Op() {
- case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
- // OK.
- case ir.OSLICEARR, ir.OSLICE3ARR:
- // Since arrays are embedded into containing object,
- // slice of non-pointer array will introduce a new pointer into b that was not already there
- // (pointer to b itself). After such assignment, if b contents escape,
- // b escapes as well. If we ignore such OSLICEARR, we will conclude
- // that b does not escape when b contents do.
- //
- // Pointer to an array is OK since it's not stored inside b directly.
- // For slicing an array (not pointer to array), there is an implicit OADDR.
- // We check that to determine non-pointer array slicing.
- src := src.(*ir.SliceExpr)
- if src.X.Op() == ir.OADDR {
- return false
- }
- default:
- return false
- }
- // slice is applied to ONAME dereference.
- var baseX ir.Node
- switch base := src.(*ir.SliceExpr).X; base.Op() {
- default:
- return false
- case ir.ODEREF:
- base := base.(*ir.StarExpr)
- baseX = base.X
- case ir.ODOTPTR:
- base := base.(*ir.SelectorExpr)
- baseX = base.X
- }
- if baseX.Op() != ir.ONAME {
- return false
- }
- // dst and src reference the same base ONAME.
- return dstX.(*ir.Name) == baseX.(*ir.Name)
-}
-
-// isSelfAssign reports whether assignment from src to dst can
-// be ignored by the escape analysis as it's effectively a self-assignment.
-func isSelfAssign(dst, src ir.Node) bool {
- if isSliceSelfAssign(dst, src) {
- return true
- }
-
- // Detect trivial assignments that assign back to the same object.
- //
- // It covers these cases:
- // val.x = val.y
- // val.x[i] = val.y[j]
- // val.x1.x2 = val.x1.y2
- // ... etc
- //
- // These assignments do not change assigned object lifetime.
-
- if dst == nil || src == nil || dst.Op() != src.Op() {
- return false
- }
-
- // The expression prefix must be both "safe" and identical.
- switch dst.Op() {
- case ir.ODOT, ir.ODOTPTR:
- // Safe trailing accessors that are permitted to differ.
- dst := dst.(*ir.SelectorExpr)
- src := src.(*ir.SelectorExpr)
- return ir.SameSafeExpr(dst.X, src.X)
- case ir.OINDEX:
- dst := dst.(*ir.IndexExpr)
- src := src.(*ir.IndexExpr)
- if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
- return false
- }
- return ir.SameSafeExpr(dst.X, src.X)
- default:
- return false
- }
-}
-
-// mayAffectMemory reports whether evaluation of n may affect the program's
-// memory state. If the expression can't affect memory state, then it can be
-// safely ignored by the escape analysis.
-func mayAffectMemory(n ir.Node) bool {
- // We may want to use a list of "memory safe" ops instead of generally
- // "side-effect free", which would include all calls and other ops that can
- // allocate or change global state. For now, it's safer to start with the latter.
- //
- // We're ignoring things like division by zero, index out of range,
- // and nil pointer dereference here.
-
- // TODO(rsc): It seems like it should be possible to replace this with
- // an ir.Any looking for any op that's not the ones in the case statement.
- // But that produces changes in the compiled output detected by buildall.
- switch n.Op() {
- case ir.ONAME, ir.OLITERAL, ir.ONIL:
- return false
-
- case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
- n := n.(*ir.BinaryExpr)
- return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
-
- case ir.OINDEX:
- n := n.(*ir.IndexExpr)
- return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
-
- case ir.OCONVNOP, ir.OCONV:
- n := n.(*ir.ConvExpr)
- return mayAffectMemory(n.X)
-
- case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
- n := n.(*ir.UnaryExpr)
- return mayAffectMemory(n.X)
-
- case ir.ODOT, ir.ODOTPTR:
- n := n.(*ir.SelectorExpr)
- return mayAffectMemory(n.X)
-
- case ir.ODEREF:
- n := n.(*ir.StarExpr)
- return mayAffectMemory(n.X)
-
- default:
- return true
- }
-}
-
-// HeapAllocReason returns the reason the given Node must be heap
-// allocated, or the empty string if it doesn't.
-func HeapAllocReason(n ir.Node) string {
- if n == nil || n.Type() == nil {
- return ""
- }
-
- // Parameters are always passed via the stack.
- if n.Op() == ir.ONAME {
- n := n.(*ir.Name)
- if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
- return ""
- }
- }
-
- if n.Type().Width > ir.MaxStackVarSize {
- return "too large for stack"
- }
-
- if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize {
- return "too large for stack"
- }
-
- if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
- return "too large for stack"
- }
- if n.Op() == ir.OCALLPART && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
- return "too large for stack"
- }
-
- if n.Op() == ir.OMAKESLICE {
- n := n.(*ir.MakeExpr)
- r := n.Cap
- if r == nil {
- r = n.Len
- }
- if !ir.IsSmallIntConst(r) {
- return "non-constant size"
- }
- if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width {
- return "too large for stack"
- }
- }
-
- return ""
-}
-
-// This special tag is applied to uintptr variables
-// that we believe may hold unsafe.Pointers for
-// calls into assembly functions.
-const UnsafeUintptrNote = "unsafe-uintptr"
-
-// This special tag is applied to uintptr parameters of functions
-// marked go:uintptrescapes.
-const UintptrEscapesNote = "uintptr-escapes"
-
-func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
- name := func() string {
- if f.Sym != nil {
- return f.Sym.Name
- }
- return fmt.Sprintf("arg#%d", narg)
- }
-
- if len(fn.Body) == 0 {
- // Assume that uintptr arguments must be held live across the call.
- // This is most important for syscall.Syscall.
- // See golang.org/issue/13372.
- // This really doesn't have much to do with escape analysis per se,
- // but we are reusing the ability to annotate an individual function
- // argument and pass those annotations along to importing code.
- if f.Type.IsUintptr() {
- if base.Flag.LowerM != 0 {
- base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
- }
- return UnsafeUintptrNote
- }
-
- if !f.Type.HasPointers() { // don't bother tagging for scalars
- return ""
- }
-
- var esc leaks
-
- // External functions are assumed unsafe, unless
- // //go:noescape is given before the declaration.
- if fn.Pragma&ir.Noescape != 0 {
- if base.Flag.LowerM != 0 && f.Sym != nil {
- base.WarnfAt(f.Pos, "%v does not escape", name())
- }
- } else {
- if base.Flag.LowerM != 0 && f.Sym != nil {
- base.WarnfAt(f.Pos, "leaking param: %v", name())
- }
- esc.AddHeap(0)
- }
-
- return esc.Encode()
- }
-
- if fn.Pragma&ir.UintptrEscapes != 0 {
- if f.Type.IsUintptr() {
- if base.Flag.LowerM != 0 {
- base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
- }
- return UintptrEscapesNote
+ return ""
}
if f.IsDDD() && f.Type.Elem().IsUintptr() {
// final argument is ...uintptr.
- if base.Flag.LowerM != 0 {
+ if diagnose {
base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name())
}
- return UintptrEscapesNote
+ return ""
}
}
@@ -2125,7 +447,7 @@ func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
esc := loc.paramEsc
esc.Optimize()
- if base.Flag.LowerM != 0 && !loc.escapes {
+ if diagnose && !loc.escapes {
if esc.Empty() {
base.WarnfAt(f.Pos, "%v does not escape", name())
}
diff --git a/src/cmd/compile/internal/escape/expr.go b/src/cmd/compile/internal/escape/expr.go
new file mode 100644
index 0000000000000000000000000000000000000000..ced90a47bcb6632a4c8df5a0a1664ab5de409429
--- /dev/null
+++ b/src/cmd/compile/internal/escape/expr.go
@@ -0,0 +1,335 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+)
+
+// expr models evaluating an expression n and flowing the result into
+// hole k.
+func (e *escape) expr(k hole, n ir.Node) {
+ if n == nil {
+ return
+ }
+ e.stmts(n.Init())
+ e.exprSkipInit(k, n)
+}
+
+func (e *escape) exprSkipInit(k hole, n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ defer func() {
+ base.Pos = lno
+ }()
+
+ if k.derefs >= 0 && !n.Type().IsUntyped() && !n.Type().HasPointers() {
+ k.dst = &e.blankLoc
+ }
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected expr: %s %v", n.Op().String(), n)
+
+ case ir.OLITERAL, ir.ONIL, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OTYPE, ir.OMETHEXPR, ir.OLINKSYMOFFSET:
+ // nop
+
+ case ir.ONAME:
+ n := n.(*ir.Name)
+ if n.Class == ir.PFUNC || n.Class == ir.PEXTERN {
+ return
+ }
+ e.flow(k, e.oldLoc(n))
+
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT:
+ n := n.(*ir.UnaryExpr)
+ e.discard(n.X)
+ case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
+ n := n.(*ir.BinaryExpr)
+ e.discard(n.X)
+ e.discard(n.Y)
+ case ir.OANDAND, ir.OOROR:
+ n := n.(*ir.LogicalExpr)
+ e.discard(n.X)
+ e.discard(n.Y)
+ case ir.OADDR:
+ n := n.(*ir.AddrExpr)
+ e.expr(k.addr(n, "address-of"), n.X) // "address-of"
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ e.expr(k.deref(n, "indirection"), n.X) // "indirection"
+ case ir.ODOT, ir.ODOTMETH, ir.ODOTINTER:
+ n := n.(*ir.SelectorExpr)
+ e.expr(k.note(n, "dot"), n.X)
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ e.expr(k.deref(n, "dot of pointer"), n.X) // "dot of pointer"
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ n := n.(*ir.TypeAssertExpr)
+ e.expr(k.dotType(n.Type(), n, "dot"), n.X)
+ case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
+ n := n.(*ir.DynamicTypeAssertExpr)
+ e.expr(k.dotType(n.Type(), n, "dot"), n.X)
+ // n.T doesn't need to be tracked; it always points to read-only storage.
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ if n.X.Type().IsArray() {
+ e.expr(k.note(n, "fixed-array-index-of"), n.X)
+ } else {
+ // TODO(mdempsky): Fix why reason text.
+ e.expr(k.deref(n, "dot of pointer"), n.X)
+ }
+ e.discard(n.Index)
+ case ir.OINDEXMAP:
+ n := n.(*ir.IndexExpr)
+ e.discard(n.X)
+ e.discard(n.Index)
+ case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR:
+ n := n.(*ir.SliceExpr)
+ e.expr(k.note(n, "slice"), n.X)
+ e.discard(n.Low)
+ e.discard(n.High)
+ e.discard(n.Max)
+
+ case ir.OCONV, ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
+ // When -d=checkptr=2 is enabled, treat
+ // conversions to unsafe.Pointer as an
+ // escaping operation. This allows better
+ // runtime instrumentation, since we can more
+ // easily detect object boundaries on the heap
+ // than the stack.
+ e.assignHeap(n.X, "conversion to unsafe.Pointer", n)
+ } else if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() {
+ e.unsafeValue(k, n.X)
+ } else {
+ e.expr(k, n.X)
+ }
+ case ir.OCONVIFACE, ir.OCONVIDATA:
+ n := n.(*ir.ConvExpr)
+ if !n.X.Type().IsInterface() && !types.IsDirectIface(n.X.Type()) {
+ k = e.spill(k, n)
+ }
+ e.expr(k.note(n, "interface-converted"), n.X)
+ case ir.OEFACE:
+ n := n.(*ir.BinaryExpr)
+ // Note: n.X is not needed because it can never point to memory that might escape.
+ e.expr(k, n.Y)
+ case ir.OIDATA, ir.OSPTR:
+ n := n.(*ir.UnaryExpr)
+ e.expr(k, n.X)
+ case ir.OSLICE2ARRPTR:
+ // the slice pointer flows directly to the result
+ n := n.(*ir.ConvExpr)
+ e.expr(k, n.X)
+ case ir.ORECV:
+ n := n.(*ir.UnaryExpr)
+ e.discard(n.X)
+
+ case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER, ir.OUNSAFEADD, ir.OUNSAFESLICE:
+ e.call([]hole{k}, n)
+
+ case ir.ONEW:
+ n := n.(*ir.UnaryExpr)
+ e.spill(k, n)
+
+ case ir.OMAKESLICE:
+ n := n.(*ir.MakeExpr)
+ e.spill(k, n)
+ e.discard(n.Len)
+ e.discard(n.Cap)
+ case ir.OMAKECHAN:
+ n := n.(*ir.MakeExpr)
+ e.discard(n.Len)
+ case ir.OMAKEMAP:
+ n := n.(*ir.MakeExpr)
+ e.spill(k, n)
+ e.discard(n.Len)
+
+ case ir.OMETHVALUE:
+ // Flow the receiver argument to both the closure and
+ // to the receiver parameter.
+
+ n := n.(*ir.SelectorExpr)
+ closureK := e.spill(k, n)
+
+ m := n.Selection
+
+ // We don't know how the method value will be called
+ // later, so conservatively assume the result
+ // parameters all flow to the heap.
+ //
+ // TODO(mdempsky): Change ks into a callback, so that
+ // we don't have to create this slice?
+ var ks []hole
+ for i := m.Type.NumResults(); i > 0; i-- {
+ ks = append(ks, e.heapHole())
+ }
+ name, _ := m.Nname.(*ir.Name)
+ paramK := e.tagHole(ks, name, m.Type.Recv())
+
+ e.expr(e.teeHole(paramK, closureK), n.X)
+
+ case ir.OPTRLIT:
+ n := n.(*ir.AddrExpr)
+ e.expr(e.spill(k, n), n.X)
+
+ case ir.OARRAYLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elt := range n.List {
+ if elt.Op() == ir.OKEY {
+ elt = elt.(*ir.KeyExpr).Value
+ }
+ e.expr(k.note(n, "array literal element"), elt)
+ }
+
+ case ir.OSLICELIT:
+ n := n.(*ir.CompLitExpr)
+ k = e.spill(k, n)
+
+ for _, elt := range n.List {
+ if elt.Op() == ir.OKEY {
+ elt = elt.(*ir.KeyExpr).Value
+ }
+ e.expr(k.note(n, "slice-literal-element"), elt)
+ }
+
+ case ir.OSTRUCTLIT:
+ n := n.(*ir.CompLitExpr)
+ for _, elt := range n.List {
+ e.expr(k.note(n, "struct literal element"), elt.(*ir.StructKeyExpr).Value)
+ }
+
+ case ir.OMAPLIT:
+ n := n.(*ir.CompLitExpr)
+ e.spill(k, n)
+
+ // Map keys and values are always stored in the heap.
+ for _, elt := range n.List {
+ elt := elt.(*ir.KeyExpr)
+ e.assignHeap(elt.Key, "map literal key", n)
+ e.assignHeap(elt.Value, "map literal value", n)
+ }
+
+ case ir.OCLOSURE:
+ n := n.(*ir.ClosureExpr)
+ k = e.spill(k, n)
+ e.closures = append(e.closures, closure{k, n})
+
+ if fn := n.Func; fn.IsHiddenClosure() {
+ for _, cv := range fn.ClosureVars {
+ if loc := e.oldLoc(cv); !loc.captured {
+ loc.captured = true
+
+ // Ignore reassignments to the variable in straightline code
+ // preceding the first capture by a closure.
+ if loc.loopDepth == e.loopDepth {
+ loc.reassigned = false
+ }
+ }
+ }
+
+ for _, n := range fn.Dcl {
+ // Add locations for local variables of the
+ // closure, if needed, in case we're not including
+ // the closure func in the batch for escape
+ // analysis (happens for escape analysis called
+ // from reflectdata.methodWrapper)
+ if n.Op() == ir.ONAME && n.Opt == nil {
+ e.with(fn).newLoc(n, false)
+ }
+ }
+ e.walkFunc(fn)
+ }
+
+ case ir.ORUNES2STR, ir.OBYTES2STR, ir.OSTR2RUNES, ir.OSTR2BYTES, ir.ORUNESTR:
+ n := n.(*ir.ConvExpr)
+ e.spill(k, n)
+ e.discard(n.X)
+
+ case ir.OADDSTR:
+ n := n.(*ir.AddStringExpr)
+ e.spill(k, n)
+
+ // Arguments of OADDSTR never escape;
+ // runtime.concatstrings makes sure of that.
+ e.discards(n.List)
+
+ case ir.ODYNAMICTYPE:
+ // Nothing to do - argument is a *runtime._type (+ maybe a *runtime.itab) pointing to static data section
+ }
+}
+
+// unsafeValue evaluates a uintptr-typed arithmetic expression looking
+// for conversions from an unsafe.Pointer.
+func (e *escape) unsafeValue(k hole, n ir.Node) {
+ if n.Type().Kind() != types.TUINTPTR {
+ base.Fatalf("unexpected type %v for %v", n.Type(), n)
+ }
+ if k.addrtaken {
+ base.Fatalf("unexpected addrtaken")
+ }
+
+ e.stmts(n.Init())
+
+ switch n.Op() {
+ case ir.OCONV, ir.OCONVNOP:
+ n := n.(*ir.ConvExpr)
+ if n.X.Type().IsUnsafePtr() {
+ e.expr(k, n.X)
+ } else {
+ e.discard(n.X)
+ }
+ case ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ if ir.IsReflectHeaderDataField(n) {
+ e.expr(k.deref(n, "reflect.Header.Data"), n.X)
+ } else {
+ e.discard(n.X)
+ }
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT:
+ n := n.(*ir.UnaryExpr)
+ e.unsafeValue(k, n.X)
+ case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.ODIV, ir.OMOD, ir.OAND, ir.OANDNOT:
+ n := n.(*ir.BinaryExpr)
+ e.unsafeValue(k, n.X)
+ e.unsafeValue(k, n.Y)
+ case ir.OLSH, ir.ORSH:
+ n := n.(*ir.BinaryExpr)
+ e.unsafeValue(k, n.X)
+ // RHS need not be uintptr-typed (#32959) and can't meaningfully
+ // flow pointers anyway.
+ e.discard(n.Y)
+ default:
+ e.exprSkipInit(e.discardHole(), n)
+ }
+}
+
+// discard evaluates an expression n for side-effects, but discards
+// its value.
+func (e *escape) discard(n ir.Node) {
+ e.expr(e.discardHole(), n)
+}
+
+func (e *escape) discards(l ir.Nodes) {
+ for _, n := range l {
+ e.discard(n)
+ }
+}
+
+// spill allocates a new location associated with expression n, flows
+// its address to k, and returns a hole that flows values to it. It's
+// intended for use with most expressions that allocate storage.
+func (e *escape) spill(k hole, n ir.Node) hole {
+ loc := e.newLoc(n, true)
+ e.flow(k.addr(n, "spill"), loc)
+ return loc.asHole()
+}
diff --git a/src/cmd/compile/internal/escape/graph.go b/src/cmd/compile/internal/escape/graph.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc3d078adddf8796349ab5aa813c81615450806b
--- /dev/null
+++ b/src/cmd/compile/internal/escape/graph.go
@@ -0,0 +1,324 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/logopt"
+ "cmd/compile/internal/types"
+ "fmt"
+)
+
+// Below we implement the methods for walking the AST and recording
+// data flow edges. Note that because a sub-expression might have
+// side-effects, it's important to always visit the entire AST.
+//
+// For example, write either:
+//
+// if x {
+// e.discard(n.Left)
+// } else {
+// e.value(k, n.Left)
+// }
+//
+// or
+//
+// if x {
+// k = e.discardHole()
+// }
+// e.value(k, n.Left)
+//
+// Do NOT write:
+//
+// // BAD: possibly loses side-effects within n.Left
+// if !x {
+// e.value(k, n.Left)
+// }
+
+// An location represents an abstract location that stores a Go
+// variable.
+type location struct {
+ n ir.Node // represented variable or expression, if any
+ curfn *ir.Func // enclosing function
+ edges []edge // incoming edges
+ loopDepth int // loopDepth at declaration
+
+ // resultIndex records the tuple index (starting at 1) for
+ // PPARAMOUT variables within their function's result type.
+ // For non-PPARAMOUT variables it's 0.
+ resultIndex int
+
+ // derefs and walkgen are used during walkOne to track the
+ // minimal dereferences from the walk root.
+ derefs int // >= -1
+ walkgen uint32
+
+ // dst and dstEdgeindex track the next immediate assignment
+ // destination location during walkone, along with the index
+ // of the edge pointing back to this location.
+ dst *location
+ dstEdgeIdx int
+
+ // queued is used by walkAll to track whether this location is
+ // in the walk queue.
+ queued bool
+
+ // escapes reports whether the represented variable's address
+ // escapes; that is, whether the variable must be heap
+ // allocated.
+ escapes bool
+
+ // transient reports whether the represented expression's
+ // address does not outlive the statement; that is, whether
+ // its storage can be immediately reused.
+ transient bool
+
+ // paramEsc records the represented parameter's leak set.
+ paramEsc leaks
+
+ captured bool // has a closure captured this variable?
+ reassigned bool // has this variable been reassigned?
+ addrtaken bool // has this variable's address been taken?
+}
+
+// An edge represents an assignment edge between two Go variables.
+type edge struct {
+ src *location
+ derefs int // >= -1
+ notes *note
+}
+
+func (l *location) asHole() hole {
+ return hole{dst: l}
+}
+
+// leak records that parameter l leaks to sink.
+func (l *location) leakTo(sink *location, derefs int) {
+ // If sink is a result parameter that doesn't escape (#44614)
+ // and we can fit return bits into the escape analysis tag,
+ // then record as a result leak.
+ if !sink.escapes && sink.isName(ir.PPARAMOUT) && sink.curfn == l.curfn {
+ ri := sink.resultIndex - 1
+ if ri < numEscResults {
+ // Leak to result parameter.
+ l.paramEsc.AddResult(ri, derefs)
+ return
+ }
+ }
+
+ // Otherwise, record as heap leak.
+ l.paramEsc.AddHeap(derefs)
+}
+
+func (l *location) isName(c ir.Class) bool {
+ return l.n != nil && l.n.Op() == ir.ONAME && l.n.(*ir.Name).Class == c
+}
+
+// A hole represents a context for evaluation of a Go
+// expression. E.g., when evaluating p in "x = **p", we'd have a hole
+// with dst==x and derefs==2.
+type hole struct {
+ dst *location
+ derefs int // >= -1
+ notes *note
+
+ // addrtaken indicates whether this context is taking the address of
+ // the expression, independent of whether the address will actually
+ // be stored into a variable.
+ addrtaken bool
+}
+
+type note struct {
+ next *note
+ where ir.Node
+ why string
+}
+
+func (k hole) note(where ir.Node, why string) hole {
+ if where == nil || why == "" {
+ base.Fatalf("note: missing where/why")
+ }
+ if base.Flag.LowerM >= 2 || logopt.Enabled() {
+ k.notes = ¬e{
+ next: k.notes,
+ where: where,
+ why: why,
+ }
+ }
+ return k
+}
+
+func (k hole) shift(delta int) hole {
+ k.derefs += delta
+ if k.derefs < -1 {
+ base.Fatalf("derefs underflow: %v", k.derefs)
+ }
+ k.addrtaken = delta < 0
+ return k
+}
+
+func (k hole) deref(where ir.Node, why string) hole { return k.shift(1).note(where, why) }
+func (k hole) addr(where ir.Node, why string) hole { return k.shift(-1).note(where, why) }
+
+func (k hole) dotType(t *types.Type, where ir.Node, why string) hole {
+ if !t.IsInterface() && !types.IsDirectIface(t) {
+ k = k.shift(1)
+ }
+ return k.note(where, why)
+}
+
+func (b *batch) flow(k hole, src *location) {
+ if k.addrtaken {
+ src.addrtaken = true
+ }
+
+ dst := k.dst
+ if dst == &b.blankLoc {
+ return
+ }
+ if dst == src && k.derefs >= 0 { // dst = dst, dst = *dst, ...
+ return
+ }
+ if dst.escapes && k.derefs < 0 { // dst = &src
+ if base.Flag.LowerM >= 2 || logopt.Enabled() {
+ pos := base.FmtPos(src.n.Pos())
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: %v escapes to heap:\n", pos, src.n)
+ }
+ explanation := b.explainFlow(pos, dst, src, k.derefs, k.notes, []*logopt.LoggedOpt{})
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(src.n.Pos(), "escapes", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", src.n), explanation)
+ }
+
+ }
+ src.escapes = true
+ return
+ }
+
+ // TODO(mdempsky): Deduplicate edges?
+ dst.edges = append(dst.edges, edge{src: src, derefs: k.derefs, notes: k.notes})
+}
+
+func (b *batch) heapHole() hole { return b.heapLoc.asHole() }
+func (b *batch) discardHole() hole { return b.blankLoc.asHole() }
+
+func (b *batch) oldLoc(n *ir.Name) *location {
+ if n.Canonical().Opt == nil {
+ base.Fatalf("%v has no location", n)
+ }
+ return n.Canonical().Opt.(*location)
+}
+
+func (e *escape) newLoc(n ir.Node, transient bool) *location {
+ if e.curfn == nil {
+ base.Fatalf("e.curfn isn't set")
+ }
+ if n != nil && n.Type() != nil && n.Type().NotInHeap() {
+ base.ErrorfAt(n.Pos(), "%v is incomplete (or unallocatable); stack allocation disallowed", n.Type())
+ }
+
+ if n != nil && n.Op() == ir.ONAME {
+ if canon := n.(*ir.Name).Canonical(); n != canon {
+ base.Fatalf("newLoc on non-canonical %v (canonical is %v)", n, canon)
+ }
+ }
+ loc := &location{
+ n: n,
+ curfn: e.curfn,
+ loopDepth: e.loopDepth,
+ transient: transient,
+ }
+ e.allLocs = append(e.allLocs, loc)
+ if n != nil {
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if n.Class == ir.PPARAM && n.Curfn == nil {
+ // ok; hidden parameter
+ } else if n.Curfn != e.curfn {
+ base.Fatalf("curfn mismatch: %v != %v for %v", n.Curfn, e.curfn, n)
+ }
+
+ if n.Opt != nil {
+ base.Fatalf("%v already has a location", n)
+ }
+ n.Opt = loc
+ }
+ }
+ return loc
+}
+
+// teeHole returns a new hole that flows into each hole of ks,
+// similar to the Unix tee(1) command.
+func (e *escape) teeHole(ks ...hole) hole {
+ if len(ks) == 0 {
+ return e.discardHole()
+ }
+ if len(ks) == 1 {
+ return ks[0]
+ }
+ // TODO(mdempsky): Optimize if there's only one non-discard hole?
+
+ // Given holes "l1 = _", "l2 = **_", "l3 = *_", ..., create a
+ // new temporary location ltmp, wire it into place, and return
+ // a hole for "ltmp = _".
+ loc := e.newLoc(nil, true)
+ for _, k := range ks {
+ // N.B., "p = &q" and "p = &tmp; tmp = q" are not
+ // semantically equivalent. To combine holes like "l1
+ // = _" and "l2 = &_", we'd need to wire them as "l1 =
+ // *ltmp" and "l2 = ltmp" and return "ltmp = &_"
+ // instead.
+ if k.derefs < 0 {
+ base.Fatalf("teeHole: negative derefs")
+ }
+
+ e.flow(k, loc)
+ }
+ return loc.asHole()
+}
+
+// later returns a new hole that flows into k, but some time later.
+// Its main effect is to prevent immediate reuse of temporary
+// variables introduced during Order.
+func (e *escape) later(k hole) hole {
+ loc := e.newLoc(nil, false)
+ e.flow(k, loc)
+ return loc.asHole()
+}
+
+// Fmt is called from node printing to print information about escape analysis results.
+func Fmt(n ir.Node) string {
+ text := ""
+ switch n.Esc() {
+ case ir.EscUnknown:
+ break
+
+ case ir.EscHeap:
+ text = "esc(h)"
+
+ case ir.EscNone:
+ text = "esc(no)"
+
+ case ir.EscNever:
+ text = "esc(N)"
+
+ default:
+ text = fmt.Sprintf("esc(%d)", n.Esc())
+ }
+
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if loc, ok := n.Opt.(*location); ok && loc.loopDepth != 0 {
+ if text != "" {
+ text += " "
+ }
+ text += fmt.Sprintf("ld(%d)", loc.loopDepth)
+ }
+ }
+
+ return text
+}
diff --git a/src/cmd/compile/internal/escape/leaks.go b/src/cmd/compile/internal/escape/leaks.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c848a5ee7859d0f2bfe3adf4118df263c0bd1f9
--- /dev/null
+++ b/src/cmd/compile/internal/escape/leaks.go
@@ -0,0 +1,106 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/base"
+ "math"
+ "strings"
+)
+
+const numEscResults = 7
+
+// An leaks represents a set of assignment flows from a parameter
+// to the heap or to any of its function's (first numEscResults)
+// result parameters.
+type leaks [1 + numEscResults]uint8
+
+// Empty reports whether l is an empty set (i.e., no assignment flows).
+func (l leaks) Empty() bool { return l == leaks{} }
+
+// Heap returns the minimum deref count of any assignment flow from l
+// to the heap. If no such flows exist, Heap returns -1.
+func (l leaks) Heap() int { return l.get(0) }
+
+// Result returns the minimum deref count of any assignment flow from
+// l to its function's i'th result parameter. If no such flows exist,
+// Result returns -1.
+func (l leaks) Result(i int) int { return l.get(1 + i) }
+
+// AddHeap adds an assignment flow from l to the heap.
+func (l *leaks) AddHeap(derefs int) { l.add(0, derefs) }
+
+// AddResult adds an assignment flow from l to its function's i'th
+// result parameter.
+func (l *leaks) AddResult(i, derefs int) { l.add(1+i, derefs) }
+
+func (l *leaks) setResult(i, derefs int) { l.set(1+i, derefs) }
+
+func (l leaks) get(i int) int { return int(l[i]) - 1 }
+
+func (l *leaks) add(i, derefs int) {
+ if old := l.get(i); old < 0 || derefs < old {
+ l.set(i, derefs)
+ }
+}
+
+func (l *leaks) set(i, derefs int) {
+ v := derefs + 1
+ if v < 0 {
+ base.Fatalf("invalid derefs count: %v", derefs)
+ }
+ if v > math.MaxUint8 {
+ v = math.MaxUint8
+ }
+
+ l[i] = uint8(v)
+}
+
+// Optimize removes result flow paths that are equal in length or
+// longer than the shortest heap flow path.
+func (l *leaks) Optimize() {
+ // If we have a path to the heap, then there's no use in
+ // keeping equal or longer paths elsewhere.
+ if x := l.Heap(); x >= 0 {
+ for i := 0; i < numEscResults; i++ {
+ if l.Result(i) >= x {
+ l.setResult(i, -1)
+ }
+ }
+ }
+}
+
+var leakTagCache = map[leaks]string{}
+
+// Encode converts l into a binary string for export data.
+func (l leaks) Encode() string {
+ if l.Heap() == 0 {
+ // Space optimization: empty string encodes more
+ // efficiently in export data.
+ return ""
+ }
+ if s, ok := leakTagCache[l]; ok {
+ return s
+ }
+
+ n := len(l)
+ for n > 0 && l[n-1] == 0 {
+ n--
+ }
+ s := "esc:" + string(l[:n])
+ leakTagCache[l] = s
+ return s
+}
+
+// parseLeaks parses a binary string representing a leaks
+func parseLeaks(s string) leaks {
+ var l leaks
+ if !strings.HasPrefix(s, "esc:") {
+ l.AddHeap(0)
+ return l
+ }
+ copy(l[:], s[4:])
+ return l
+}
diff --git a/src/cmd/compile/internal/escape/solve.go b/src/cmd/compile/internal/escape/solve.go
new file mode 100644
index 0000000000000000000000000000000000000000..77d6b27dd75b01178d546b18da323f1263558566
--- /dev/null
+++ b/src/cmd/compile/internal/escape/solve.go
@@ -0,0 +1,289 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/logopt"
+ "cmd/internal/src"
+ "fmt"
+ "strings"
+)
+
+// walkAll computes the minimal dereferences between all pairs of
+// locations.
+func (b *batch) walkAll() {
+ // We use a work queue to keep track of locations that we need
+ // to visit, and repeatedly walk until we reach a fixed point.
+ //
+ // We walk once from each location (including the heap), and
+ // then re-enqueue each location on its transition from
+ // transient->!transient and !escapes->escapes, which can each
+ // happen at most once. So we take Θ(len(e.allLocs)) walks.
+
+ // LIFO queue, has enough room for e.allLocs and e.heapLoc.
+ todo := make([]*location, 0, len(b.allLocs)+1)
+ enqueue := func(loc *location) {
+ if !loc.queued {
+ todo = append(todo, loc)
+ loc.queued = true
+ }
+ }
+
+ for _, loc := range b.allLocs {
+ enqueue(loc)
+ }
+ enqueue(&b.heapLoc)
+
+ var walkgen uint32
+ for len(todo) > 0 {
+ root := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+ root.queued = false
+
+ walkgen++
+ b.walkOne(root, walkgen, enqueue)
+ }
+}
+
+// walkOne computes the minimal number of dereferences from root to
+// all other locations.
+func (b *batch) walkOne(root *location, walkgen uint32, enqueue func(*location)) {
+ // The data flow graph has negative edges (from addressing
+ // operations), so we use the Bellman-Ford algorithm. However,
+ // we don't have to worry about infinite negative cycles since
+ // we bound intermediate dereference counts to 0.
+
+ root.walkgen = walkgen
+ root.derefs = 0
+ root.dst = nil
+
+ todo := []*location{root} // LIFO queue
+ for len(todo) > 0 {
+ l := todo[len(todo)-1]
+ todo = todo[:len(todo)-1]
+
+ derefs := l.derefs
+
+ // If l.derefs < 0, then l's address flows to root.
+ addressOf := derefs < 0
+ if addressOf {
+ // For a flow path like "root = &l; l = x",
+ // l's address flows to root, but x's does
+ // not. We recognize this by lower bounding
+ // derefs at 0.
+ derefs = 0
+
+ // If l's address flows to a non-transient
+ // location, then l can't be transiently
+ // allocated.
+ if !root.transient && l.transient {
+ l.transient = false
+ enqueue(l)
+ }
+ }
+
+ if b.outlives(root, l) {
+ // l's value flows to root. If l is a function
+ // parameter and root is the heap or a
+ // corresponding result parameter, then record
+ // that value flow for tagging the function
+ // later.
+ if l.isName(ir.PPARAM) {
+ if (logopt.Enabled() || base.Flag.LowerM >= 2) && !l.escapes {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: parameter %v leaks to %s with derefs=%d:\n", base.FmtPos(l.n.Pos()), l.n, b.explainLoc(root), derefs)
+ }
+ explanation := b.explainPath(root, l)
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(l.n.Pos(), "leak", "escape", ir.FuncName(e_curfn),
+ fmt.Sprintf("parameter %v leaks to %s with derefs=%d", l.n, b.explainLoc(root), derefs), explanation)
+ }
+ }
+ l.leakTo(root, derefs)
+ }
+
+ // If l's address flows somewhere that
+ // outlives it, then l needs to be heap
+ // allocated.
+ if addressOf && !l.escapes {
+ if logopt.Enabled() || base.Flag.LowerM >= 2 {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: %v escapes to heap:\n", base.FmtPos(l.n.Pos()), l.n)
+ }
+ explanation := b.explainPath(root, l)
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ logopt.LogOpt(l.n.Pos(), "escape", "escape", ir.FuncName(e_curfn), fmt.Sprintf("%v escapes to heap", l.n), explanation)
+ }
+ }
+ l.escapes = true
+ enqueue(l)
+ continue
+ }
+ }
+
+ for i, edge := range l.edges {
+ if edge.src.escapes {
+ continue
+ }
+ d := derefs + edge.derefs
+ if edge.src.walkgen != walkgen || edge.src.derefs > d {
+ edge.src.walkgen = walkgen
+ edge.src.derefs = d
+ edge.src.dst = l
+ edge.src.dstEdgeIdx = i
+ todo = append(todo, edge.src)
+ }
+ }
+ }
+}
+
+// explainPath prints an explanation of how src flows to the walk root.
+func (b *batch) explainPath(root, src *location) []*logopt.LoggedOpt {
+ visited := make(map[*location]bool)
+ pos := base.FmtPos(src.n.Pos())
+ var explanation []*logopt.LoggedOpt
+ for {
+ // Prevent infinite loop.
+ if visited[src] {
+ if base.Flag.LowerM >= 2 {
+ fmt.Printf("%s: warning: truncated explanation due to assignment cycle; see golang.org/issue/35518\n", pos)
+ }
+ break
+ }
+ visited[src] = true
+ dst := src.dst
+ edge := &dst.edges[src.dstEdgeIdx]
+ if edge.src != src {
+ base.Fatalf("path inconsistency: %v != %v", edge.src, src)
+ }
+
+ explanation = b.explainFlow(pos, dst, src, edge.derefs, edge.notes, explanation)
+
+ if dst == root {
+ break
+ }
+ src = dst
+ }
+
+ return explanation
+}
+
+func (b *batch) explainFlow(pos string, dst, srcloc *location, derefs int, notes *note, explanation []*logopt.LoggedOpt) []*logopt.LoggedOpt {
+ ops := "&"
+ if derefs >= 0 {
+ ops = strings.Repeat("*", derefs)
+ }
+ print := base.Flag.LowerM >= 2
+
+ flow := fmt.Sprintf(" flow: %s = %s%v:", b.explainLoc(dst), ops, b.explainLoc(srcloc))
+ if print {
+ fmt.Printf("%s:%s\n", pos, flow)
+ }
+ if logopt.Enabled() {
+ var epos src.XPos
+ if notes != nil {
+ epos = notes.where.Pos()
+ } else if srcloc != nil && srcloc.n != nil {
+ epos = srcloc.n.Pos()
+ }
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ explanation = append(explanation, logopt.NewLoggedOpt(epos, "escflow", "escape", ir.FuncName(e_curfn), flow))
+ }
+
+ for note := notes; note != nil; note = note.next {
+ if print {
+ fmt.Printf("%s: from %v (%v) at %s\n", pos, note.where, note.why, base.FmtPos(note.where.Pos()))
+ }
+ if logopt.Enabled() {
+ var e_curfn *ir.Func // TODO(mdempsky): Fix.
+ explanation = append(explanation, logopt.NewLoggedOpt(note.where.Pos(), "escflow", "escape", ir.FuncName(e_curfn),
+ fmt.Sprintf(" from %v (%v)", note.where, note.why)))
+ }
+ }
+ return explanation
+}
+
+func (b *batch) explainLoc(l *location) string {
+ if l == &b.heapLoc {
+ return "{heap}"
+ }
+ if l.n == nil {
+ // TODO(mdempsky): Omit entirely.
+ return "{temp}"
+ }
+ if l.n.Op() == ir.ONAME {
+ return fmt.Sprintf("%v", l.n)
+ }
+ return fmt.Sprintf("{storage for %v}", l.n)
+}
+
+// outlives reports whether values stored in l may survive beyond
+// other's lifetime if stack allocated.
+func (b *batch) outlives(l, other *location) bool {
+ // The heap outlives everything.
+ if l.escapes {
+ return true
+ }
+
+ // We don't know what callers do with returned values, so
+ // pessimistically we need to assume they flow to the heap and
+ // outlive everything too.
+ if l.isName(ir.PPARAMOUT) {
+ // Exception: Directly called closures can return
+ // locations allocated outside of them without forcing
+ // them to the heap. For example:
+ //
+ // var u int // okay to stack allocate
+ // *(func() *int { return &u }()) = 42
+ if containsClosure(other.curfn, l.curfn) && l.curfn.ClosureCalled() {
+ return false
+ }
+
+ return true
+ }
+
+ // If l and other are within the same function, then l
+ // outlives other if it was declared outside other's loop
+ // scope. For example:
+ //
+ // var l *int
+ // for {
+ // l = new(int)
+ // }
+ if l.curfn == other.curfn && l.loopDepth < other.loopDepth {
+ return true
+ }
+
+ // If other is declared within a child closure of where l is
+ // declared, then l outlives it. For example:
+ //
+ // var l *int
+ // func() {
+ // l = new(int)
+ // }
+ if containsClosure(l.curfn, other.curfn) {
+ return true
+ }
+
+ return false
+}
+
+// containsClosure reports whether c is a closure contained within f.
+func containsClosure(f, c *ir.Func) bool {
+ // Common case.
+ if f == c {
+ return false
+ }
+
+ // Closures within function Foo are named like "Foo.funcN..."
+ // TODO(mdempsky): Better way to recognize this.
+ fn := f.Sym().Name
+ cn := c.Sym().Name
+ return len(cn) > len(fn) && cn[:len(fn)] == fn && cn[len(fn)] == '.'
+}
diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go
new file mode 100644
index 0000000000000000000000000000000000000000..0afb5d64ef681433aa031e2334da33c71d3e3991
--- /dev/null
+++ b/src/cmd/compile/internal/escape/stmt.go
@@ -0,0 +1,208 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "fmt"
+)
+
+// stmt evaluates a single Go statement.
+func (e *escape) stmt(n ir.Node) {
+ if n == nil {
+ return
+ }
+
+ lno := ir.SetPos(n)
+ defer func() {
+ base.Pos = lno
+ }()
+
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v:[%d] %v stmt: %v\n", base.FmtPos(base.Pos), e.loopDepth, e.curfn, n)
+ }
+
+ e.stmts(n.Init())
+
+ switch n.Op() {
+ default:
+ base.Fatalf("unexpected stmt: %v", n)
+
+ case ir.ODCLCONST, ir.ODCLTYPE, ir.OFALL, ir.OINLMARK:
+ // nop
+
+ case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
+ // TODO(mdempsky): Handle dead code?
+
+ case ir.OBLOCK:
+ n := n.(*ir.BlockStmt)
+ e.stmts(n.List)
+
+ case ir.ODCL:
+ // Record loop depth at declaration.
+ n := n.(*ir.Decl)
+ if !ir.IsBlank(n.X) {
+ e.dcl(n.X)
+ }
+
+ case ir.OLABEL:
+ n := n.(*ir.LabelStmt)
+ switch e.labels[n.Label] {
+ case nonlooping:
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v:%v non-looping label\n", base.FmtPos(base.Pos), n)
+ }
+ case looping:
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v: %v looping label\n", base.FmtPos(base.Pos), n)
+ }
+ e.loopDepth++
+ default:
+ base.Fatalf("label missing tag")
+ }
+ delete(e.labels, n.Label)
+
+ case ir.OIF:
+ n := n.(*ir.IfStmt)
+ e.discard(n.Cond)
+ e.block(n.Body)
+ e.block(n.Else)
+
+ case ir.OFOR, ir.OFORUNTIL:
+ n := n.(*ir.ForStmt)
+ e.loopDepth++
+ e.discard(n.Cond)
+ e.stmt(n.Post)
+ e.block(n.Body)
+ e.loopDepth--
+
+ case ir.ORANGE:
+ // for Key, Value = range X { Body }
+ n := n.(*ir.RangeStmt)
+
+ // X is evaluated outside the loop.
+ tmp := e.newLoc(nil, false)
+ e.expr(tmp.asHole(), n.X)
+
+ e.loopDepth++
+ ks := e.addrs([]ir.Node{n.Key, n.Value})
+ if n.X.Type().IsArray() {
+ e.flow(ks[1].note(n, "range"), tmp)
+ } else {
+ e.flow(ks[1].deref(n, "range-deref"), tmp)
+ }
+ e.reassigned(ks, n)
+
+ e.block(n.Body)
+ e.loopDepth--
+
+ case ir.OSWITCH:
+ n := n.(*ir.SwitchStmt)
+
+ if guard, ok := n.Tag.(*ir.TypeSwitchGuard); ok {
+ var ks []hole
+ if guard.Tag != nil {
+ for _, cas := range n.Cases {
+ cv := cas.Var
+ k := e.dcl(cv) // type switch variables have no ODCL.
+ if cv.Type().HasPointers() {
+ ks = append(ks, k.dotType(cv.Type(), cas, "switch case"))
+ }
+ }
+ }
+ e.expr(e.teeHole(ks...), n.Tag.(*ir.TypeSwitchGuard).X)
+ } else {
+ e.discard(n.Tag)
+ }
+
+ for _, cas := range n.Cases {
+ e.discards(cas.List)
+ e.block(cas.Body)
+ }
+
+ case ir.OSELECT:
+ n := n.(*ir.SelectStmt)
+ for _, cas := range n.Cases {
+ e.stmt(cas.Comm)
+ e.block(cas.Body)
+ }
+ case ir.ORECV:
+ // TODO(mdempsky): Consider e.discard(n.Left).
+ n := n.(*ir.UnaryExpr)
+ e.exprSkipInit(e.discardHole(), n) // already visited n.Ninit
+ case ir.OSEND:
+ n := n.(*ir.SendStmt)
+ e.discard(n.Chan)
+ e.assignHeap(n.Value, "send", n)
+
+ case ir.OAS:
+ n := n.(*ir.AssignStmt)
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
+ case ir.OASOP:
+ n := n.(*ir.AssignOpStmt)
+ // TODO(mdempsky): Worry about OLSH/ORSH?
+ e.assignList([]ir.Node{n.X}, []ir.Node{n.Y}, "assign", n)
+ case ir.OAS2:
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair", n)
+
+ case ir.OAS2DOTTYPE: // v, ok = x.(type)
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-dot-type", n)
+ case ir.OAS2MAPR: // v, ok = m[k]
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-mapr", n)
+ case ir.OAS2RECV, ir.OSELRECV2: // v, ok = <-ch
+ n := n.(*ir.AssignListStmt)
+ e.assignList(n.Lhs, n.Rhs, "assign-pair-receive", n)
+
+ case ir.OAS2FUNC:
+ n := n.(*ir.AssignListStmt)
+ e.stmts(n.Rhs[0].Init())
+ ks := e.addrs(n.Lhs)
+ e.call(ks, n.Rhs[0])
+ e.reassigned(ks, n)
+ case ir.ORETURN:
+ n := n.(*ir.ReturnStmt)
+ results := e.curfn.Type().Results().FieldSlice()
+ dsts := make([]ir.Node, len(results))
+ for i, res := range results {
+ dsts[i] = res.Nname.(*ir.Name)
+ }
+ e.assignList(dsts, n.Results, "return", n)
+ case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OINLCALL, ir.OCLOSE, ir.OCOPY, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ e.call(nil, n)
+ case ir.OGO, ir.ODEFER:
+ n := n.(*ir.GoDeferStmt)
+ e.goDeferStmt(n)
+
+ case ir.OTAILCALL:
+ n := n.(*ir.TailCallStmt)
+ e.call(nil, n.Call)
+ }
+}
+
+func (e *escape) stmts(l ir.Nodes) {
+ for _, n := range l {
+ e.stmt(n)
+ }
+}
+
+// block is like stmts, but preserves loopDepth.
+func (e *escape) block(l ir.Nodes) {
+ old := e.loopDepth
+ e.stmts(l)
+ e.loopDepth = old
+}
+
+func (e *escape) dcl(n *ir.Name) hole {
+ if n.Curfn != e.curfn || n.IsClosureVar() {
+ base.Fatalf("bad declaration of %v", n)
+ }
+ loc := e.oldLoc(n)
+ loc.loopDepth = e.loopDepth
+ return loc.asHole()
+}
diff --git a/src/cmd/compile/internal/escape/utils.go b/src/cmd/compile/internal/escape/utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c6e9bcbebda07143061a9ddb631e837685db432
--- /dev/null
+++ b/src/cmd/compile/internal/escape/utils.go
@@ -0,0 +1,215 @@
+// 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.
+
+package escape
+
+import (
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+)
+
+func isSliceSelfAssign(dst, src ir.Node) bool {
+ // Detect the following special case.
+ //
+ // func (b *Buffer) Foo() {
+ // n, m := ...
+ // b.buf = b.buf[n:m]
+ // }
+ //
+ // This assignment is a no-op for escape analysis,
+ // it does not store any new pointers into b that were not already there.
+ // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
+ // Here we assume that the statement will not contain calls,
+ // that is, that order will move any calls to init.
+ // Otherwise base ONAME value could change between the moments
+ // when we evaluate it for dst and for src.
+
+ // dst is ONAME dereference.
+ var dstX ir.Node
+ switch dst.Op() {
+ default:
+ return false
+ case ir.ODEREF:
+ dst := dst.(*ir.StarExpr)
+ dstX = dst.X
+ case ir.ODOTPTR:
+ dst := dst.(*ir.SelectorExpr)
+ dstX = dst.X
+ }
+ if dstX.Op() != ir.ONAME {
+ return false
+ }
+ // src is a slice operation.
+ switch src.Op() {
+ case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
+ // OK.
+ case ir.OSLICEARR, ir.OSLICE3ARR:
+ // Since arrays are embedded into containing object,
+ // slice of non-pointer array will introduce a new pointer into b that was not already there
+ // (pointer to b itself). After such assignment, if b contents escape,
+ // b escapes as well. If we ignore such OSLICEARR, we will conclude
+ // that b does not escape when b contents do.
+ //
+ // Pointer to an array is OK since it's not stored inside b directly.
+ // For slicing an array (not pointer to array), there is an implicit OADDR.
+ // We check that to determine non-pointer array slicing.
+ src := src.(*ir.SliceExpr)
+ if src.X.Op() == ir.OADDR {
+ return false
+ }
+ default:
+ return false
+ }
+ // slice is applied to ONAME dereference.
+ var baseX ir.Node
+ switch base := src.(*ir.SliceExpr).X; base.Op() {
+ default:
+ return false
+ case ir.ODEREF:
+ base := base.(*ir.StarExpr)
+ baseX = base.X
+ case ir.ODOTPTR:
+ base := base.(*ir.SelectorExpr)
+ baseX = base.X
+ }
+ if baseX.Op() != ir.ONAME {
+ return false
+ }
+ // dst and src reference the same base ONAME.
+ return dstX.(*ir.Name) == baseX.(*ir.Name)
+}
+
+// isSelfAssign reports whether assignment from src to dst can
+// be ignored by the escape analysis as it's effectively a self-assignment.
+func isSelfAssign(dst, src ir.Node) bool {
+ if isSliceSelfAssign(dst, src) {
+ return true
+ }
+
+ // Detect trivial assignments that assign back to the same object.
+ //
+ // It covers these cases:
+ // val.x = val.y
+ // val.x[i] = val.y[j]
+ // val.x1.x2 = val.x1.y2
+ // ... etc
+ //
+ // These assignments do not change assigned object lifetime.
+
+ if dst == nil || src == nil || dst.Op() != src.Op() {
+ return false
+ }
+
+ // The expression prefix must be both "safe" and identical.
+ switch dst.Op() {
+ case ir.ODOT, ir.ODOTPTR:
+ // Safe trailing accessors that are permitted to differ.
+ dst := dst.(*ir.SelectorExpr)
+ src := src.(*ir.SelectorExpr)
+ return ir.SameSafeExpr(dst.X, src.X)
+ case ir.OINDEX:
+ dst := dst.(*ir.IndexExpr)
+ src := src.(*ir.IndexExpr)
+ if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
+ return false
+ }
+ return ir.SameSafeExpr(dst.X, src.X)
+ default:
+ return false
+ }
+}
+
+// mayAffectMemory reports whether evaluation of n may affect the program's
+// memory state. If the expression can't affect memory state, then it can be
+// safely ignored by the escape analysis.
+func mayAffectMemory(n ir.Node) bool {
+ // We may want to use a list of "memory safe" ops instead of generally
+ // "side-effect free", which would include all calls and other ops that can
+ // allocate or change global state. For now, it's safer to start with the latter.
+ //
+ // We're ignoring things like division by zero, index out of range,
+ // and nil pointer dereference here.
+
+ // TODO(rsc): It seems like it should be possible to replace this with
+ // an ir.Any looking for any op that's not the ones in the case statement.
+ // But that produces changes in the compiled output detected by buildall.
+ switch n.Op() {
+ case ir.ONAME, ir.OLITERAL, ir.ONIL:
+ return false
+
+ case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
+ n := n.(*ir.BinaryExpr)
+ return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
+
+ case ir.OINDEX:
+ n := n.(*ir.IndexExpr)
+ return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
+
+ case ir.OCONVNOP, ir.OCONV:
+ n := n.(*ir.ConvExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ n := n.(*ir.UnaryExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.ODOT, ir.ODOTPTR:
+ n := n.(*ir.SelectorExpr)
+ return mayAffectMemory(n.X)
+
+ case ir.ODEREF:
+ n := n.(*ir.StarExpr)
+ return mayAffectMemory(n.X)
+
+ default:
+ return true
+ }
+}
+
+// HeapAllocReason returns the reason the given Node must be heap
+// allocated, or the empty string if it doesn't.
+func HeapAllocReason(n ir.Node) string {
+ if n == nil || n.Type() == nil {
+ return ""
+ }
+
+ // Parameters are always passed via the stack.
+ if n.Op() == ir.ONAME {
+ n := n.(*ir.Name)
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+ return ""
+ }
+ }
+
+ if n.Type().Size() > ir.MaxStackVarSize {
+ return "too large for stack"
+ }
+
+ if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Size() > ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+
+ if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+ if n.Op() == ir.OMETHVALUE && typecheck.MethodValueType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
+ return "too large for stack"
+ }
+
+ if n.Op() == ir.OMAKESLICE {
+ n := n.(*ir.MakeExpr)
+ r := n.Cap
+ if r == nil {
+ r = n.Len
+ }
+ if !ir.IsSmallIntConst(r) {
+ return "non-constant size"
+ }
+ if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
+ return "too large for stack"
+ }
+ }
+
+ return ""
+}
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 2137f1d1961abf8987383d0cd835459fb37fc2c5..eed438705ade0f20a18451eb8a0a0f5cfc8e24c2 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -5,46 +5,16 @@
package gc
import (
+ "fmt"
+ "go/constant"
+
"cmd/compile/internal/base"
- "cmd/compile/internal/inline"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/bio"
- "fmt"
- "go/constant"
)
-func exportf(bout *bio.Writer, format string, args ...interface{}) {
- fmt.Fprintf(bout, format, args...)
- if base.Debug.Export != 0 {
- fmt.Printf(format, args...)
- }
-}
-
-func dumpexport(bout *bio.Writer) {
- p := &exporter{marked: make(map[*types.Type]bool)}
- for _, n := range typecheck.Target.Exports {
- // Must catch it here rather than Export(), because the type can be
- // not fully set (still TFORW) when Export() is called.
- if n.Type() != nil && n.Type().HasTParam() {
- base.Fatalf("Cannot (yet) export a generic type: %v", n)
- }
- p.markObject(n)
- }
-
- // The linker also looks for the $$ marker - use char after $$ to distinguish format.
- exportf(bout, "\n$$B\n") // indicate binary export format
- off := bout.Offset()
- typecheck.WriteExports(bout.Writer)
- size := bout.Offset() - off
- exportf(bout, "\n$$\n")
-
- if base.Debug.Export != 0 {
- fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, size)
- }
-}
-
func dumpasmhdr() {
b, err := bio.Create(base.Flag.AsmHdr)
if err != nil {
@@ -61,14 +31,14 @@ func dumpasmhdr() {
if t == constant.Float || t == constant.Complex {
break
}
- fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym().Name, n.Val())
+ fmt.Fprintf(b, "#define const_%s %v\n", n.Sym().Name, n.Val())
case ir.OTYPE:
t := n.Type()
if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
break
}
- fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Width))
+ fmt.Fprintf(b, "#define %s__size %d\n", n.Sym().Name, int(t.Size()))
for _, f := range t.Fields().Slice() {
if !f.Sym.IsBlank() {
fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym().Name, f.Sym.Name, int(f.Offset))
@@ -79,83 +49,3 @@ func dumpasmhdr() {
b.Close()
}
-
-type exporter struct {
- marked map[*types.Type]bool // types already seen by markType
-}
-
-// markObject visits a reachable object.
-func (p *exporter) markObject(n ir.Node) {
- if n.Op() == ir.ONAME {
- n := n.(*ir.Name)
- if n.Class == ir.PFUNC {
- inline.Inline_Flood(n, typecheck.Export)
- }
- }
-
- p.markType(n.Type())
-}
-
-// markType recursively visits types reachable from t to identify
-// functions whose inline bodies may be needed.
-func (p *exporter) markType(t *types.Type) {
- if p.marked[t] {
- return
- }
- p.marked[t] = true
-
- // If this is a named type, mark all of its associated
- // methods. Skip interface types because t.Methods contains
- // only their unexpanded method set (i.e., exclusive of
- // interface embeddings), and the switch statement below
- // handles their full method set.
- if t.Sym() != nil && t.Kind() != types.TINTER {
- for _, m := range t.Methods().Slice() {
- if types.IsExported(m.Sym.Name) {
- p.markObject(ir.AsNode(m.Nname))
- }
- }
- }
-
- // Recursively mark any types that can be produced given a
- // value of type t: dereferencing a pointer; indexing or
- // iterating over an array, slice, or map; receiving from a
- // channel; accessing a struct field or interface method; or
- // calling a function.
- //
- // Notably, we don't mark function parameter types, because
- // the user already needs some way to construct values of
- // those types.
- switch t.Kind() {
- case types.TPTR, types.TARRAY, types.TSLICE:
- p.markType(t.Elem())
-
- case types.TCHAN:
- if t.ChanDir().CanRecv() {
- p.markType(t.Elem())
- }
-
- case types.TMAP:
- p.markType(t.Key())
- p.markType(t.Elem())
-
- case types.TSTRUCT:
- for _, f := range t.FieldSlice() {
- if types.IsExported(f.Sym.Name) || f.Embedded != 0 {
- p.markType(f.Type)
- }
- }
-
- case types.TFUNC:
- for _, f := range t.Results().FieldSlice() {
- p.markType(f.Type)
- }
-
- case types.TINTER:
- for _, f := range t.AllMethods().Slice() {
- if types.IsExported(f.Sym.Name) {
- p.markType(f.Type)
- }
- }
- }
-}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index ce50cbb4c2e691ed3054d20894fc1bc722623eda..ed81ef7bc0471458df4387387a3466c1979badf6 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -32,6 +32,7 @@ import (
"log"
"os"
"runtime"
+ "sort"
)
func hidePanic() {
@@ -83,7 +84,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
types.BuiltinPkg.Prefix = "go.builtin" // not go%2ebuiltin
// pseudo-package, accessed by import "unsafe"
- ir.Pkgs.Unsafe = types.NewPkg("unsafe", "unsafe")
+ types.UnsafePkg = types.NewPkg("unsafe", "unsafe")
// Pseudo-package that contains the compiler's builtin
// declarations for package runtime. These are declared in a
@@ -106,7 +107,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
- dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
+ dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
if !base.EnableTrace && base.Flag.LowerT {
log.Fatalf("compiler not built with support for -t")
@@ -148,20 +149,18 @@ func Main(archInit func(*ssagen.ArchInfo)) {
if base.Compiling(base.NoInstrumentPkgs) {
base.Flag.Race = false
base.Flag.MSan = false
+ base.Flag.ASan = false
}
ssagen.Arch.LinkArch.Init(base.Ctxt)
startProfile()
- if base.Flag.Race || base.Flag.MSan {
+ if base.Flag.Race || base.Flag.MSan || base.Flag.ASan {
base.Flag.Cfg.Instrumenting = true
}
if base.Flag.Dwarf {
dwarf.EnableLogging(base.Debug.DwarfInl != 0)
}
if base.Debug.SoftFloat != 0 {
- if buildcfg.Experiment.RegabiArgs {
- log.Fatalf("softfloat mode with GOEXPERIMENT=regabiargs not implemented ")
- }
ssagen.Arch.SoftFloat = true
}
@@ -181,21 +180,40 @@ func Main(archInit func(*ssagen.ArchInfo)) {
typecheck.Target = new(ir.Package)
- typecheck.NeedITab = func(t, iface *types.Type) { reflectdata.ITabAddr(t, iface) }
typecheck.NeedRuntimeType = reflectdata.NeedRuntimeType // TODO(rsc): TypeSym for lock?
base.AutogeneratedPos = makePos(src.NewFileBase("", ""), 1, 0)
typecheck.InitUniverse()
+ typecheck.InitRuntime()
// Parse and typecheck input.
noder.LoadPackage(flag.Args())
dwarfgen.RecordPackageName()
- // Build init task.
- if initTask := pkginit.Task(); initTask != nil {
- typecheck.Export(initTask)
+ // Prepare for backend processing. This must happen before pkginit,
+ // because it generates itabs for initializing global variables.
+ ssagen.InitConfig()
+
+ // Create "init" function for package-scope variable initialization
+ // statements, if any.
+ //
+ // Note: This needs to happen early, before any optimizations. The
+ // Go spec defines a precise order than initialization should be
+ // carried out in, and even mundane optimizations like dead code
+ // removal can skew the results (e.g., #43444).
+ pkginit.MakeInit()
+
+ // Stability quirk: sort top-level declarations, so we're not
+ // sensitive to the order that functions are added. In particular,
+ // the order that noder+typecheck add function closures is very
+ // subtle, and not important to reproduce.
+ if base.Debug.UnifiedQuirks != 0 {
+ s := typecheck.Target.Decls
+ sort.SliceStable(s, func(i, j int) bool {
+ return s[i].Pos().Before(s[j].Pos())
+ })
}
// Eliminate some obviously dead code.
@@ -227,7 +245,13 @@ func Main(archInit func(*ssagen.ArchInfo)) {
base.Timer.Start("fe", "inlining")
if base.Flag.LowerL != 0 {
inline.InlinePackage()
+ // If any new fully-instantiated types were referenced during
+ // inlining, we need to create needed instantiations.
+ if len(typecheck.GetInstTypeList()) > 0 {
+ noder.BuildInstantiations(false)
+ }
}
+ noder.MakeWrappers(typecheck.Target) // must happen after inlining
// Devirtualize.
for _, n := range typecheck.Target.Decls {
@@ -237,6 +261,11 @@ func Main(archInit func(*ssagen.ArchInfo)) {
}
ir.CurFunc = nil
+ // Build init task, if needed.
+ if initTask := pkginit.Task(); initTask != nil {
+ typecheck.Export(initTask)
+ }
+
// Generate ABI wrappers. Must happen before escape analysis
// and doesn't benefit from dead-coding or inlining.
symABIs.GenABIWrappers()
@@ -252,6 +281,11 @@ func Main(archInit func(*ssagen.ArchInfo)) {
base.Timer.Start("fe", "escapes")
escape.Funcs(typecheck.Target.Decls)
+ // TODO(mdempsky): This is a hack. We need a proper, global work
+ // queue for scheduling function compilation so components don't
+ // need to adjust their behavior depending on when they're called.
+ reflectdata.AfterGlobalEscapeAnalysis = true
+
// Collect information for go:nowritebarrierrec
// checking. This must happen before transforming closures during Walk
// We'll do the final check after write barriers are
@@ -260,17 +294,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
ssagen.EnableNoWriteBarrierRecCheck()
}
- // Prepare for SSA compilation.
- // This must be before CompileITabs, because CompileITabs
- // can trigger function compilation.
- typecheck.InitRuntime()
- ssagen.InitConfig()
-
- // Just before compilation, compile itabs found on
- // the right side of OCONVIFACE so that methods
- // can be de-virtualized during compilation.
ir.CurFunc = nil
- reflectdata.CompileITabs()
// Compile top level functions.
// Don't use range--walk can add functions to Target.Decls.
@@ -278,6 +302,10 @@ func Main(archInit func(*ssagen.ArchInfo)) {
fcount := int64(0)
for i := 0; i < len(typecheck.Target.Decls); i++ {
if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+ // Don't try compiling dead hidden closure.
+ if fn.IsDeadcodeClosure() {
+ continue
+ }
enqueueFunc(fn)
fcount++
}
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 474d718525f5b80a3adc9c47c3aad8e879830e48..dcb54047f1fce75b577f8c32c40f0e19f88464e4 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -7,6 +7,7 @@ package gc
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/noder"
"cmd/compile/internal/objw"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/staticdata"
@@ -103,7 +104,7 @@ func finishArchiveEntry(bout *bio.Writer, start int64, name string) {
func dumpCompilerObj(bout *bio.Writer) {
printObjHeader(bout)
- dumpexport(bout)
+ noder.WriteExports(bout)
}
func dumpdata() {
@@ -116,7 +117,7 @@ func dumpdata() {
addsignats(typecheck.Target.Externs)
reflectdata.WriteRuntimeTypes()
reflectdata.WriteTabs()
- numPTabs, numITabs := reflectdata.CountTabs()
+ numPTabs := reflectdata.CountPTabs()
reflectdata.WriteImportStrings()
reflectdata.WriteBasicTypes()
dumpembeds()
@@ -157,13 +158,10 @@ func dumpdata() {
if numExports != len(typecheck.Target.Exports) {
base.Fatalf("Target.Exports changed after compile functions loop")
}
- newNumPTabs, newNumITabs := reflectdata.CountTabs()
+ newNumPTabs := reflectdata.CountPTabs()
if newNumPTabs != numPTabs {
base.Fatalf("ptabs changed after compile functions loop")
}
- if newNumITabs != numITabs {
- base.Fatalf("itabs changed after compile functions loop")
- }
}
func dumpLinkerObj(bout *bio.Writer) {
@@ -251,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 {
@@ -261,7 +258,10 @@ func addGCLocals() {
if x := fn.ArgInfo; x != nil {
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
x.Set(obj.AttrStatic, true)
- x.Set(obj.AttrContentAddressable, true)
+ }
+ if x := fn.ArgLiveInfo; x != nil {
+ objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
+ x.Set(obj.AttrStatic, true)
}
}
}
@@ -276,7 +276,7 @@ func ggloblnod(nam *ir.Name) {
if nam.Type() != nil && !nam.Type().HasPointers() {
flags |= obj.NOPTR
}
- base.Ctxt.Globl(s, nam.Type().Width, flags)
+ base.Ctxt.Globl(s, nam.Type().Size(), flags)
if nam.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
}
diff --git a/src/cmd/compile/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
index 4baddbc029a8d2dc587650a1b99243bc20b9b619..56fd137de2374b96b9788417b809fec1e0a91388 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/exportdata.go b/src/cmd/compile/internal/importer/exportdata.go
index 3925a64314ea0d8d160afcc5fbc3719dbae58338..6a672be9c1bac15a5fa4cc89e69b67c21e95f4f1 100644
--- a/src/cmd/compile/internal/importer/exportdata.go
+++ b/src/cmd/compile/internal/importer/exportdata.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2011 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.
diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go
index feb18cf2c9ad78c50f2287d723246991e678e8bd..ff40be65bbeceebcbb67d3d07c4b87631ea2c904 100644
--- a/src/cmd/compile/internal/importer/gcimporter.go
+++ b/src/cmd/compile/internal/importer/gcimporter.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2011 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.
@@ -156,7 +155,7 @@ func Import(packages map[string]*types2.Package, path, srcDir string, lookup fun
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
if len(data) > 0 && data[0] == 'i' {
- _, pkg, err = iImportData(packages, data[1:], id)
+ pkg, err = ImportData(packages, string(data[1:]), id)
} else {
err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
}
diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go
index 7fb8fed59cf9be1cd00471a1d0854e89d4ed1148..5d80db244b9bf3c31ae93ddac34b24f37f3ac901 100644
--- a/src/cmd/compile/internal/importer/gcimporter_test.go
+++ b/src/cmd/compile/internal/importer/gcimporter_test.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2011 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.
@@ -9,8 +8,8 @@ import (
"bytes"
"cmd/compile/internal/types2"
"fmt"
+ "internal/goexperiment"
"internal/testenv"
- "io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -64,7 +63,7 @@ const maxTime = 30 * time.Second
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
- list, err := ioutil.ReadDir(dirname)
+ list, err := os.ReadDir(dirname)
if err != nil {
t.Fatalf("testDir(%s): %s", dirname, err)
}
@@ -92,7 +91,7 @@ func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
}
func mktmpdir(t *testing.T) string {
- tmpdir, err := ioutil.TempDir("", "gcimporter_test")
+ tmpdir, err := os.MkdirTemp("", "gcimporter_test")
if err != nil {
t.Fatal("mktmpdir:", err)
}
@@ -109,25 +108,29 @@ func TestImportTestdata(t *testing.T) {
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
}
- tmpdir := mktmpdir(t)
- defer os.RemoveAll(tmpdir)
+ testfiles := map[string][]string{
+ "exports.go": {"go/ast", "go/token"},
+ }
+ if !goexperiment.Unified {
+ testfiles["generics.go"] = nil
+ }
- compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
-
- if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
- // The package's Imports list must include all packages
- // explicitly imported by exports.go, plus all packages
- // referenced indirectly via exported objects in exports.go.
- // With the textual export format, the list may also include
- // additional packages that are not strictly required for
- // import processing alone (they are exported to err "on
- // the safe side").
- // TODO(gri) update the want list to be precise, now that
- // the textual export data is gone.
- got := fmt.Sprint(pkg.Imports())
- for _, want := range []string{"go/ast", "go/token"} {
- if !strings.Contains(got, want) {
- t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ for testfile, wantImports := range testfiles {
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
+ path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
+
+ if pkg := testPath(t, path, tmpdir); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by testfile, plus all packages
+ // referenced indirectly via exported objects in testfile.
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range wantImports {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
}
}
}
@@ -142,7 +145,7 @@ func TestVersionHandling(t *testing.T) {
}
const dir = "./testdata/versions"
- list, err := ioutil.ReadDir(dir)
+ list, err := os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
@@ -195,7 +198,7 @@ func TestVersionHandling(t *testing.T) {
// create file with corrupted export data
// 1) read file
- data, err := ioutil.ReadFile(filepath.Join(dir, name))
+ data, err := os.ReadFile(filepath.Join(dir, name))
if err != nil {
t.Fatal(err)
}
@@ -212,7 +215,7 @@ func TestVersionHandling(t *testing.T) {
// 4) write the file
pkgpath += "_corrupted"
filename := filepath.Join(corruptdir, pkgpath) + ".a"
- ioutil.WriteFile(filename, data, 0666)
+ os.WriteFile(filename, data, 0666)
// test that importing the corrupted file results in an error
_, err = Import(make(map[string]*types2.Package), pkgpath, corruptdir, nil)
@@ -255,14 +258,13 @@ var importedObjectTests = []struct {
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
// interfaces
- {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
+ {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
- // go/types.Type has grown much larger - excluded for now
- // {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
+ {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
}
func TestImportedTypes(t *testing.T) {
@@ -457,17 +459,17 @@ func TestIssue13898(t *testing.T) {
t.Fatal("go/types not found")
}
- // look for go/types2.Object type
+ // look for go/types.Object type
obj := lookupObj(t, goTypesPkg.Scope(), "Object")
typ, ok := obj.Type().(*types2.Named)
if !ok {
- t.Fatalf("go/types2.Object type is %v; wanted named type", typ)
+ t.Fatalf("go/types.Object type is %v; wanted named type", typ)
}
- // lookup go/types2.Object.Pkg method
+ // lookup go/types.Object.Pkg method
m, index, indirect := types2.LookupFieldOrMethod(typ, false, nil, "Pkg")
if m == nil {
- t.Fatalf("go/types2.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
+ t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
}
// the method must belong to go/types
diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go
index 8ab0b7b98961fdefe1e4dc605a65a029979c79b1..7c51d3b16fc4b669bd4757e1257d3fef713716bb 100644
--- a/src/cmd/compile/internal/importer/iimport.go
+++ b/src/cmd/compile/internal/importer/iimport.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// 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.
@@ -9,7 +8,6 @@
package importer
import (
- "bytes"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types2"
"encoding/binary"
@@ -19,10 +17,11 @@ import (
"io"
"math/big"
"sort"
+ "strings"
)
type intReader struct {
- *bytes.Reader
+ *strings.Reader
path string
}
@@ -42,6 +41,21 @@ func (r *intReader) uint64() uint64 {
return i
}
+// Keep this in sync with constants in iexport.go.
+const (
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGenerics = 2
+ iexportVersionGo1_18 = 2
+
+ iexportVersionCurrent = 2
+)
+
+type ident struct {
+ pkg string
+ name string
+}
+
const predeclReserved = 32
type itag uint64
@@ -57,6 +71,9 @@ const (
signatureType
structType
interfaceType
+ typeParamType
+ instanceType
+ unionType
)
const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
@@ -65,8 +82,8 @@ const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
// and returns the number of bytes consumed and a reference to the package.
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
-func iImportData(imports map[string]*types2.Package, data []byte, path string) (_ int, pkg *types2.Package, err error) {
- const currentVersion = 1
+func ImportData(imports map[string]*types2.Package, data, path string) (pkg *types2.Package, err error) {
+ const currentVersion = iexportVersionCurrent
version := int64(-1)
defer func() {
if e := recover(); e != nil {
@@ -78,11 +95,11 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
}
}()
- r := &intReader{bytes.NewReader(data), path}
+ r := &intReader{strings.NewReader(data), path}
version = int64(r.uint64())
switch version {
- case currentVersion, 0:
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
errorf("unknown iexport format version %d", version)
}
@@ -96,16 +113,20 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
r.Seek(sLen+dLen, io_SeekCurrent)
p := iimporter{
- ipath: path,
- version: int(version),
+ exportVersion: version,
+ ipath: path,
+ version: int(version),
- stringData: stringData,
- stringCache: make(map[uint64]string),
- pkgCache: make(map[uint64]*types2.Package),
+ stringData: stringData,
+ pkgCache: make(map[uint64]*types2.Package),
+ posBaseCache: make(map[uint64]*syntax.PosBase),
declData: declData,
pkgIndex: make(map[*types2.Package]map[string]uint64),
typCache: make(map[uint64]types2.Type),
+ // Separate map for typeparams, keyed by their package and unique
+ // name (name with subscript).
+ tparamIndex: make(map[ident]types2.Type),
}
for i, pt := range predeclared {
@@ -117,17 +138,22 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff)
pkgName := p.stringAt(r.uint64())
- _ = r.uint64() // package height; unused by go/types
+ pkgHeight := int(r.uint64())
if pkgPath == "" {
pkgPath = path
}
pkg := imports[pkgPath]
if pkg == nil {
- pkg = types2.NewPackage(pkgPath, pkgName)
+ pkg = types2.NewPackageHeight(pkgPath, pkgName, pkgHeight)
imports[pkgPath] = pkg
- } else if pkg.Name() != pkgName {
- errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
+ } else {
+ if pkg.Name() != pkgName {
+ errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
+ }
+ if pkg.Height() != pkgHeight {
+ errorf("conflicting heights %v and %v for package %q", pkg.Height(), pkgHeight, path)
+ }
}
p.pkgCache[pkgPathOff] = pkg
@@ -153,10 +179,6 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
p.doDecl(localpkg, name)
}
- for _, typ := range p.interfaceList {
- typ.Complete()
- }
-
// record all referenced packages as imports
list := append(([]*types2.Package)(nil), pkgList[1:]...)
sort.Sort(byPath(list))
@@ -165,21 +187,22 @@ func iImportData(imports map[string]*types2.Package, data []byte, path string) (
// package was imported completely and without errors
localpkg.MarkComplete()
- consumed, _ := r.Seek(0, io_SeekCurrent)
- return int(consumed), localpkg, nil
+ return localpkg, nil
}
type iimporter struct {
- ipath string
- version int
+ exportVersion int64
+ ipath string
+ version int
- stringData []byte
- stringCache map[uint64]string
- pkgCache map[uint64]*types2.Package
+ stringData string
+ pkgCache map[uint64]*types2.Package
+ posBaseCache map[uint64]*syntax.PosBase
- declData []byte
- pkgIndex map[*types2.Package]map[string]uint64
- typCache map[uint64]types2.Type
+ declData string
+ pkgIndex map[*types2.Package]map[string]uint64
+ typCache map[uint64]types2.Type
+ tparamIndex map[ident]types2.Type
interfaceList []*types2.Interface
}
@@ -199,24 +222,21 @@ func (p *iimporter) doDecl(pkg *types2.Package, name string) {
// Reader.Reset is not available in Go 1.4.
// Use bytes.NewReader for now.
// r.declReader.Reset(p.declData[off:])
- r.declReader = *bytes.NewReader(p.declData[off:])
+ r.declReader = *strings.NewReader(p.declData[off:])
r.obj(name)
}
func (p *iimporter) stringAt(off uint64) string {
- if s, ok := p.stringCache[off]; ok {
- return s
- }
+ var x [binary.MaxVarintLen64]byte
+ n := copy(x[:], p.stringData[off:])
- slen, n := binary.Uvarint(p.stringData[off:])
+ slen, n := binary.Uvarint(x[:n])
if n <= 0 {
errorf("varint failed")
}
spos := off + uint64(n)
- s := string(p.stringData[spos : spos+slen])
- p.stringCache[off] = s
- return s
+ return p.stringData[spos : spos+slen]
}
func (p *iimporter) pkgAt(off uint64) *types2.Package {
@@ -228,8 +248,18 @@ func (p *iimporter) pkgAt(off uint64) *types2.Package {
return nil
}
+func (p *iimporter) posBaseAt(off uint64) *syntax.PosBase {
+ if posBase, ok := p.posBaseCache[off]; ok {
+ return posBase
+ }
+ filename := p.stringAt(off)
+ posBase := syntax.NewTrimmedFileBase(filename, true)
+ p.posBaseCache[off] = posBase
+ return posBase
+}
+
func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
- if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
+ if t, ok := p.typCache[off]; ok && canReuse(base, t) {
return t
}
@@ -241,22 +271,40 @@ func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
// Reader.Reset is not available in Go 1.4.
// Use bytes.NewReader for now.
// r.declReader.Reset(p.declData[off-predeclReserved:])
- r.declReader = *bytes.NewReader(p.declData[off-predeclReserved:])
+ r.declReader = *strings.NewReader(p.declData[off-predeclReserved:])
t := r.doType(base)
- if base == nil || !isInterface(t) {
+ if canReuse(base, t) {
p.typCache[off] = t
}
return t
}
+// canReuse reports whether the type rhs on the RHS of the declaration for def
+// may be re-used.
+//
+// Specifically, if def is non-nil and rhs is an interface type with methods, it
+// may not be re-used because we have a convention of setting the receiver type
+// for interface methods to def.
+func canReuse(def *types2.Named, rhs types2.Type) bool {
+ if def == nil {
+ return true
+ }
+ iface, _ := rhs.(*types2.Interface)
+ if iface == nil {
+ return true
+ }
+ // Don't use iface.Empty() here as iface may not be complete.
+ return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
+}
+
type importReader struct {
- p *iimporter
- declReader bytes.Reader
- currPkg *types2.Package
- prevFile string
- prevLine int64
- prevColumn int64
+ p *iimporter
+ declReader strings.Reader
+ currPkg *types2.Package
+ prevPosBase *syntax.PosBase
+ prevLine int64
+ prevColumn int64
}
func (r *importReader) obj(name string) {
@@ -274,17 +322,26 @@ func (r *importReader) obj(name string) {
r.declare(types2.NewConst(pos, r.currPkg, name, typ, val))
- case 'F':
- sig := r.signature(nil)
-
+ case 'F', 'G':
+ var tparams []*types2.TypeParam
+ if tag == 'G' {
+ tparams = r.tparamList()
+ }
+ sig := r.signature(nil, nil, tparams)
r.declare(types2.NewFunc(pos, r.currPkg, name, sig))
- case 'T':
+ case 'T', 'U':
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types2.NewTypeName(pos, r.currPkg, name, nil)
named := types2.NewNamed(obj, nil, nil)
+ // Declare obj before calling r.tparamList, so the new type name is recognized
+ // if used in the constraint of one of its own typeparams (see #48280).
r.declare(obj)
+ if tag == 'U' {
+ tparams := r.tparamList()
+ named.SetTypeParams(tparams)
+ }
underlying := r.p.typAt(r.uint64(), named).Underlying()
named.SetUnderlying(underlying)
@@ -294,12 +351,57 @@ func (r *importReader) obj(name string) {
mpos := r.pos()
mname := r.ident()
recv := r.param()
- msig := r.signature(recv)
+
+ // If the receiver has any targs, set those as the
+ // rparams of the method (since those are the
+ // typeparams being used in the method sig/body).
+ targs := baseType(recv.Type()).TypeArgs()
+ var rparams []*types2.TypeParam
+ if targs.Len() > 0 {
+ rparams = make([]*types2.TypeParam, targs.Len())
+ for i := range rparams {
+ rparams[i], _ = targs.At(i).(*types2.TypeParam)
+ }
+ }
+ msig := r.signature(recv, rparams, nil)
named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig))
}
}
+ case 'P':
+ // We need to "declare" a typeparam in order to have a name that
+ // can be referenced recursively (if needed) in the type param's
+ // bound.
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ // Remove the "path" from the type param name that makes it unique
+ ix := strings.LastIndex(name, ".")
+ if ix < 0 {
+ errorf("missing path for type param")
+ }
+ tn := types2.NewTypeName(pos, r.currPkg, name[ix+1:], nil)
+ t := types2.NewTypeParam(tn, nil)
+ // To handle recursive references to the typeparam within its
+ // bound, save the partial type in tparamIndex before reading the bounds.
+ id := ident{r.currPkg.Name(), name}
+ r.p.tparamIndex[id] = t
+
+ 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()
@@ -316,6 +418,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:
@@ -439,12 +545,11 @@ func (r *importReader) pos() syntax.Pos {
r.posv0()
}
- if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
+ if (r.prevPosBase == nil || r.prevPosBase.Filename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
return syntax.Pos{}
}
- // TODO(gri) fix this
- // return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
- return syntax.Pos{}
+
+ return syntax.MakePos(r.prevPosBase, uint(r.prevLine), uint(r.prevColumn))
}
func (r *importReader) posv0() {
@@ -454,7 +559,7 @@ func (r *importReader) posv0() {
} else if l := r.int64(); l == -1 {
r.prevLine += deltaNewFile
} else {
- r.prevFile = r.string()
+ r.prevPosBase = r.posBase()
r.prevLine = l
}
}
@@ -466,7 +571,7 @@ func (r *importReader) posv1() {
delta = r.int64()
r.prevLine += delta >> 1
if delta&1 != 0 {
- r.prevFile = r.string()
+ r.prevPosBase = r.posBase()
}
}
}
@@ -480,8 +585,9 @@ func isInterface(t types2.Type) bool {
return ok
}
-func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) }
-func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) }
+func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+func (r *importReader) posBase() *syntax.PosBase { return r.p.posBaseAt(r.uint64()) }
func (r *importReader) doType(base *types2.Named) types2.Type {
switch k := r.kind(); k {
@@ -507,7 +613,7 @@ func (r *importReader) doType(base *types2.Named) types2.Type {
return types2.NewMap(r.typ(), r.typ())
case signatureType:
r.currPkg = r.pkg()
- return r.signature(nil)
+ return r.signature(nil, nil, nil)
case structType:
r.currPkg = r.pkg()
@@ -547,13 +653,56 @@ func (r *importReader) doType(base *types2.Named) types2.Type {
recv = types2.NewVar(syntax.Pos{}, r.currPkg, "", base)
}
- msig := r.signature(recv)
+ msig := r.signature(recv, nil, nil)
methods[i] = types2.NewFunc(mpos, r.currPkg, mname, msig)
}
typ := types2.NewInterfaceType(methods, embeddeds)
r.p.interfaceList = append(r.p.interfaceList, typ)
return typ
+
+ case typeParamType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ pkg, name := r.qualifiedIdent()
+ id := ident{pkg.Name(), name}
+ if t, ok := r.p.tparamIndex[id]; ok {
+ // We're already in the process of importing this typeparam.
+ return t
+ }
+ // Otherwise, import the definition of the typeparam now.
+ r.p.doDecl(pkg, name)
+ return r.p.tparamIndex[id]
+
+ case instanceType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ // pos does not matter for instances: they are positioned on the original
+ // type.
+ _ = r.pos()
+ len := r.uint64()
+ targs := make([]types2.Type, len)
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+ baseType := r.typ()
+ // The imported instantiated type doesn't include any methods, so
+ // we must always use the methods of the base (orig) type.
+ // TODO provide a non-nil *Context
+ t, _ := types2.Instantiate(nil, baseType, targs, false)
+ return t
+
+ case unionType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ terms := make([]*types2.Term, r.uint64())
+ for i := range terms {
+ terms[i] = types2.NewTerm(r.bool(), r.typ())
+ }
+ return types2.NewUnion(terms)
}
}
@@ -561,11 +710,23 @@ func (r *importReader) kind() itag {
return itag(r.uint64())
}
-func (r *importReader) signature(recv *types2.Var) *types2.Signature {
+func (r *importReader) signature(recv *types2.Var, rparams, tparams []*types2.TypeParam) *types2.Signature {
params := r.paramList()
results := r.paramList()
variadic := params.Len() > 0 && r.bool()
- return types2.NewSignature(recv, params, results, variadic)
+ return types2.NewSignatureType(recv, rparams, tparams, params, results, variadic)
+}
+
+func (r *importReader) tparamList() []*types2.TypeParam {
+ n := r.uint64()
+ if n == 0 {
+ return nil
+ }
+ xs := make([]*types2.TypeParam, n)
+ for i := range xs {
+ xs[i] = r.typ().(*types2.TypeParam)
+ }
+ return xs
}
func (r *importReader) paramList() *types2.Tuple {
@@ -610,3 +771,13 @@ func (r *importReader) byte() byte {
}
return x
}
+
+func baseType(typ types2.Type) *types2.Named {
+ // pointer receivers are never types2.Named types
+ if p, _ := typ.(*types2.Pointer); p != nil {
+ typ = p.Elem()
+ }
+ // receiver base types are always (possibly generic) types2.Named types
+ n, _ := typ.(*types2.Named)
+ return n
+}
diff --git a/src/cmd/compile/internal/importer/support.go b/src/cmd/compile/internal/importer/support.go
index 40b9c7c9583fbff277c7d5a2237bdc82596384de..9377d997796190b402536a25f849c75717516fe5 100644
--- a/src/cmd/compile/internal/importer/support.go
+++ b/src/cmd/compile/internal/importer/support.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2015 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.
@@ -119,7 +118,14 @@ var predeclared = []types2.Type{
types2.Typ[types2.Invalid], // only appears in packages with errors
// used internally by gc; never used by this package or in .a files
+ // not to be confused with the universe any
anyType{},
+
+ // comparable
+ types2.Universe.Lookup("comparable").Type(),
+
+ // any
+ types2.Universe.Lookup("any").Type(),
}
type anyType struct{}
diff --git a/src/cmd/compile/internal/importer/testdata/a.go b/src/cmd/compile/internal/importer/testdata/a.go
index 06dafee98c30f82e459be13f0c0ced7e535d57af..56e4292cda9f3a0892677a20722c833165171290 100644
--- a/src/cmd/compile/internal/importer/testdata/a.go
+++ b/src/cmd/compile/internal/importer/testdata/a.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2016 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.
diff --git a/src/cmd/compile/internal/importer/testdata/b.go b/src/cmd/compile/internal/importer/testdata/b.go
index a601dbccc5a0e9b76ac8bfd752e2fb7e5b6397cd..419667820078e47d4744ee7c809a8f63516c8a42 100644
--- a/src/cmd/compile/internal/importer/testdata/b.go
+++ b/src/cmd/compile/internal/importer/testdata/b.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2016 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.
diff --git a/src/cmd/compile/internal/importer/testdata/exports.go b/src/cmd/compile/internal/importer/testdata/exports.go
index 2a720fd2c172541bbbe32976791534c06a2ecbdb..91598c03e35c0337edd1f51b4b399135b32be8dd 100644
--- a/src/cmd/compile/internal/importer/testdata/exports.go
+++ b/src/cmd/compile/internal/importer/testdata/exports.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2011 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.
@@ -16,14 +15,17 @@ const init1 = 0
func init() {}
const (
- C0 int = 0
- C1 = 3.14159265
- C2 = 2.718281828i
- C3 = -123.456e-789
- C4 = +123.456e+789
- C5 = 1234i
- C6 = "foo\n"
- C7 = `bar\n`
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456e+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+ C8 = 42
+ C9 int = 42
+ C10 float64 = 42
)
type (
diff --git a/src/cmd/compile/internal/importer/testdata/generics.go b/src/cmd/compile/internal/importer/testdata/generics.go
new file mode 100644
index 0000000000000000000000000000000000000000..00bf04000fa06c717f8bad1ab238dd83a8047ebe
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/generics.go
@@ -0,0 +1,29 @@
+// 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 used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package generics
+
+type Any any
+
+var x any
+
+type T[A, B any] struct {
+ Left A
+ Right B
+}
+
+var X T[int, string] = T[int, string]{1, "hi"}
+
+func ToInt[P interface{ ~int }](p P) int { return int(p) }
+
+var IntID = ToInt[int]
+
+type G[C comparable] int
+
+func ImplicitFunc[T ~int]() {}
+
+type ImplicitType[T ~int] int
diff --git a/src/cmd/compile/internal/importer/testdata/issue15920.go b/src/cmd/compile/internal/importer/testdata/issue15920.go
index b40202616275836a6d5e43eec0f8d5cca29d1f74..c70f7d8267b2f9209cf5f52a0ca7fe1bcc303649 100644
--- a/src/cmd/compile/internal/importer/testdata/issue15920.go
+++ b/src/cmd/compile/internal/importer/testdata/issue15920.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2016 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.
diff --git a/src/cmd/compile/internal/importer/testdata/issue20046.go b/src/cmd/compile/internal/importer/testdata/issue20046.go
index e412f353ad2e6d7cbca580ba24adb67acfe4c51e..c63ee821c959dafda6799ccc8df77347221ad46a 100644
--- a/src/cmd/compile/internal/importer/testdata/issue20046.go
+++ b/src/cmd/compile/internal/importer/testdata/issue20046.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// 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.
diff --git a/src/cmd/compile/internal/importer/testdata/issue25301.go b/src/cmd/compile/internal/importer/testdata/issue25301.go
index a9dc1d7f083b2eae0353e51fff547b2776fc4476..e3dc98b4e1f9acdc9801c6a66e9305206f943cb5 100644
--- a/src/cmd/compile/internal/importer/testdata/issue25301.go
+++ b/src/cmd/compile/internal/importer/testdata/issue25301.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// 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.
diff --git a/src/cmd/compile/internal/importer/testdata/issue25596.go b/src/cmd/compile/internal/importer/testdata/issue25596.go
index 95bef42280e9b4283bcec8bb32993ffc4999da87..8923373e5fa44d57d25ce1b4ccdc3a480360c74f 100644
--- a/src/cmd/compile/internal/importer/testdata/issue25596.go
+++ b/src/cmd/compile/internal/importer/testdata/issue25596.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// 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.
diff --git a/src/cmd/compile/internal/importer/testdata/p.go b/src/cmd/compile/internal/importer/testdata/p.go
index 34a20eaa1451eca59573898adcad40c3a6a58e26..9e2e7057653725fc8ce963b5768828b3d9a88b42 100644
--- a/src/cmd/compile/internal/importer/testdata/p.go
+++ b/src/cmd/compile/internal/importer/testdata/p.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2016 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.
diff --git a/src/cmd/compile/internal/importer/testdata/versions/test.go b/src/cmd/compile/internal/importer/testdata/versions/test.go
index 2f8eb5ced047c4ae88aadad7a34b688a2ad838a8..227fc092519212f30793e1266fe619858ac28bef 100644
--- a/src/cmd/compile/internal/importer/testdata/versions/test.go
+++ b/src/cmd/compile/internal/importer/testdata/versions/test.go
@@ -1,4 +1,3 @@
-// UNREVIEWED
// Copyright 2016 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.
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index d6b4ced4e157cd46a2dbbcc814635453f32e198e..716a7fbcd93a25900e204efa575a3a532aabd6fd 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -179,6 +179,8 @@ func CanInline(fn *ir.Func) {
Cost: inlineMaxBudget - visitor.budget,
Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor),
Body: inlcopylist(fn.Body),
+
+ CanDelayResults: canDelayResults(fn),
}
if base.Flag.LowerM > 1 {
@@ -191,60 +193,36 @@ func CanInline(fn *ir.Func) {
}
}
-// Inline_Flood marks n's inline body for export and recursively ensures
-// all called functions are marked too.
-func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) {
- if n == nil {
- return
- }
- if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
- base.Fatalf("Inline_Flood: unexpected %v, %v, %v", n, n.Op(), n.Class)
- }
- fn := n.Func
- if fn == nil {
- base.Fatalf("Inline_Flood: missing Func on %v", n)
- }
- if fn.Inl == nil {
- return
- }
-
- if fn.ExportInline() {
- return
- }
- fn.SetExportInline(true)
-
- typecheck.ImportedBody(fn)
-
- var doFlood func(n ir.Node)
- doFlood = func(n ir.Node) {
- switch n.Op() {
- case ir.OMETHEXPR, ir.ODOTMETH:
- Inline_Flood(ir.MethodExprName(n), exportsym)
+// canDelayResults reports whether inlined calls to fn can delay
+// declaring the result parameter until the "return" statement.
+func canDelayResults(fn *ir.Func) bool {
+ // We can delay declaring+initializing result parameters if:
+ // (1) there's exactly one "return" statement in the inlined function;
+ // (2) it's not an empty return statement (#44355); and
+ // (3) the result parameters aren't named.
- case ir.ONAME:
- n := n.(*ir.Name)
- switch n.Class {
- case ir.PFUNC:
- Inline_Flood(n, exportsym)
- exportsym(n)
- case ir.PEXTERN:
- exportsym(n)
+ nreturns := 0
+ ir.VisitList(fn.Body, func(n ir.Node) {
+ if n, ok := n.(*ir.ReturnStmt); ok {
+ nreturns++
+ if len(n.Results) == 0 {
+ nreturns++ // empty return statement (case 2)
}
+ }
+ })
- case ir.OCALLPART:
- // Okay, because we don't yet inline indirect
- // calls to method values.
- case ir.OCLOSURE:
- // VisitList doesn't visit closure bodies, so force a
- // recursive call to VisitList on the body of the closure.
- ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood)
+ if nreturns != 1 {
+ return false // not exactly one return statement (case 1)
+ }
+
+ // temporaries for return values.
+ for _, param := range fn.Type().Results().FieldSlice() {
+ if sym := types.OrigSym(param.Sym); sym != nil && !sym.IsBlank() {
+ return false // found a named result parameter (case 3)
}
}
- // Recursively identify all referenced functions for
- // reexport. We want to include even non-called functions,
- // because after inlining they might be callable.
- ir.VisitList(ir.Nodes(fn.Inl.Body), doFlood)
+ return true
}
// hairyVisitor visits a function body to determine its inlining
@@ -295,13 +273,43 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
}
}
}
+ if n.X.Op() == ir.OMETHEXPR {
+ if meth := ir.MethodExprName(n.X); meth != nil {
+ if fn := meth.Func; fn != nil {
+ s := fn.Sym()
+ var cheap bool
+ if types.IsRuntimePkg(s.Pkg) && s.Name == "heapBits.nextArena" {
+ // Special case: explicitly allow mid-stack inlining of
+ // runtime.heapBits.next even though it calls slow-path
+ // runtime.heapBits.nextArena.
+ cheap = true
+ }
+ // Special case: on architectures that can do unaligned loads,
+ // explicitly mark encoding/binary methods as cheap,
+ // because in practice they are, even though our inlining
+ // budgeting system does not see that. See issue 42958.
+ if base.Ctxt.Arch.CanMergeLoads && s.Pkg.Path == "encoding/binary" {
+ switch s.Name {
+ case "littleEndian.Uint64", "littleEndian.Uint32", "littleEndian.Uint16",
+ "bigEndian.Uint64", "bigEndian.Uint32", "bigEndian.Uint16",
+ "littleEndian.PutUint64", "littleEndian.PutUint32", "littleEndian.PutUint16",
+ "bigEndian.PutUint64", "bigEndian.PutUint32", "bigEndian.PutUint16":
+ cheap = true
+ }
+ }
+ if cheap {
+ break // treat like any other node, that is, cost of 1
+ }
+ }
+ }
+ }
if ir.IsIntrinsicCall(n) {
// Treat like any other node.
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
}
@@ -309,28 +317,8 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
// Call cost for non-leaf inlining.
v.budget -= v.extraCallCost
- // Call is okay if inlinable and we have the budget for the body.
case ir.OCALLMETH:
- n := n.(*ir.CallExpr)
- t := n.X.Type()
- if t == nil {
- base.Fatalf("no function type for [%p] %+v\n", n.X, n.X)
- }
- fn := ir.MethodExprName(n.X).Func
- if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
- // Special case: explicitly allow
- // mid-stack inlining of
- // runtime.heapBits.next even though
- // it calls slow-path
- // runtime.heapBits.nextArena.
- break
- }
- if fn.Inl != nil {
- v.budget -= fn.Inl.Cost
- break
- }
- // Call cost for non-leaf inlining.
- v.budget -= v.extraCallCost
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
// Things that are too hairy, irrespective of the budget
case ir.OCALL, ir.OCALLINTER:
@@ -370,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
@@ -402,35 +389,18 @@ 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) {
// This if and the condition cost nothing.
- // TODO(rsc): It seems strange that we visit the dead branch.
- return doList(n.Init(), v.do) ||
- doList(n.Body, v.do) ||
- doList(n.Else, v.do)
+ if doList(n.Init(), v.do) {
+ return true
+ }
+ if ir.BoolVal(n.Cond) {
+ return doList(n.Body, v.do)
+ } else {
+ return doList(n.Else, v.do)
+ }
}
case ir.ONAME:
@@ -445,7 +415,7 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
// and don't charge for the OBLOCK itself. The ++ undoes the -- below.
v.budget++
- case ir.OCALLPART, ir.OSLICELIT:
+ case ir.OMETHVALUE, ir.OSLICELIT:
v.budget-- // Hack for toolstash -cmp.
case ir.OMETHEXPR:
@@ -499,9 +469,6 @@ func inlcopy(n ir.Node) ir.Node {
// x.Func.Body for iexport and local inlining.
oldfn := x.Func
newfn := ir.NewFunc(oldfn.Pos())
- if oldfn.ClosureCalled() {
- newfn.SetClosureCalled(true)
- }
m.(*ir.ClosureExpr).Func = newfn
newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
// XXX OK to share fn.Type() ??
@@ -544,37 +511,6 @@ func InlineCalls(fn *ir.Func) {
ir.CurFunc = savefn
}
-// Turn an OINLCALL into a statement.
-func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
- n := ir.NewBlockStmt(inlcall.Pos(), nil)
- n.List = inlcall.Init()
- n.List.Append(inlcall.Body.Take()...)
- return n
-}
-
-// Turn an OINLCALL into a single valued expression.
-// The result of inlconv2expr MUST be assigned back to n, e.g.
-// n.Left = inlconv2expr(n.Left)
-func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
- r := n.ReturnVars[0]
- return ir.InitExpr(append(n.Init(), n.Body...), r)
-}
-
-// Turn the rlist (with the return values) of the OINLCALL in
-// n into an expression list lumping the ninit and body
-// containing the inlined statements on the first list element so
-// order will be preserved. Used in return, oas2func and call
-// statements.
-func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
- if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 {
- base.Fatalf("inlconv2list %+v\n", n)
- }
-
- s := n.ReturnVars
- s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0])
- return s
-}
-
// inlnode recurses over the tree to find inlineable calls, which will
// be turned into OINLCALLs by mkinlcall. When the recursion comes
// back up will examine left, right, list, rlist, ninit, ntest, nincr,
@@ -597,21 +533,33 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
case ir.ODEFER, ir.OGO:
n := n.(*ir.GoDeferStmt)
switch call := n.Call; call.Op() {
- case ir.OCALLFUNC, ir.OCALLMETH:
+ case ir.OCALLMETH:
+ base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
+ case ir.OCALLFUNC:
call := call.(*ir.CallExpr)
call.NoInline = true
}
+ case ir.OTAILCALL:
+ n := n.(*ir.TailCallStmt)
+ n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)?
// TODO do them here (or earlier),
// so escape analysis can avoid more heapmoves.
case ir.OCLOSURE:
return n
case ir.OCALLMETH:
- // Prevent inlining some reflect.Value methods when using checkptr,
- // even when package reflect was compiled without it (#35073).
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+ case ir.OCALLFUNC:
n := n.(*ir.CallExpr)
- if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
- return n
+ if n.X.Op() == ir.OMETHEXPR {
+ // Prevent inlining some reflect.Value methods when using checkptr,
+ // even when package reflect was compiled without it (#35073).
+ if meth := ir.MethodExprName(n.X); meth != nil {
+ s := meth.Sym()
+ if base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
+ return n
+ }
+ }
}
}
@@ -619,72 +567,31 @@ func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.No
ir.EditChildren(n, edit)
- if as := n; as.Op() == ir.OAS2FUNC {
- as := as.(*ir.AssignListStmt)
- if as.Rhs[0].Op() == ir.OINLCALL {
- as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr))
- as.SetOp(ir.OAS2)
- as.SetTypecheck(0)
- n = typecheck.Stmt(as)
- }
- }
-
// with all the branches out of the way, it is now time to
// transmogrify this node itself unless inhibited by the
// switch at the top of this function.
switch n.Op() {
- case ir.OCALLFUNC, ir.OCALLMETH:
- n := n.(*ir.CallExpr)
- if n.NoInline {
- return n
- }
- }
+ case ir.OCALLMETH:
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
- var call *ir.CallExpr
- switch n.Op() {
case ir.OCALLFUNC:
- call = n.(*ir.CallExpr)
+ call := n.(*ir.CallExpr)
+ if call.NoInline {
+ break
+ }
if base.Flag.LowerM > 3 {
fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
}
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)
}
-
- case ir.OCALLMETH:
- call = n.(*ir.CallExpr)
- if base.Flag.LowerM > 3 {
- fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel)
- }
-
- // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
- if call.X.Type() == nil {
- base.Fatalf("no function type for [%p] %+v\n", call.X, call.X)
- }
-
- n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit)
}
base.Pos = lno
- if n.Op() == ir.OINLCALL {
- ic := n.(*ir.InlinedCallExpr)
- switch call.Use {
- default:
- ir.Dump("call", call)
- base.Fatalf("call missing use")
- case ir.CallUseExpr:
- n = inlconv2expr(ic)
- case ir.CallUseStmt:
- n = inlconv2stmt(ic)
- case ir.CallUseList:
- // leave for caller to convert
- }
- }
-
return n
}
@@ -740,7 +647,12 @@ var inlgen int
// when producing output for debugging the compiler itself.
var SSADumpInline = func(*ir.Func) {}
-// If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
+// NewInline allows the inliner implementation to be overridden.
+// If it returns nil, the legacy inliner will handle this call
+// instead.
+var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { return nil }
+
+// If n is a OCALLFUNC node, and fn is an ONAME node for a
// function with an inlinable body, return an OINLCALL node that can replace n.
// The returned node's Ninit has the parameter assignments, the Nbody is the
// inlined function body, and (List, Rlist) contain the (input, output)
@@ -773,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
@@ -793,38 +726,90 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
defer func() {
inlMap[fn] = false
}()
- if base.Debug.TypecheckInl == 0 {
- typecheck.ImportedBody(fn)
+
+ typecheck.FixVariadicCall(n)
+
+ parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
+
+ sym := fn.Linksym()
+ inlIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
+
+ if base.Flag.GenDwarfInl > 0 {
+ if !sym.WasInlined() {
+ base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
+ sym.Set(obj.AttrWasInlined, true)
+ }
}
- // We have a function node, and it has an inlineable body.
- if base.Flag.LowerM > 1 {
- fmt.Printf("%v: inlining call to %v %v { %v }\n", ir.Line(n), fn.Sym(), fn.Type(), ir.Nodes(fn.Inl.Body))
- } else if base.Flag.LowerM != 0 {
+ if base.Flag.LowerM != 0 {
fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
}
if base.Flag.LowerM > 2 {
fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
}
- SSADumpInline(fn)
+ res := NewInline(n, fn, inlIndex)
+ if res == nil {
+ res = oldInline(n, fn, inlIndex)
+ }
+
+ // transitive inlining
+ // might be nice to do this before exporting the body,
+ // but can't emit the body with inlining expanded.
+ // instead we emit the things that the body needs
+ // and each use must redo the inlining.
+ // luckily these are small.
+ ir.EditChildren(res, edit)
- ninit := n.Init()
+ if base.Flag.LowerM > 2 {
+ fmt.Printf("%v: After inlining %+v\n\n", ir.Line(res), res)
+ }
- // For normal function calls, the function callee expression
- // may contain side effects (e.g., added by addinit during
- // inlconv2expr or inlconv2list). Make sure to preserve these,
- // if necessary (#42703).
- if n.Op() == ir.OCALLFUNC {
- callee := n.X
- for callee.Op() == ir.OCONVNOP {
+ return res
+}
+
+// CalleeEffects appends any side effects from evaluating callee to init.
+func CalleeEffects(init *ir.Nodes, callee ir.Node) {
+ for {
+ switch callee.Op() {
+ case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
+ return // done
+
+ case ir.OCONVNOP:
conv := callee.(*ir.ConvExpr)
- ninit.Append(ir.TakeInit(conv)...)
+ init.Append(ir.TakeInit(conv)...)
callee = conv.X
+
+ case ir.OINLCALL:
+ ic := callee.(*ir.InlinedCallExpr)
+ init.Append(ir.TakeInit(ic)...)
+ init.Append(ic.Body.Take()...)
+ callee = ic.SingleResult()
+
+ default:
+ base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee)
}
- if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
- base.Fatalf("unexpected callee expression: %v", callee)
- }
+ }
+}
+
+// oldInline creates an InlinedCallExpr to replace the given call
+// expression. fn is the callee function to be inlined. inlIndex is
+// the inlining tree position index, for use with src.NewInliningBase
+// when rewriting positions.
+func oldInline(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
+ if base.Debug.TypecheckInl == 0 {
+ typecheck.ImportedBody(fn)
+ }
+
+ SSADumpInline(fn)
+
+ ninit := call.Init()
+
+ // For normal function calls, the function callee expression
+ // may contain side effects. Make sure to preserve these,
+ // if necessary (#42703).
+ if call.Op() == ir.OCALLFUNC {
+ CalleeEffects(&ninit, call.X)
}
// Make temp names to use instead of the originals.
@@ -854,25 +839,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
}
// We can delay declaring+initializing result parameters if:
- // (1) there's exactly one "return" statement in the inlined function;
- // (2) it's not an empty return statement (#44355); and
- // (3) the result parameters aren't named.
- delayretvars := true
-
- nreturns := 0
- ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) {
- if n, ok := n.(*ir.ReturnStmt); ok {
- nreturns++
- if len(n.Results) == 0 {
- delayretvars = false // empty return statement (case 2)
- }
- }
- })
-
- if nreturns != 1 {
- delayretvars = false // not exactly one return statement (case 1)
- }
-
// temporaries for return values.
var retvars []ir.Node
for i, t := range fn.Type().Results().Fields().Slice() {
@@ -882,7 +848,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
m = inlvar(n)
m = typecheck.Expr(m).(*ir.Name)
inlvars[n] = m
- delayretvars = false // found a named result parameter (case 3)
} else {
// anonymous return values, synthesize names for use in assignment that replaces return
m = retvar(t, i)
@@ -905,61 +870,23 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
// Assign arguments to the parameters' temp names.
as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
as.Def = true
- if n.Op() == ir.OCALLMETH {
- sel := n.X.(*ir.SelectorExpr)
- if sel.X == nil {
- base.Fatalf("method call without receiver: %+v", n)
- }
- as.Rhs.Append(sel.X)
+ if call.Op() == ir.OCALLMETH {
+ base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
}
- as.Rhs.Append(n.Args...)
-
- // For non-dotted calls to variadic functions, we assign the
- // variadic parameter's temp name separately.
- var vas *ir.AssignStmt
+ as.Rhs.Append(call.Args...)
if recv := fn.Type().Recv(); recv != nil {
as.Lhs.Append(inlParam(recv, as, inlvars))
}
for _, param := range fn.Type().Params().Fields().Slice() {
- // For ordinary parameters or variadic parameters in
- // dotted calls, just add the variable to the
- // assignment list, and we're done.
- if !param.IsDDD() || n.IsDDD {
- as.Lhs.Append(inlParam(param, as, inlvars))
- continue
- }
-
- // Otherwise, we need to collect the remaining values
- // to pass as a slice.
-
- x := len(as.Lhs)
- for len(as.Lhs) < len(as.Rhs) {
- as.Lhs.Append(argvar(param.Type, len(as.Lhs)))
- }
- varargs := as.Lhs[x:]
-
- vas = ir.NewAssignStmt(base.Pos, nil, nil)
- vas.X = inlParam(param, vas, inlvars)
- if len(varargs) == 0 {
- vas.Y = typecheck.NodNil()
- vas.Y.SetType(param.Type)
- } else {
- lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(param.Type), nil)
- lit.List = varargs
- vas.Y = lit
- }
+ as.Lhs.Append(inlParam(param, as, inlvars))
}
if len(as.Rhs) != 0 {
ninit.Append(typecheck.Stmt(as))
}
- if vas != nil {
- ninit.Append(typecheck.Stmt(vas))
- }
-
- if !delayretvars {
+ if !fn.Inl.CanDelayResults {
// Zero the return parameters.
for _, n := range retvars {
ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
@@ -972,40 +899,21 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
inlgen++
- parent := -1
- if b := base.Ctxt.PosTable.Pos(n.Pos()).Base(); b != nil {
- parent = b.InliningIndex()
- }
-
- sym := fn.Linksym()
- newIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
-
// Add an inline mark just before the inlined body.
// This mark is inline in the code so that it's a reasonable spot
// to put a breakpoint. Not sure if that's really necessary or not
// (in which case it could go at the end of the function instead).
// Note issue 28603.
- inlMark := ir.NewInlineMarkStmt(base.Pos, types.BADWIDTH)
- inlMark.SetPos(n.Pos().WithIsStmt())
- inlMark.Index = int64(newIndex)
- ninit.Append(inlMark)
-
- if base.Flag.GenDwarfInl > 0 {
- if !sym.WasInlined() {
- base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
- sym.Set(obj.AttrWasInlined, true)
- }
- }
+ ninit.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(inlIndex)))
subst := inlsubst{
- retlabel: retlabel,
- retvars: retvars,
- delayretvars: delayretvars,
- inlvars: inlvars,
- defnMarker: ir.NilExpr{},
- bases: make(map[*src.PosBase]*src.PosBase),
- newInlIndex: newIndex,
- fn: fn,
+ retlabel: retlabel,
+ retvars: retvars,
+ inlvars: inlvars,
+ defnMarker: ir.NilExpr{},
+ bases: make(map[*src.PosBase]*src.PosBase),
+ newInlIndex: inlIndex,
+ fn: fn,
}
subst.edit = subst.node
@@ -1026,26 +934,11 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b
//dumplist("ninit post", ninit);
- call := ir.NewInlinedCallExpr(base.Pos, nil, nil)
- *call.PtrInit() = ninit
- call.Body = body
- call.ReturnVars = retvars
- call.SetType(n.Type())
- call.SetTypecheck(1)
-
- // transitive inlining
- // might be nice to do this before exporting the body,
- // but can't emit the body with inlining expanded.
- // instead we emit the things that the body needs
- // and each use must redo the inlining.
- // luckily these are small.
- ir.EditChildren(call, edit)
-
- if base.Flag.LowerM > 2 {
- fmt.Printf("%v: After inlining %+v\n\n", ir.Line(call), call)
- }
-
- return call
+ res := ir.NewInlinedCallExpr(base.Pos, body, retvars)
+ res.SetInit(ninit)
+ res.SetType(call.Type())
+ res.SetTypecheck(1)
+ return res
}
// Every time we expand a function we generate a new set of tmpnames,
@@ -1058,8 +951,10 @@ func inlvar(var_ *ir.Name) *ir.Name {
n := typecheck.NewName(var_.Sym())
n.SetType(var_.Type())
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetUsed(true)
+ n.SetAutoTemp(var_.AutoTemp())
n.Curfn = ir.CurFunc // the calling function, not the called one
n.SetAddrtaken(var_.Addrtaken())
@@ -1071,18 +966,7 @@ func inlvar(var_ *ir.Name) *ir.Name {
func retvar(t *types.Field, i int) *ir.Name {
n := typecheck.NewName(typecheck.LookupNum("~R", i))
n.SetType(t.Type)
- n.Class = ir.PAUTO
- n.SetUsed(true)
- n.Curfn = ir.CurFunc // the calling function, not the called one
- ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
- return n
-}
-
-// Synthesize a variable to store the inlined function's arguments
-// when they come from a multiple return call.
-func argvar(t *types.Type, i int) ir.Node {
- n := typecheck.NewName(typecheck.LookupNum("~arg", i))
- n.SetType(t.Elem())
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetUsed(true)
n.Curfn = ir.CurFunc // the calling function, not the called one
@@ -1099,10 +983,6 @@ type inlsubst struct {
// Temporary result variables.
retvars []ir.Node
- // Whether result variables should be initialized at the
- // "return" statement.
- delayretvars bool
-
inlvars map[*ir.Name]*ir.Name
// defnMarker is used to mark a Node for reassignment.
// inlsubst.clovar set this during creating new ONAME.
@@ -1157,17 +1037,21 @@ func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
// clovar creates a new ONAME node for a local variable or param of a closure
// inside a function being inlined.
func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
- // TODO(danscales): want to get rid of this shallow copy, with code like the
- // following, but it is hard to copy all the necessary flags in a maintainable way.
- // m := ir.NewNameAt(n.Pos(), n.Sym())
- // m.Class = n.Class
- // m.SetType(n.Type())
- // m.SetTypecheck(1)
- //if n.IsClosureVar() {
- // m.SetIsClosureVar(true)
- //}
- m := &ir.Name{}
- *m = *n
+ m := ir.NewNameAt(n.Pos(), n.Sym())
+ m.Class = n.Class
+ m.SetType(n.Type())
+ m.SetTypecheck(1)
+ if n.IsClosureVar() {
+ m.SetIsClosureVar(true)
+ }
+ if n.Addrtaken() {
+ m.SetAddrtaken(true)
+ }
+ if n.Used() {
+ m.SetUsed(true)
+ }
+ m.Defn = n.Defn
+
m.Curfn = subst.newclofn
switch defn := n.Defn.(type) {
@@ -1200,6 +1084,8 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
m.Defn = &subst.defnMarker
case *ir.TypeSwitchGuard:
// TODO(mdempsky): Set m.Defn properly. See discussion on #45743.
+ case *ir.RangeStmt:
+ // TODO: Set m.Defn properly if we support inlining range statement in the future.
default:
base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn)
}
@@ -1222,38 +1108,27 @@ func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
// closure does the necessary substitions for a ClosureExpr n and returns the new
// closure node.
func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
- m := ir.Copy(n)
-
- // Prior to the subst edit, set a flag in the inlsubst to
- // indicated that we don't want to update the source positions in
- // the new closure. If we do this, it will appear that the closure
- // itself has things inlined into it, which is not the case. See
- // issue #46234 for more details.
+ // Prior to the subst edit, set a flag in the inlsubst to indicate
+ // that we don't want to update the source positions in the new
+ // closure function. If we do this, it will appear that the
+ // closure itself has things inlined into it, which is not the
+ // case. See issue #46234 for more details. At the same time, we
+ // do want to update the position in the new ClosureExpr (which is
+ // part of the function we're working on). See #49171 for an
+ // example of what happens if we miss that update.
+ newClosurePos := subst.updatedPos(n.Pos())
defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
subst.noPosUpdate = true
- ir.EditChildren(m, subst.edit)
//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
- // The following is similar to funcLit
oldfn := n.Func
- newfn := ir.NewFunc(oldfn.Pos())
- // These three lines are not strictly necessary, but just to be clear
- // that new function needs to redo typechecking and inlinability.
- newfn.SetTypecheck(0)
- newfn.SetInlinabilityChecked(false)
- newfn.Inl = nil
- newfn.SetIsHiddenClosure(true)
- newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
- newfn.Nname.Func = newfn
+ newfn := ir.NewClosureFunc(oldfn.Pos(), true)
+
// Ntype can be nil for -G=3 mode.
if oldfn.Nname.Ntype != nil {
newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
}
- newfn.Nname.Defn = newfn
-
- m.(*ir.ClosureExpr).Func = newfn
- newfn.OClosure = m.(*ir.ClosureExpr)
if subst.newclofn != nil {
//fmt.Printf("Inlining a closure with a nested closure\n")
@@ -1303,13 +1178,10 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
// Actually create the named function for the closure, now that
// the closure is inlined in a specific function.
- m.SetTypecheck(0)
- if oldfn.ClosureCalled() {
- typecheck.Callee(m)
- } else {
- typecheck.Expr(m)
- }
- return m
+ newclo := newfn.OClosure
+ newclo.SetPos(newClosurePos)
+ newclo.SetInit(subst.list(n.Init()))
+ return typecheck.Expr(newclo)
}
// node recursively copies a node from the saved pristine body of the
@@ -1376,7 +1248,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())
@@ -1391,7 +1263,7 @@ func (subst *inlsubst) node(n ir.Node) ir.Node {
}
as.Rhs = subst.list(n.Results)
- if subst.delayretvars {
+ if subst.fn.Inl.CanDelayResults {
for _, n := range as.Lhs {
as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
n.Name().Defn = as
@@ -1404,7 +1276,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
@@ -1412,9 +1284,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:
@@ -1425,9 +1296,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:
@@ -1439,6 +1309,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 {
@@ -1455,6 +1346,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/expr.go b/src/cmd/compile/internal/ir/expr.go
index f70645f07913f8c309023d0fc84208d8e09b85b3..f526d987a7b20e6318655445319c30a7a1cda058 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -142,28 +142,15 @@ func (n *BinaryExpr) SetOp(op Op) {
}
}
-// A CallUse records how the result of the call is used:
-type CallUse byte
-
-const (
- _ CallUse = iota
-
- CallUseExpr // single expression result is used
- CallUseList // list of results are used
- CallUseStmt // results not used - call is a statement
-)
-
// A CallExpr is a function call X(Args).
type CallExpr struct {
miniExpr
origNode
- X Node
- Args Nodes
- KeepAlive []*Name // vars to be kept alive until call returns
- IsDDD bool
- Use CallUse
- NoInline bool
- PreserveClosure bool // disable directClosureCall for this call
+ X Node
+ Args Nodes
+ KeepAlive []*Name // vars to be kept alive until call returns
+ IsDDD bool
+ NoInline bool
}
func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {
@@ -181,8 +168,12 @@ func (n *CallExpr) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
- case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH,
- OAPPEND, ODELETE, OGETG, OMAKE, OPRINT, OPRINTN, ORECOVER:
+ case OAPPEND,
+ OCALL, OCALLFUNC, OCALLINTER, OCALLMETH,
+ ODELETE,
+ OGETG, OGETCALLERPC, OGETCALLERSP,
+ OMAKE, OPRINT, OPRINTN,
+ ORECOVER, ORECOVERFP:
n.op = op
}
}
@@ -192,8 +183,10 @@ type ClosureExpr struct {
miniExpr
Func *Func `mknode:"-"`
Prealloc *Name
+ IsGoWrap bool // whether this is wrapper closure of a go statement
}
+// Deprecated: Use NewClosureFunc instead.
func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr {
n := &ClosureExpr{Func: fn}
n.op = OCLOSURE
@@ -277,12 +270,12 @@ func (n *ConvExpr) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
- case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR:
+ case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR:
n.op = op
}
}
-// An IndexExpr is an index expression X[Y].
+// An IndexExpr is an index expression X[Index].
type IndexExpr struct {
miniExpr
X Node
@@ -323,26 +316,24 @@ func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr {
// A StructKeyExpr is an Field: Value composite literal key.
type StructKeyExpr struct {
miniExpr
- Field *types.Sym
- Value Node
- Offset int64
+ Field *types.Field
+ Value Node
}
-func NewStructKeyExpr(pos src.XPos, field *types.Sym, value Node) *StructKeyExpr {
+func NewStructKeyExpr(pos src.XPos, field *types.Field, value Node) *StructKeyExpr {
n := &StructKeyExpr{Field: field, Value: value}
n.pos = pos
n.op = OSTRUCTKEY
- n.Offset = types.BADWIDTH
return n
}
-func (n *StructKeyExpr) Sym() *types.Sym { return n.Field }
+func (n *StructKeyExpr) Sym() *types.Sym { return n.Field.Sym }
// An InlinedCallExpr is an inlined function call.
type InlinedCallExpr struct {
miniExpr
Body Nodes
- ReturnVars Nodes
+ ReturnVars Nodes // must be side-effect free
}
func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr {
@@ -354,6 +345,21 @@ func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr {
return n
}
+func (n *InlinedCallExpr) SingleResult() Node {
+ if have := len(n.ReturnVars); have != 1 {
+ base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have)
+ }
+ if !n.Type().HasShape() && n.ReturnVars[0].Type().HasShape() {
+ // If the type of the call is not a shape, but the type of the return value
+ // is a shape, we need to do an implicit conversion, so the real type
+ // of n is maintained.
+ r := NewConvExpr(n.Pos(), OCONVNOP, n.Type(), n.ReturnVars[0])
+ r.SetTypecheck(1)
+ return r
+ }
+ return n.ReturnVars[0]
+}
+
// A LogicalExpr is a expression X Op Y where Op is && or ||.
// It is separate from BinaryExpr to make room for statements
// that must be executed before Y but after X.
@@ -448,6 +454,20 @@ func (n *ParenExpr) SetOTYPE(t *types.Type) {
t.SetNod(n)
}
+// A RawOrigExpr represents an arbitrary Go expression as a string value.
+// When printed in diagnostics, the string value is written out exactly as-is.
+type RawOrigExpr struct {
+ miniExpr
+ Raw string
+}
+
+func NewRawOrigExpr(pos src.XPos, op Op, raw string) *RawOrigExpr {
+ n := &RawOrigExpr{Raw: raw}
+ n.pos = pos
+ n.op = op
+ return n
+}
+
// A ResultExpr represents a direct access to a result.
type ResultExpr struct {
miniExpr
@@ -494,10 +514,15 @@ func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type)
// A SelectorExpr is a selector expression X.Sel.
type SelectorExpr struct {
miniExpr
- X Node
- Sel *types.Sym
+ X Node
+ // Sel is the name of the field or method being selected, without (in the
+ // case of methods) any preceding type specifier. If the field/method is
+ // exported, than the Sym uses the local package regardless of the package
+ // of the containing type.
+ Sel *types.Sym
+ // The actual selected field - may not be filled in until typechecking.
Selection *types.Field
- Prealloc *Name // preallocated storage for OCALLPART, if any
+ Prealloc *Name // preallocated storage for OMETHVALUE, if any
}
func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr {
@@ -511,7 +536,7 @@ func (n *SelectorExpr) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
- case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OCALLPART, OMETHEXPR:
+ case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OMETHVALUE, OMETHEXPR:
n.op = op
}
}
@@ -652,6 +677,38 @@ func (n *TypeAssertExpr) SetOp(op Op) {
}
}
+// A DynamicTypeAssertExpr asserts that X is of dynamic type T.
+type DynamicTypeAssertExpr struct {
+ miniExpr
+ X Node
+ // N = not an interface
+ // E = empty interface
+ // I = nonempty interface
+ // For E->N, T is a *runtime.type for N
+ // For I->N, T is a *runtime.itab for N+I
+ // For E->I, T is a *runtime.type for I
+ // For I->I, ditto
+ // For I->E, T is a *runtime.type for interface{} (unnecessary, but just to fill in the slot)
+ // For E->E, ditto
+ T Node
+}
+
+func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, t Node) *DynamicTypeAssertExpr {
+ n := &DynamicTypeAssertExpr{X: x, T: t}
+ n.pos = pos
+ n.op = op
+ return n
+}
+
+func (n *DynamicTypeAssertExpr) SetOp(op Op) {
+ switch op {
+ default:
+ panic(n.no("SetOp " + op.String()))
+ case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2:
+ n.op = op
+ }
+}
+
// A UnaryExpr is a unary expression Op X,
// or Op(X) for a builtin function that does not end up being a call.
type UnaryExpr struct {
@@ -678,6 +735,11 @@ func (n *UnaryExpr) SetOp(op Op) {
}
}
+// Probably temporary: using Implicit() flag to mark generic function nodes that
+// are called to make getGfInfo analysis easier in one pre-order pass.
+func (n *InstExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 }
+func (n *InstExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }
+
// An InstExpr is a generic function or type instantiation.
type InstExpr struct {
miniExpr
@@ -773,6 +835,11 @@ func StaticValue(n Node) Node {
continue
}
+ if n.Op() == OINLCALL {
+ n = n.(*InlinedCallExpr).SingleResult()
+ continue
+ }
+
n1 := staticValue1(n)
if n1 == nil {
return n
@@ -1071,7 +1138,7 @@ func MethodExprName(n Node) *Name {
// MethodExprFunc is like MethodExprName, but returns the types.Field instead.
func MethodExprFunc(n Node) *types.Field {
switch n.Op() {
- case ODOTMETH, OMETHEXPR, OCALLPART:
+ case ODOTMETH, OMETHEXPR, OMETHVALUE:
return n.(*SelectorExpr).Selection
}
base.Fatalf("unexpected node: %v (%v)", n, n.Op())
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go
index f2ae0f7606ee7ba04d8b219f1330de8ecf18a121..033188547b61ca65c35e4d4e671db419e30e7bdb 100644
--- a/src/cmd/compile/internal/ir/fmt.go
+++ b/src/cmd/compile/internal/ir/fmt.go
@@ -185,6 +185,7 @@ var OpPrec = []int{
OCLOSE: 8,
OCOMPLIT: 8,
OCONVIFACE: 8,
+ OCONVIDATA: 8,
OCONVNOP: 8,
OCONV: 8,
OCOPY: 8,
@@ -237,7 +238,7 @@ var OpPrec = []int{
ODOTTYPE: 8,
ODOT: 8,
OXDOT: 8,
- OCALLPART: 8,
+ OMETHVALUE: 8,
OMETHEXPR: 8,
OPLUS: 7,
ONOT: 7,
@@ -385,7 +386,7 @@ func stmtFmt(n Node, s fmt.State) {
case OTAILCALL:
n := n.(*TailCallStmt)
- fmt.Fprintf(s, "tailcall %v", n.Target)
+ fmt.Fprintf(s, "tailcall %v", n.Call)
case OINLMARK:
n := n.(*InlineMarkStmt)
@@ -546,7 +547,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
n = nn.X
continue
}
- case OCONV, OCONVNOP, OCONVIFACE:
+ case OCONV, OCONVNOP, OCONVIFACE, OCONVIDATA:
nn := nn.(*ConvExpr)
if nn.Implicit() {
n = nn.X
@@ -558,7 +559,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
}
nprec := OpPrec[n.Op()]
- if n.Op() == OTYPE && n.Type().IsPtr() {
+ if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() {
nprec = OpPrec[ODEREF]
}
@@ -567,6 +568,11 @@ func exprFmt(n Node, s fmt.State, prec int) {
return
}
+ if n, ok := n.(*RawOrigExpr); ok {
+ fmt.Fprint(s, n.Raw)
+ return
+ }
+
switch n.Op() {
case OPAREN:
n := n.(*ParenExpr)
@@ -709,6 +715,10 @@ func exprFmt(n Node, s fmt.State, prec int) {
fmt.Fprintf(s, "... argument")
return
}
+ if typ := n.Type(); typ != nil {
+ fmt.Fprintf(s, "%v{%s}", typ, ellipsisIf(len(n.List) != 0))
+ return
+ }
if n.Ntype != nil {
fmt.Fprintf(s, "%v{%s}", n.Ntype, ellipsisIf(len(n.List) != 0))
return
@@ -752,7 +762,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
n := n.(*StructKeyExpr)
fmt.Fprintf(s, "%v:%v", n.Field, n.Value)
- case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OCALLPART, OMETHEXPR:
+ case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OMETHVALUE, OMETHEXPR:
n := n.(*SelectorExpr)
exprFmt(n.X, s, nprec)
if n.Sel == nil {
@@ -804,6 +814,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
case OCONV,
OCONVIFACE,
+ OCONVIDATA,
OCONVNOP,
OBYTES2STR,
ORUNES2STR,
@@ -854,6 +865,15 @@ func exprFmt(n Node, s fmt.State, prec int) {
}
fmt.Fprintf(s, "(%.v)", n.Args)
+ case OINLCALL:
+ n := n.(*InlinedCallExpr)
+ // TODO(mdempsky): Print Init and/or Body?
+ if len(n.ReturnVars) == 1 {
+ fmt.Fprintf(s, "%v", n.ReturnVars[0])
+ return
+ }
+ fmt.Fprintf(s, "(.%v)", n.ReturnVars)
+
case OMAKEMAP, OMAKECHAN, OMAKESLICE:
n := n.(*MakeExpr)
if n.Cap != nil {
@@ -986,7 +1006,7 @@ func (l Nodes) Format(s fmt.State, verb rune) {
// Dump prints the message s followed by a debug dump of n.
func Dump(s string, n Node) {
- fmt.Printf("%s [%p]%+v\n", s, n, n)
+ fmt.Printf("%s%+v\n", s, n)
}
// DumpList prints the message s followed by a debug dump of each node in the list.
@@ -1041,8 +1061,8 @@ func dumpNodeHeader(w io.Writer, n Node) {
}
}
- if n.Typecheck() != 0 {
- fmt.Fprintf(w, " tc(%d)", n.Typecheck())
+ if n.Sym() != nil && n.Op() != ONAME && n.Op() != ONONAME && n.Op() != OTYPE {
+ fmt.Fprintf(w, " %+v", n.Sym())
}
// Print Node-specific fields of basic type in header line.
@@ -1112,18 +1132,27 @@ func dumpNodeHeader(w io.Writer, n Node) {
}
fmt.Fprintf(w, " %+v", n.Type())
}
+ if n.Typecheck() != 0 {
+ fmt.Fprintf(w, " tc(%d)", n.Typecheck())
+ }
if n.Pos().IsKnown() {
- pfx := ""
+ fmt.Fprint(w, " # ")
switch n.Pos().IsStmt() {
case src.PosNotStmt:
- pfx = "_" // "-" would be confusing
+ fmt.Fprint(w, "_") // "-" would be confusing
case src.PosIsStmt:
- pfx = "+"
+ fmt.Fprint(w, "+")
+ }
+ for i, pos := range base.Ctxt.AllPos(n.Pos(), nil) {
+ if i > 0 {
+ fmt.Fprint(w, ",")
+ }
+ // TODO(mdempsky): Print line pragma details too.
+ file := filepath.Base(pos.Filename())
+ // Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync.
+ fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col())
}
- pos := base.Ctxt.PosTable.Pos(n.Pos())
- file := filepath.Base(pos.Filename())
- fmt.Fprintf(w, " # %s%s:%d", pfx, file, pos.Line())
}
}
@@ -1222,13 +1251,6 @@ func dumpNode(w io.Writer, n Node, depth int) {
return
}
- if n.Sym() != nil {
- fmt.Fprintf(w, " %+v", n.Sym())
- }
- if n.Type() != nil {
- fmt.Fprintf(w, " %+v", n.Type())
- }
-
v := reflect.ValueOf(n).Elem()
t := reflect.TypeOf(n).Elem()
nf := t.NumField()
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index 20fe965711df33896e2f8025ea91b450a863dc4d..41c96079f75df3ceed4c695ed686f89f939a5415 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -9,6 +9,7 @@ import (
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src"
+ "fmt"
)
// A Func corresponds to a single function in a Go program
@@ -39,14 +40,14 @@ import (
// constructs a fresh node.
//
// A method value (t.M) is represented by ODOTMETH/ODOTINTER
-// when it is called directly and by OCALLPART otherwise.
+// when it is called directly and by OMETHVALUE otherwise.
// These are like method expressions, except that for ODOTMETH/ODOTINTER,
// the method name is stored in Sym instead of Right.
-// Each OCALLPART ends up being implemented as a new
+// Each OMETHVALUE ends up being implemented as a new
// function, a bit like a closure, with its own ODCLFUNC.
-// The OCALLPART uses n.Func to record the linkage to
+// The OMETHVALUE uses n.Func to record the linkage to
// the generated ODCLFUNC, but there is no
-// pointer from the Func back to the OCALLPART.
+// pointer from the Func back to the OMETHVALUE.
type Func struct {
miniNode
Body Nodes
@@ -166,6 +167,11 @@ type Inline struct {
// another package is imported.
Dcl []*Name
Body []Node
+
+ // CanDelayResults reports whether it's safe for the inliner to delay
+ // initializing the result parameters until immediately before the
+ // "return" statement.
+ CanDelayResults bool
}
// A Mark represents a scope boundary.
@@ -190,13 +196,14 @@ const (
// true if closure inside a function; false if a simple function or a
// closure in a global variable initialization
funcIsHiddenClosure
+ funcIsDeadcodeClosure // true if closure is deadcode
funcHasDefer // contains a defer statement
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
+ funcClosureCalled // closure is only immediately called; used by escape analysis
)
type SymAndPos struct {
@@ -210,6 +217,7 @@ func (f *Func) ABIWrapper() bool { return f.flags&funcABIWrapper !
func (f *Func) Needctxt() bool { return f.flags&funcNeedctxt != 0 }
func (f *Func) ReflectMethod() bool { return f.flags&funcReflectMethod != 0 }
func (f *Func) IsHiddenClosure() bool { return f.flags&funcIsHiddenClosure != 0 }
+func (f *Func) IsDeadcodeClosure() bool { return f.flags&funcIsDeadcodeClosure != 0 }
func (f *Func) HasDefer() bool { return f.flags&funcHasDefer != 0 }
func (f *Func) NilCheckDisabled() bool { return f.flags&funcNilCheckDisabled != 0 }
func (f *Func) InlinabilityChecked() bool { return f.flags&funcInlinabilityChecked != 0 }
@@ -224,6 +232,7 @@ func (f *Func) SetABIWrapper(b bool) { f.flags.set(funcABIWrapper,
func (f *Func) SetNeedctxt(b bool) { f.flags.set(funcNeedctxt, b) }
func (f *Func) SetReflectMethod(b bool) { f.flags.set(funcReflectMethod, b) }
func (f *Func) SetIsHiddenClosure(b bool) { f.flags.set(funcIsHiddenClosure, b) }
+func (f *Func) SetIsDeadcodeClosure(b bool) { f.flags.set(funcIsDeadcodeClosure, b) }
func (f *Func) SetHasDefer(b bool) { f.flags.set(funcHasDefer, b) }
func (f *Func) SetNilCheckDisabled(b bool) { f.flags.set(funcNilCheckDisabled, b) }
func (f *Func) SetInlinabilityChecked(b bool) { f.flags.set(funcInlinabilityChecked, b) }
@@ -272,6 +281,17 @@ func PkgFuncName(f *Func) string {
var CurFunc *Func
+// WithFunc invokes do with CurFunc and base.Pos set to curfn and
+// curfn.Pos(), respectively, and then restores their previous values
+// before returning.
+func WithFunc(curfn *Func, do func()) {
+ oldfn, oldpos := CurFunc, base.Pos
+ defer func() { CurFunc, base.Pos = oldfn, oldpos }()
+
+ CurFunc, base.Pos = curfn, curfn.Pos()
+ do()
+}
+
func FuncSymName(s *types.Sym) string {
return s.Name + "·f"
}
@@ -279,7 +299,7 @@ func FuncSymName(s *types.Sym) string {
// MarkFunc marks a node as a function.
func MarkFunc(n *Name) {
if n.Op() != ONAME || n.Class != Pxxx {
- base.Fatalf("expected ONAME/Pxxx node, got %v", n)
+ base.FatalfAt(n.Pos(), "expected ONAME/Pxxx node, got %v (%v/%v)", n, n.Op(), n.Class)
}
n.Class = PFUNC
@@ -296,8 +316,8 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
}
}
- if base.Flag.CompilingRuntime && clo.Esc() == EscHeap {
- base.ErrorfAt(clo.Pos(), "heap-allocated closure, not allowed in runtime")
+ if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap {
+ base.ErrorfAt(clo.Pos(), "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func))
}
}
@@ -306,3 +326,109 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
func IsTrivialClosure(clo *ClosureExpr) bool {
return len(clo.Func.ClosureVars) == 0
}
+
+// globClosgen is like Func.Closgen, but for the global scope.
+var globClosgen int32
+
+// closureName generates a new unique name for a closure within outerfn.
+func closureName(outerfn *Func) *types.Sym {
+ pkg := types.LocalPkg
+ outer := "glob."
+ prefix := "func"
+ gen := &globClosgen
+
+ if outerfn != nil {
+ if outerfn.OClosure != nil {
+ prefix = ""
+ }
+
+ pkg = outerfn.Sym().Pkg
+ outer = FuncName(outerfn)
+
+ // There may be multiple functions named "_". In those
+ // cases, we can't use their individual Closgens as it
+ // would lead to name clashes.
+ if !IsBlank(outerfn.Nname) {
+ gen = &outerfn.Closgen
+ }
+ }
+
+ *gen++
+ return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
+}
+
+// NewClosureFunc creates a new Func to represent a function literal.
+// If hidden is true, then the closure is marked hidden (i.e., as a
+// function literal contained within another function, rather than a
+// package-scope variable initialization expression).
+func NewClosureFunc(pos src.XPos, hidden bool) *Func {
+ fn := NewFunc(pos)
+ fn.SetIsHiddenClosure(hidden)
+
+ fn.Nname = NewNameAt(pos, BlankNode.Sym())
+ fn.Nname.Func = fn
+ fn.Nname.Defn = fn
+
+ fn.OClosure = NewClosureExpr(pos, fn)
+
+ return fn
+}
+
+// NameClosure generates a unique for the given function literal,
+// which must have appeared within outerfn.
+func NameClosure(clo *ClosureExpr, outerfn *Func) {
+ fn := clo.Func
+ if fn.IsHiddenClosure() != (outerfn != nil) {
+ base.FatalfAt(clo.Pos(), "closure naming inconsistency: hidden %v, but outer %v", fn.IsHiddenClosure(), outerfn)
+ }
+
+ name := fn.Nname
+ if !IsBlank(name) {
+ base.FatalfAt(clo.Pos(), "closure already named: %v", name)
+ }
+
+ name.SetSym(closureName(outerfn))
+ MarkFunc(name)
+}
+
+// UseClosure checks that the ginen function literal has been setup
+// correctly, and then returns it as an expression.
+// It must be called after clo.Func.ClosureVars has been set.
+func UseClosure(clo *ClosureExpr, pkg *Package) Node {
+ fn := clo.Func
+ name := fn.Nname
+
+ if IsBlank(name) {
+ base.FatalfAt(fn.Pos(), "unnamed closure func: %v", fn)
+ }
+ // Caution: clo.Typecheck() is still 0 when UseClosure is called by
+ // tcClosure.
+ if fn.Typecheck() != 1 || name.Typecheck() != 1 {
+ base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn)
+ }
+ if clo.Type() == nil || name.Type() == nil {
+ base.FatalfAt(fn.Pos(), "missing types: %v", fn)
+ }
+ if !types.Identical(clo.Type(), name.Type()) {
+ base.FatalfAt(fn.Pos(), "mismatched types: %v", fn)
+ }
+
+ if base.Flag.W > 1 {
+ s := fmt.Sprintf("new closure func: %v", fn)
+ Dump(s, fn)
+ }
+
+ if pkg != nil {
+ pkg.Decls = append(pkg.Decls, fn)
+ }
+
+ if false && IsTrivialClosure(clo) {
+ // TODO(mdempsky): Investigate if we can/should optimize this
+ // case. walkClosure already handles it later, but it could be
+ // useful to recognize earlier (e.g., it might allow multiple
+ // inlined calls to a function to share a common trivial closure
+ // func, rather than cloning it for each inlined call).
+ }
+
+ return clo
+}
diff --git a/src/cmd/compile/internal/ir/mini.go b/src/cmd/compile/internal/ir/mini.go
index a7ff4ac9c77a1a122b8fd558dd69f6b83e4879cb..eeb74081fb8bc51724e761f3579dc660416490bb 100644
--- a/src/cmd/compile/internal/ir/mini.go
+++ b/src/cmd/compile/internal/ir/mini.go
@@ -62,7 +62,7 @@ const (
func (n *miniNode) Typecheck() uint8 { return n.bits.get2(miniTypecheckShift) }
func (n *miniNode) SetTypecheck(x uint8) {
- if x > 3 {
+ if x > 2 {
panic(fmt.Sprintf("cannot SetTypecheck %d", x))
}
n.bits.set2(miniTypecheckShift, x)
diff --git a/src/cmd/compile/internal/ir/name.go b/src/cmd/compile/internal/ir/name.go
index b6c68bc5e01a4b33d8b590f29658886446584fbf..1d4110c73cf731ac343a5b8395a917663b902e8f 100644
--- a/src/cmd/compile/internal/ir/name.go
+++ b/src/cmd/compile/internal/ir/name.go
@@ -40,6 +40,7 @@ type Name struct {
Class Class // uint8
pragma PragmaFlag // int16
flags bitset16
+ DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1
sym *types.Sym
Func *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem
Offset_ int64
@@ -51,6 +52,8 @@ type Name struct {
// For a local variable (not param) or extern, the initializing assignment (OAS or OAS2).
// For a closure var, the ONAME node of the outer captured variable.
// For the case-local variables of a type switch, the type switch guard (OTYPESW).
+ // For a range variable, the range statement (ORANGE)
+ // For a recv variable in a case of a select statement, the receive assignment (OSELRECV2)
// For the name of a function, points to corresponding Func node.
Defn Node
@@ -143,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.
@@ -358,39 +364,74 @@ func (n *Name) Byval() bool {
return n.Canonical().flags&nameByval != 0
}
+// NewClosureVar returns a new closure variable for fn to refer to
+// outer variable n.
+func NewClosureVar(pos src.XPos, fn *Func, n *Name) *Name {
+ c := NewNameAt(pos, n.Sym())
+ c.Curfn = fn
+ c.Class = PAUTOHEAP
+ c.SetIsClosureVar(true)
+ c.Defn = n.Canonical()
+ c.Outer = n
+
+ c.SetType(n.Type())
+ c.SetTypecheck(n.Typecheck())
+
+ fn.ClosureVars = append(fn.ClosureVars, c)
+
+ return c
+}
+
+// NewHiddenParam returns a new hidden parameter for fn with the given
+// name and type.
+func NewHiddenParam(pos src.XPos, fn *Func, sym *types.Sym, typ *types.Type) *Name {
+ if fn.OClosure != nil {
+ base.FatalfAt(fn.Pos(), "cannot add hidden parameters to closures")
+ }
+
+ fn.SetNeedctxt(true)
+
+ // Create a fake parameter, disassociated from any real function, to
+ // pretend to capture.
+ fake := NewNameAt(pos, sym)
+ fake.Class = PPARAM
+ fake.SetType(typ)
+ fake.SetByval(true)
+
+ return NewClosureVar(pos, fn, fake)
+}
+
// CaptureName returns a Name suitable for referring to n from within function
// fn or from the package block if fn is nil. If n is a free variable declared
-// within a function that encloses fn, then CaptureName returns a closure
-// variable that refers to n and adds it to fn.ClosureVars. Otherwise, it simply
-// returns n.
+// within a function that encloses fn, then CaptureName returns the closure
+// variable that refers to n within fn, creating it if necessary.
+// Otherwise, it simply returns n.
func CaptureName(pos src.XPos, fn *Func, n *Name) *Name {
- if n.IsClosureVar() {
- base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
- }
- if n.Op() != ONAME || n.Curfn == nil || n.Curfn == fn {
+ if n.Op() != ONAME || n.Curfn == nil {
return n // okay to use directly
}
- if fn == nil {
- base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
+ if n.IsClosureVar() {
+ base.FatalfAt(pos, "misuse of CaptureName on closure variable: %v", n)
}
c := n.Innermost
- if c != nil && c.Curfn == fn {
+ if c == nil {
+ c = n
+ }
+ if c.Curfn == fn {
return c
}
+ if fn == nil {
+ base.FatalfAt(pos, "package-block reference to %v, declared in %v", n, n.Curfn)
+ }
+
// Do not have a closure var for the active closure yet; make one.
- c = NewNameAt(pos, n.Sym())
- c.Curfn = fn
- c.Class = PAUTOHEAP
- c.SetIsClosureVar(true)
- c.Defn = n
+ c = NewClosureVar(pos, fn, c)
// Link into list of active closure variables.
// Popped from list in FinishCaptureNames.
- c.Outer = n.Innermost
n.Innermost = c
- fn.ClosureVars = append(fn.ClosureVars, c)
return c
}
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index af559cc0820cc3e74ebd3065630e558c008e3e53..4fdee5010b660c1b8cbab3e02b1c6db068ffdd79 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -159,7 +159,6 @@ const (
OCALLFUNC // X(Args) (function call f(args))
OCALLMETH // X(Args) (direct method call x.Method(args))
OCALLINTER // X(Args) (interface method call x.Method(args))
- OCALLPART // X.Sel (method expression x.Method, not called)
OCAP // cap(X)
OCLOSE // close(X)
OCLOSURE // func Type { Func.Closure.Body } (func literal)
@@ -171,6 +170,7 @@ const (
OPTRLIT // &X (X is composite literal)
OCONV // Type(X) (type conversion)
OCONVIFACE // Type(X) (type conversion, to interface)
+ OCONVIDATA // Builds a data word to store X in an interface. Equivalent to IDATA(CONVIFACE(X)). Is an ir.ConvExpr.
OCONVNOP // Type(X) (type conversion, no effect)
OCOPY // copy(X, Y)
ODCL // var X (declares X of type X.Type)
@@ -237,6 +237,7 @@ const (
OSLICE3ARR // X[Low : High : Max] (X is pointer to array)
OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity)
ORECOVER // recover()
+ ORECOVERFP // recover(Args) w/ explicit FP argument
ORECV // <-X
ORUNESTR // Type(X) (Type is string, X is rune)
OSELRECV2 // like OAS2: Lhs = Rhs where len(Lhs)=2, len(Rhs)=1, Rhs[0].Op = ORECV (appears as .Var of OCASE)
@@ -249,14 +250,16 @@ const (
OSIZEOF // unsafe.Sizeof(X)
OUNSAFEADD // unsafe.Add(X, Y)
OUNSAFESLICE // unsafe.Slice(X, Y)
- OMETHEXPR // method expression
+ OMETHEXPR // X(Args) (method expression T.Method(args), first argument is the method receiver)
+ OMETHVALUE // X.Sel (method expression t.Method, not called)
// statements
OBLOCK // { List } (block of code)
OBREAK // break [Label]
// OCASE: case List: Body (List==nil means default)
// For OTYPESW, List is a OTYPE node for the specified type (or OLITERAL
- // for nil), and, if a type-switch variable is specified, Rlist is an
+ // for nil) or an ODYNAMICTYPE indicating a runtime type for generics.
+ // If a type-switch variable is specified, Var is an
// ONAME for the version of the type-switch variable with the specified
// type.
OCASE
@@ -317,13 +320,30 @@ const (
OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
OLINKSYMOFFSET // offset within a name
+ // opcodes for generics
+ ODYNAMICDOTTYPE // x = i.(T) where T is a type parameter (or derived from a type parameter)
+ ODYNAMICDOTTYPE2 // x, ok = i.(T) where T is a type parameter (or derived from a type parameter)
+ ODYNAMICTYPE // a type node for type switches (represents a dynamic target type for a type switch)
+
// arch-specific opcodes
- OTAILCALL // tail call to another function
- OGETG // runtime.getg() (read g pointer)
+ OTAILCALL // tail call to another function
+ OGETG // runtime.getg() (read g pointer)
+ OGETCALLERPC // runtime.getcallerpc() (continuation PC in caller frame)
+ OGETCALLERSP // runtime.getcallersp() (stack pointer in caller frame)
OEND
)
+// IsCmp reports whether op is a comparison operation (==, !=, <, <=,
+// >, or >=).
+func (op Op) IsCmp() bool {
+ switch op {
+ case OEQ, ONE, OLT, OLE, OGT, OGE:
+ return true
+ }
+ return false
+}
+
// Nodes is a pointer to a slice of *Node.
// For fields that are not used in most nodes, this is used instead of
// a slice to save space.
@@ -436,18 +456,19 @@ func (s NameSet) Sorted(less func(*Name, *Name) bool) []*Name {
return res
}
-type PragmaFlag int16
+type PragmaFlag uint16
const (
// Func pragmas.
- Nointerface PragmaFlag = 1 << iota
- Noescape // func parameters don't escape
- Norace // func must not have race detector annotations
- Nosplit // func should not execute on separate stack
- Noinline // func should not be inlined
- NoCheckPtr // func should not be instrumented by checkptr
- CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
- UintptrEscapes // pointers converted to uintptr escape
+ Nointerface PragmaFlag = 1 << iota
+ Noescape // func parameters don't escape
+ Norace // func must not have race detector annotations
+ Nosplit // func should not execute on separate stack
+ Noinline // func should not be inlined
+ NoCheckPtr // func should not be instrumented by checkptr
+ CgoUnsafeArgs // treat a pointer to one arg as a pointer to them all
+ UintptrKeepAlive // pointers converted to uintptr must be kept alive (compiler internal only)
+ UintptrEscapes // pointers converted to uintptr escape
// Runtime-only func pragmas.
// See ../../../../runtime/README.md for detailed descriptions.
@@ -563,7 +584,7 @@ func OuterValue(n Node) Node {
for {
switch nn := n; nn.Op() {
case OXDOT:
- base.Fatalf("OXDOT in walk")
+ base.FatalfAt(n.Pos(), "OXDOT in OuterValue: %v", n)
case ODOT:
nn := nn.(*SelectorExpr)
n = nn.X
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
index 22855d7163f44a6e9d51586c60cabc3c397dccff..44988880c8c2a7a52a37517cc57d361e3b3ab144 100644
--- a/src/cmd/compile/internal/ir/node_gen.go
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -463,6 +463,62 @@ func (n *Decl) editChildren(edit func(Node) Node) {
}
}
+func (n *DynamicType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *DynamicType) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *DynamicType) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.ITab != nil && do(n.ITab) {
+ return true
+ }
+ return false
+}
+func (n *DynamicType) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.ITab != nil {
+ n.ITab = edit(n.ITab).(Node)
+ }
+}
+
+func (n *DynamicTypeAssertExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *DynamicTypeAssertExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *DynamicTypeAssertExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.X != nil && do(n.X) {
+ return true
+ }
+ if n.T != nil && do(n.T) {
+ return true
+ }
+ return false
+}
+func (n *DynamicTypeAssertExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.X != nil {
+ n.X = edit(n.X).(Node)
+ }
+ if n.T != nil {
+ n.T = edit(n.T).(Node)
+ }
+}
+
func (n *ForStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *ForStmt) copy() Node {
c := *n
@@ -947,6 +1003,22 @@ func (n *RangeStmt) editChildren(edit func(Node) Node) {
}
}
+func (n *RawOrigExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *RawOrigExpr) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *RawOrigExpr) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ return false
+}
+func (n *RawOrigExpr) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+}
+
func (n *ResultExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *ResultExpr) copy() Node {
c := *n
@@ -1259,15 +1331,15 @@ func (n *TailCallStmt) doChildren(do func(Node) bool) bool {
if doNodes(n.init, do) {
return true
}
- if n.Target != nil && do(n.Target) {
+ if n.Call != nil && do(n.Call) {
return true
}
return false
}
func (n *TailCallStmt) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
- if n.Target != nil {
- n.Target = edit(n.Target).(*Name)
+ if n.Call != nil {
+ n.Call = edit(n.Call).(*CallExpr)
}
}
diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go
index 405a0c6b3c985851c4dbbf68c5fd8990a61e06cf..b8cee718182c08889eb73e6493d0c67ddb0e7b59 100644
--- a/src/cmd/compile/internal/ir/op_string.go
+++ b/src/cmd/compile/internal/ir/op_string.go
@@ -41,18 +41,18 @@ func _() {
_ = x[OCALLFUNC-30]
_ = x[OCALLMETH-31]
_ = x[OCALLINTER-32]
- _ = x[OCALLPART-33]
- _ = x[OCAP-34]
- _ = x[OCLOSE-35]
- _ = x[OCLOSURE-36]
- _ = x[OCOMPLIT-37]
- _ = x[OMAPLIT-38]
- _ = x[OSTRUCTLIT-39]
- _ = x[OARRAYLIT-40]
- _ = x[OSLICELIT-41]
- _ = x[OPTRLIT-42]
- _ = x[OCONV-43]
- _ = x[OCONVIFACE-44]
+ _ = x[OCAP-33]
+ _ = x[OCLOSE-34]
+ _ = x[OCLOSURE-35]
+ _ = x[OCOMPLIT-36]
+ _ = x[OMAPLIT-37]
+ _ = x[OSTRUCTLIT-38]
+ _ = x[OARRAYLIT-39]
+ _ = x[OSLICELIT-40]
+ _ = x[OPTRLIT-41]
+ _ = x[OCONV-42]
+ _ = x[OCONVIFACE-43]
+ _ = x[OCONVIDATA-44]
_ = x[OCONVNOP-45]
_ = x[OCOPY-46]
_ = x[ODCL-47]
@@ -109,65 +109,72 @@ func _() {
_ = x[OSLICE3ARR-98]
_ = x[OSLICEHEADER-99]
_ = x[ORECOVER-100]
- _ = x[ORECV-101]
- _ = x[ORUNESTR-102]
- _ = x[OSELRECV2-103]
- _ = x[OIOTA-104]
- _ = x[OREAL-105]
- _ = x[OIMAG-106]
- _ = x[OCOMPLEX-107]
- _ = x[OALIGNOF-108]
- _ = x[OOFFSETOF-109]
- _ = x[OSIZEOF-110]
- _ = x[OUNSAFEADD-111]
- _ = x[OUNSAFESLICE-112]
- _ = x[OMETHEXPR-113]
- _ = x[OBLOCK-114]
- _ = x[OBREAK-115]
- _ = x[OCASE-116]
- _ = x[OCONTINUE-117]
- _ = x[ODEFER-118]
- _ = x[OFALL-119]
- _ = x[OFOR-120]
- _ = x[OFORUNTIL-121]
- _ = x[OGOTO-122]
- _ = x[OIF-123]
- _ = x[OLABEL-124]
- _ = x[OGO-125]
- _ = x[ORANGE-126]
- _ = x[ORETURN-127]
- _ = x[OSELECT-128]
- _ = x[OSWITCH-129]
- _ = x[OTYPESW-130]
- _ = x[OFUNCINST-131]
- _ = x[OTCHAN-132]
- _ = x[OTMAP-133]
- _ = x[OTSTRUCT-134]
- _ = x[OTINTER-135]
- _ = x[OTFUNC-136]
- _ = x[OTARRAY-137]
- _ = x[OTSLICE-138]
- _ = x[OINLCALL-139]
- _ = x[OEFACE-140]
- _ = x[OITAB-141]
- _ = x[OIDATA-142]
- _ = x[OSPTR-143]
- _ = x[OCFUNC-144]
- _ = x[OCHECKNIL-145]
- _ = x[OVARDEF-146]
- _ = x[OVARKILL-147]
- _ = x[OVARLIVE-148]
- _ = x[ORESULT-149]
- _ = x[OINLMARK-150]
- _ = x[OLINKSYMOFFSET-151]
- _ = x[OTAILCALL-152]
- _ = x[OGETG-153]
- _ = x[OEND-154]
+ _ = x[ORECOVERFP-101]
+ _ = x[ORECV-102]
+ _ = x[ORUNESTR-103]
+ _ = x[OSELRECV2-104]
+ _ = x[OIOTA-105]
+ _ = x[OREAL-106]
+ _ = x[OIMAG-107]
+ _ = x[OCOMPLEX-108]
+ _ = x[OALIGNOF-109]
+ _ = x[OOFFSETOF-110]
+ _ = x[OSIZEOF-111]
+ _ = x[OUNSAFEADD-112]
+ _ = x[OUNSAFESLICE-113]
+ _ = x[OMETHEXPR-114]
+ _ = x[OMETHVALUE-115]
+ _ = x[OBLOCK-116]
+ _ = x[OBREAK-117]
+ _ = x[OCASE-118]
+ _ = x[OCONTINUE-119]
+ _ = x[ODEFER-120]
+ _ = x[OFALL-121]
+ _ = x[OFOR-122]
+ _ = x[OFORUNTIL-123]
+ _ = x[OGOTO-124]
+ _ = x[OIF-125]
+ _ = x[OLABEL-126]
+ _ = x[OGO-127]
+ _ = x[ORANGE-128]
+ _ = x[ORETURN-129]
+ _ = x[OSELECT-130]
+ _ = x[OSWITCH-131]
+ _ = x[OTYPESW-132]
+ _ = x[OFUNCINST-133]
+ _ = x[OTCHAN-134]
+ _ = x[OTMAP-135]
+ _ = x[OTSTRUCT-136]
+ _ = x[OTINTER-137]
+ _ = x[OTFUNC-138]
+ _ = x[OTARRAY-139]
+ _ = x[OTSLICE-140]
+ _ = x[OINLCALL-141]
+ _ = x[OEFACE-142]
+ _ = x[OITAB-143]
+ _ = x[OIDATA-144]
+ _ = x[OSPTR-145]
+ _ = x[OCFUNC-146]
+ _ = x[OCHECKNIL-147]
+ _ = x[OVARDEF-148]
+ _ = x[OVARKILL-149]
+ _ = x[OVARLIVE-150]
+ _ = x[ORESULT-151]
+ _ = x[OINLMARK-152]
+ _ = x[OLINKSYMOFFSET-153]
+ _ = x[ODYNAMICDOTTYPE-154]
+ _ = x[ODYNAMICDOTTYPE2-155]
+ _ = x[ODYNAMICTYPE-156]
+ _ = x[OTAILCALL-157]
+ _ = x[OGETG-158]
+ _ = x[OGETCALLERPC-159]
+ _ = x[OGETCALLERSP-160]
+ _ = x[OEND-161]
}
-const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
+const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 213, 216, 221, 228, 235, 241, 250, 258, 266, 272, 276, 285, 292, 296, 299, 306, 314, 321, 327, 330, 336, 343, 351, 355, 362, 370, 372, 374, 376, 378, 380, 382, 387, 392, 400, 403, 412, 415, 419, 427, 434, 443, 456, 459, 462, 465, 468, 471, 474, 480, 483, 486, 492, 496, 499, 503, 508, 513, 519, 524, 528, 533, 541, 549, 555, 564, 575, 582, 586, 593, 601, 605, 609, 613, 620, 627, 635, 641, 650, 661, 669, 674, 679, 683, 691, 696, 700, 703, 711, 715, 717, 722, 724, 729, 735, 741, 747, 753, 761, 766, 770, 777, 783, 788, 794, 800, 807, 812, 816, 821, 825, 830, 838, 844, 851, 858, 864, 871, 884, 892, 896, 899}
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 208, 213, 220, 227, 233, 242, 250, 258, 264, 268, 277, 286, 293, 297, 300, 307, 315, 322, 328, 331, 337, 344, 352, 356, 363, 371, 373, 375, 377, 379, 381, 383, 388, 393, 401, 404, 413, 416, 420, 428, 435, 444, 457, 460, 463, 466, 469, 472, 475, 481, 484, 487, 493, 497, 500, 504, 509, 514, 520, 525, 529, 534, 542, 550, 556, 565, 576, 583, 592, 596, 603, 611, 615, 619, 623, 630, 637, 645, 651, 660, 671, 679, 688, 693, 698, 702, 710, 715, 719, 722, 730, 734, 736, 741, 743, 748, 754, 760, 766, 772, 780, 785, 789, 796, 802, 807, 813, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 877, 883, 890, 903, 917, 932, 943, 951, 955, 966, 977, 980}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
diff --git a/src/cmd/compile/internal/ir/package.go b/src/cmd/compile/internal/ir/package.go
index e4b93d113eda9c89b44512e2e5957c977df15a03..3896e2b91b1177dbc41977960d7fe26648e88d6e 100644
--- a/src/cmd/compile/internal/ir/package.go
+++ b/src/cmd/compile/internal/ir/package.go
@@ -32,7 +32,4 @@ type Package struct {
// Exported (or re-exported) symbols.
Exports []*Name
-
- // Map from function names of stencils to already-created stencils.
- Stencils map[*types.Sym]*Func
}
diff --git a/src/cmd/compile/internal/ir/scc.go b/src/cmd/compile/internal/ir/scc.go
index 83c6074170b31370e0e6c6e71f35e56533204d2b..a42951c1dda2de5676c496e1b9cfe45971e54ed5 100644
--- a/src/cmd/compile/internal/ir/scc.go
+++ b/src/cmd/compile/internal/ir/scc.go
@@ -90,7 +90,7 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 {
if n := n.(*Name); n.Class == PFUNC {
do(n.Defn)
}
- case ODOTMETH, OCALLPART, OMETHEXPR:
+ case ODOTMETH, OMETHVALUE, OMETHEXPR:
if fn := MethodExprName(n); fn != nil {
do(fn.Defn)
}
@@ -116,12 +116,11 @@ func (v *bottomUpVisitor) visit(n *Func) uint32 {
var i int
for i = len(v.stack) - 1; i >= 0; i-- {
x := v.stack[i]
+ v.nodeID[x] = ^uint32(0)
if x == n {
break
}
- v.nodeID[x] = ^uint32(0)
}
- v.nodeID[n] = ^uint32(0)
block := v.stack[i:]
// Run escape analysis on this set of functions.
v.stack = v.stack[:i]
diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go
index 8115012f97852c030d59b0e74bdd0cad17825916..e7d0d873b781ef79e43fa81d2447a139a54cdea5 100644
--- a/src/cmd/compile/internal/ir/stmt.go
+++ b/src/cmd/compile/internal/ir/stmt.go
@@ -244,7 +244,7 @@ func NewGoDeferStmt(pos src.XPos, op Op, call Node) *GoDeferStmt {
return n
}
-// A IfStmt is a return statement: if Init; Cond { Then } else { Else }.
+// An IfStmt is a return statement: if Init; Cond { Body } else { Else }.
type IfStmt struct {
miniStmt
Cond Node
@@ -338,7 +338,7 @@ type SelectStmt struct {
HasBreak bool
// TODO(rsc): Instead of recording here, replace with a block?
- Compiled Nodes // compiled form, after walkSwitch
+ Compiled Nodes // compiled form, after walkSelect
}
func NewSelectStmt(pos src.XPos, cases []*CommClause) *SelectStmt {
@@ -385,14 +385,11 @@ func NewSwitchStmt(pos src.XPos, tag Node, cases []*CaseClause) *SwitchStmt {
// code generation to jump directly to another function entirely.
type TailCallStmt struct {
miniStmt
- Target *Name
+ Call *CallExpr // the underlying call
}
-func NewTailCallStmt(pos src.XPos, target *Name) *TailCallStmt {
- if target.Op() != ONAME || target.Class != PFUNC {
- base.FatalfAt(pos, "tail call to non-func %v", target)
- }
- n := &TailCallStmt{Target: target}
+func NewTailCallStmt(pos src.XPos, call *CallExpr) *TailCallStmt {
+ n := &TailCallStmt{Call: call}
n.pos = pos
n.op = OTAILCALL
return n
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 61727fb1c4b004d52e4942631e200ce837f791f4..b204a1d544e7b4e2ba9835d1b7c27f529082adf6 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -11,33 +11,36 @@ import (
// Syms holds known symbols.
var Syms struct {
- AssertE2I *obj.LSym
- AssertE2I2 *obj.LSym
- AssertI2I *obj.LSym
- AssertI2I2 *obj.LSym
- Deferproc *obj.LSym
- DeferprocStack *obj.LSym
- Deferreturn *obj.LSym
- Duffcopy *obj.LSym
- Duffzero *obj.LSym
- GCWriteBarrier *obj.LSym
- Goschedguarded *obj.LSym
- Growslice *obj.LSym
- Msanread *obj.LSym
- Msanwrite *obj.LSym
- Msanmove *obj.LSym
- Newobject *obj.LSym
- Newproc *obj.LSym
- Panicdivide *obj.LSym
- Panicshift *obj.LSym
- PanicdottypeE *obj.LSym
- PanicdottypeI *obj.LSym
- Panicnildottype *obj.LSym
- Panicoverflow *obj.LSym
- Raceread *obj.LSym
- Racereadrange *obj.LSym
- Racewrite *obj.LSym
- Racewriterange *obj.LSym
+ AssertE2I *obj.LSym
+ AssertE2I2 *obj.LSym
+ AssertI2I *obj.LSym
+ AssertI2I2 *obj.LSym
+ Asanread *obj.LSym
+ Asanwrite *obj.LSym
+ CheckPtrAlignment *obj.LSym
+ Deferproc *obj.LSym
+ DeferprocStack *obj.LSym
+ Deferreturn *obj.LSym
+ Duffcopy *obj.LSym
+ Duffzero *obj.LSym
+ GCWriteBarrier *obj.LSym
+ Goschedguarded *obj.LSym
+ Growslice *obj.LSym
+ Msanread *obj.LSym
+ Msanwrite *obj.LSym
+ Msanmove *obj.LSym
+ Newobject *obj.LSym
+ Newproc *obj.LSym
+ Panicdivide *obj.LSym
+ Panicshift *obj.LSym
+ PanicdottypeE *obj.LSym
+ PanicdottypeI *obj.LSym
+ Panicnildottype *obj.LSym
+ Panicoverflow *obj.LSym
+ Raceread *obj.LSym
+ Racereadrange *obj.LSym
+ Racewrite *obj.LSym
+ Racewriterange *obj.LSym
// Wasm
SigPanic *obj.LSym
Staticuint64s *obj.LSym
@@ -68,5 +71,4 @@ var Pkgs struct {
Go *types.Pkg
Itab *types.Pkg
Runtime *types.Pkg
- Unsafe *types.Pkg
}
diff --git a/src/cmd/compile/internal/ir/type.go b/src/cmd/compile/internal/ir/type.go
index a903ea8cd45543faebe7c9bce30c428f318b6a1b..63dd673dcdf11c9440139583a0252b22c384cdd6 100644
--- a/src/cmd/compile/internal/ir/type.go
+++ b/src/cmd/compile/internal/ir/type.go
@@ -300,11 +300,36 @@ func (n *typeNode) CanBeNtype() {}
// TypeNode returns the Node representing the type t.
func TypeNode(t *types.Type) Ntype {
+ return TypeNodeAt(src.NoXPos, t)
+}
+
+// TypeNodeAt is like TypeNode, but allows specifying the position
+// information if a new OTYPE needs to be constructed.
+//
+// Deprecated: Use TypeNode instead. For typical use, the position for
+// an anonymous OTYPE node should not matter. However, TypeNodeAt is
+// available for use with toolstash -cmp to refactor existing code
+// that is sensitive to OTYPE position.
+func TypeNodeAt(pos src.XPos, t *types.Type) Ntype {
if n := t.Obj(); n != nil {
if n.Type() != t {
base.Fatalf("type skew: %v has type %v, but expected %v", n, n.Type(), t)
}
return n.(Ntype)
}
- return newTypeNode(src.NoXPos, t)
+ return newTypeNode(pos, t)
+}
+
+// A DynamicType represents the target type in a type switch.
+type DynamicType struct {
+ miniExpr
+ X Node // a *runtime._type for the targeted type
+ ITab Node // for type switches from nonempty interfaces to non-interfaces, this is the itab for that pair.
+}
+
+func NewDynamicType(pos src.XPos, x Node) *DynamicType {
+ n := &DynamicType{X: x}
+ n.pos = pos
+ n.op = ODYNAMICTYPE
+ return n
}
diff --git a/src/cmd/compile/internal/ir/val.go b/src/cmd/compile/internal/ir/val.go
index 03c320e205dbfdf8b6b46eb7d3d9adcc8295c46d..bfe7d2bb436c8999631fdbf364c7c766006118a5 100644
--- a/src/cmd/compile/internal/ir/val.go
+++ b/src/cmd/compile/internal/ir/val.go
@@ -66,7 +66,7 @@ func Float64Val(v constant.Value) float64 {
func AssertValidTypeForConst(t *types.Type, v constant.Value) {
if !ValidTypeForConst(t, v) {
- base.Fatalf("%v does not represent %v", t, v)
+ base.Fatalf("%v (%v) does not represent %v (%v)", t, t.Kind(), v, v.Kind())
}
}
diff --git a/src/cmd/compile/internal/liveness/arg.go b/src/cmd/compile/internal/liveness/arg.go
new file mode 100644
index 0000000000000000000000000000000000000000..2ca5d095aa91095591eff61171f5922046caf48f
--- /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 3431f54ede84e09bec45272d044247e115eb59d2..60b25938677c85c1ce7c07be102159230f506637 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 f5c2ef7709e515c376cc181c8e338bd51a1b33d8..3202e506c8f913aef4c14aad66a388b6b8971cc2 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -15,7 +15,6 @@
package liveness
import (
- "crypto/md5"
"crypto/sha1"
"fmt"
"os"
@@ -274,7 +273,7 @@ func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) {
}
}
- if n.Class == ir.PPARAM && !n.Addrtaken() && n.Type().Width > int64(types.PtrSize) {
+ if n.Class == ir.PPARAM && !n.Addrtaken() && n.Type().Size() > int64(types.PtrSize) {
// Only aggregate-typed arguments that are not address-taken can be
// partially live.
lv.partLiveArgs[n] = true
@@ -855,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,
}
}
@@ -903,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 {
@@ -1082,6 +1082,10 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) {
if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") {
return
}
+ if lv.fn.Wrapper() || lv.fn.Dupok() {
+ // Skip reporting liveness information for compiler-generated wrappers.
+ return
+ }
if !(v == nil || v.Op.IsCall()) {
// Historically we only printed this information at
// calls. Keep doing so.
@@ -1322,19 +1326,9 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) {
loff = objw.BitVec(&liveSymTmp, loff, locals)
}
- // Give these LSyms content-addressable names,
- // so that they can be de-duplicated.
- // This provides significant binary size savings.
- //
// These symbols will be added to Ctxt.Data by addGCLocals
// after parallel compilation is done.
- makeSym := func(tmpSym *obj.LSym) *obj.LSym {
- return base.Ctxt.LookupInit(fmt.Sprintf("gclocals·%x", md5.Sum(tmpSym.P)), func(lsym *obj.LSym) {
- lsym.P = tmpSym.P
- lsym.Set(obj.AttrContentAddressable, true)
- })
- }
- return makeSym(&argsSymTmp), makeSym(&liveSymTmp)
+ return base.Ctxt.GCLocalsSym(argsSymTmp.P), base.Ctxt.GCLocalsSym(liveSymTmp.P)
}
// Entry pointer for Compute analysis. Solves for the Compute of
@@ -1424,6 +1418,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym {
// Populate the stack object data.
// Format must match runtime/stack.go:stackObjectRecord.
x := base.Ctxt.Lookup(lv.fn.LSym.Name + ".stkobj")
+ x.Set(obj.AttrContentAddressable, true)
lv.fn.LSym.Func().StackObjects = x
off := 0
off = objw.Uintptr(x, off, uint64(len(vars)))
@@ -1440,7 +1435,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym {
off = objw.Uint32(x, off, uint32(frameOffset))
t := v.Type()
- sz := t.Width
+ sz := t.Size()
if sz != int64(int32(sz)) {
base.Fatalf("stack object too big: %v of type %v, size %d", v, t, sz)
}
@@ -1450,7 +1445,7 @@ func (lv *liveness) emitStackObjects() *obj.LSym {
}
off = objw.Uint32(x, off, uint32(sz))
off = objw.Uint32(x, off, uint32(ptrdata))
- off = objw.SymPtr(x, off, lsym, 0)
+ off = objw.SymPtrOff(x, off, lsym)
}
if base.Flag.Live != 0 {
diff --git a/src/cmd/compile/internal/logopt/logopt_test.go b/src/cmd/compile/internal/logopt/logopt_test.go
index 71976174b03517def512290e47d86a4980a02963..902cbc8091f1eb1e56b1c0507cd25b19b52c78c1 100644
--- a/src/cmd/compile/internal/logopt/logopt_test.go
+++ b/src/cmd/compile/internal/logopt/logopt_test.go
@@ -209,7 +209,7 @@ func s15a8(x *[15]int64) [15]int64 {
want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`)
want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`)
// escape analysis explanation
- want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r2 with derefs=0",`+
+ want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r0 with derefs=0",`+
`"relatedInformation":[`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+
@@ -220,8 +220,8 @@ func s15a8(x *[15]int64) [15]int64 {
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
`{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~R0 = \u0026y.b (assign-pair)"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r2 = ~R0:"},`+
- `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return (*int)(~R0) (return)"}]}`)
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~R0:"},`+
+ `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~R0 (return)"}]}`)
})
}
diff --git a/src/cmd/compile/internal/mips/galign.go b/src/cmd/compile/internal/mips/galign.go
index f892923ba038f32a370873af79a9671e55e65a1b..4e6897042ec04ce2e81fa58960a26541fa5aaeb0 100644
--- a/src/cmd/compile/internal/mips/galign.go
+++ b/src/cmd/compile/internal/mips/galign.go
@@ -21,7 +21,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.SoftFloat = (buildcfg.GOMIPS == "softfloat")
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index e0447f38cbf23347c05a831d304581259eb9b092..6326f966bf2158807d6bdab361a19393c884f831 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -475,6 +475,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p6.To.SetTarget(p2)
case ssa.OpMIPSCALLstatic, ssa.OpMIPSCALLclosure, ssa.OpMIPSCALLinter:
s.Call(v)
+ case ssa.OpMIPSCALLtail:
+ s.TailCall(v)
case ssa.OpMIPSLoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -841,14 +843,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockMIPSEQ, ssa.BlockMIPSNE,
ssa.BlockMIPSLTZ, ssa.BlockMIPSGEZ,
ssa.BlockMIPSLEZ, ssa.BlockMIPSGTZ,
diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go
index af81366e51bdf51c4cd88b55da03440da29773c6..412bc71aab270d97befa5dd3296ad381db180dcc 100644
--- a/src/cmd/compile/internal/mips64/galign.go
+++ b/src/cmd/compile/internal/mips64/galign.go
@@ -21,7 +21,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.SoftFloat = buildcfg.GOMIPS64 == "softfloat"
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = func(s *ssagen.State, b *ssa.Block) {}
arch.SSAGenValue = ssaGenValue
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index e821a00876fa799bc6b0a4413ed48acb445e336e..990b9788f765e39974873f07f544b218b171df6f 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -491,6 +491,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p6.To.SetTarget(p2)
case ssa.OpMIPS64CALLstatic, ssa.OpMIPS64CALLclosure, ssa.OpMIPS64CALLinter:
s.Call(v)
+ case ssa.OpMIPS64CALLtail:
+ s.TailCall(v)
case ssa.OpMIPS64LoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -808,14 +810,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockMIPS64EQ, ssa.BlockMIPS64NE,
ssa.BlockMIPS64LTZ, ssa.BlockMIPS64GEZ,
ssa.BlockMIPS64LEZ, ssa.BlockMIPS64GTZ,
diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8cb7729acc2287a13f626d9ab8ce495701b6711
--- /dev/null
+++ b/src/cmd/compile/internal/noder/codes.go
@@ -0,0 +1,124 @@
+// UNREVIEWED
+
+// 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 noder
+
+type code interface {
+ marker() syncMarker
+ value() int
+}
+
+type codeVal int
+
+func (c codeVal) marker() syncMarker { return syncVal }
+func (c codeVal) value() int { return int(c) }
+
+const (
+ valBool codeVal = iota
+ valString
+ valInt64
+ valBigInt
+ valBigRat
+ valBigFloat
+)
+
+type codeType int
+
+func (c codeType) marker() syncMarker { return syncType }
+func (c codeType) value() int { return int(c) }
+
+const (
+ typeBasic codeType = iota
+ typeNamed
+ typePointer
+ typeSlice
+ typeArray
+ typeChan
+ typeMap
+ typeSignature
+ typeStruct
+ typeInterface
+ typeUnion
+ typeTypeParam
+)
+
+type codeObj int
+
+func (c codeObj) marker() syncMarker { return syncCodeObj }
+func (c codeObj) value() int { return int(c) }
+
+const (
+ objAlias codeObj = iota
+ objConst
+ objType
+ objFunc
+ objVar
+ objStub
+)
+
+type codeStmt int
+
+func (c codeStmt) marker() syncMarker { return syncStmt1 }
+func (c codeStmt) value() int { return int(c) }
+
+const (
+ stmtEnd codeStmt = iota
+ stmtLabel
+ stmtBlock
+ stmtExpr
+ stmtSend
+ stmtAssign
+ stmtAssignOp
+ stmtIncDec
+ stmtBranch
+ stmtCall
+ stmtReturn
+ stmtIf
+ stmtFor
+ stmtSwitch
+ stmtSelect
+
+ // TODO(mdempsky): Remove after we don't care about toolstash -cmp.
+ stmtTypeDeclHack
+)
+
+type codeExpr int
+
+func (c codeExpr) marker() syncMarker { return syncExpr }
+func (c codeExpr) value() int { return int(c) }
+
+// TODO(mdempsky): Split expr into addr, for lvalues.
+const (
+ exprNone codeExpr = iota
+ exprConst
+ exprType // type expression
+ exprLocal // local variable
+ exprName // global variable or function
+ exprBlank
+ exprCompLit
+ exprFuncLit
+ exprSelector
+ exprIndex
+ exprSlice
+ exprAssert
+ exprUnaryOp
+ exprBinaryOp
+ exprCall
+ exprConvert
+)
+
+type codeDecl int
+
+func (c codeDecl) marker() syncMarker { return syncDecl }
+func (c codeDecl) value() int { return int(c) }
+
+const (
+ declEnd codeDecl = iota
+ declFunc
+ declMethod
+ declVar
+ declOther
+)
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
index 4ca2eb4740a4916e171d2cfe278a38c9b472e9f1..df1ca1c5055e0e2b497178fc063599b0375fb147 100644
--- a/src/cmd/compile/internal/noder/decl.go
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -18,43 +18,48 @@ import (
// TODO(mdempsky): Skip blank declarations? Probably only safe
// for declarations without pragmas.
-func (g *irgen) decls(decls []syntax.Decl) []ir.Node {
- var res ir.Nodes
+func (g *irgen) decls(res *ir.Nodes, decls []syntax.Decl) {
for _, decl := range decls {
switch decl := decl.(type) {
case *syntax.ConstDecl:
- g.constDecl(&res, decl)
+ g.constDecl(res, decl)
case *syntax.FuncDecl:
- g.funcDecl(&res, decl)
+ g.funcDecl(res, decl)
case *syntax.TypeDecl:
if ir.CurFunc == nil {
continue // already handled in irgen.generate
}
- g.typeDecl(&res, decl)
+ g.typeDecl(res, decl)
case *syntax.VarDecl:
- g.varDecl(&res, decl)
+ g.varDecl(res, decl)
default:
g.unhandled("declaration", decl)
}
}
- return res
}
func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
- // TODO(mdempsky): Merge with gcimports so we don't have to import
- // packages twice.
-
g.pragmaFlags(decl.Pragma, 0)
- ipkg := importfile(decl)
- if ipkg == ir.Pkgs.Unsafe {
+ // Get the imported package's path, as resolved already by types2
+ // and gcimporter. This is the same path as would be computed by
+ // parseImportPath.
+ switch pkgNameOf(g.info, decl).Imported().Path() {
+ case "unsafe":
p.importedUnsafe = true
- }
- if ipkg.Path == "embed" {
+ case "embed":
p.importedEmbed = true
}
}
+// pkgNameOf returns the PkgName associated with the given ImportDecl.
+func pkgNameOf(info *types2.Info, decl *syntax.ImportDecl) *types2.PkgName {
+ if name := decl.LocalPkgName; name != nil {
+ return info.Defs[name].(*types2.PkgName)
+ }
+ return info.Implicits[decl].(*types2.PkgName)
+}
+
func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
g.pragmaFlags(decl.Pragma, 0)
@@ -81,6 +86,18 @@ func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
}
func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
+ assert(g.curDecl == "")
+ // Set g.curDecl to the function name, as context for the type params declared
+ // during types2-to-types1 translation if this is a generic function.
+ g.curDecl = decl.Name.Value
+ obj2 := g.info.Defs[decl.Name]
+ recv := types2.AsSignature(obj2.Type()).Recv()
+ if recv != nil {
+ t2 := deref2(recv.Type())
+ // This is a method, so set g.curDecl to recvTypeName.methName instead.
+ g.curDecl = t2.(*types2.Named).Obj().Name() + "." + g.curDecl
+ }
+
fn := ir.NewFunc(g.pos(decl))
fn.Nname, _ = g.def(decl.Name)
fn.Nname.Func = fn
@@ -90,29 +107,72 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 {
base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined")
}
+ if fn.Pragma&ir.Nointerface != 0 {
+ // Propagate //go:nointerface from Func.Pragma to Field.Nointerface.
+ // This is a bit roundabout, but this is the earliest point where we've
+ // processed the function's pragma flags, and we've also already created
+ // the Fields to represent the receiver's method set.
+ if recv := fn.Type().Recv(); recv != nil {
+ typ := types.ReceiverBaseType(recv.Type)
+ if typ.OrigSym() != nil {
+ // For a generic method, we mark the methods on the
+ // base generic type, since those are the methods
+ // that will be stenciled.
+ typ = typ.OrigSym().Def.Type()
+ }
+ meth := typecheck.Lookdot1(fn, typecheck.Lookup(decl.Name.Value), typ, typ.Methods(), 0)
+ meth.SetNointerface(true)
+ }
+ }
+
+ if decl.Body != nil && fn.Pragma&ir.Noescape != 0 {
+ base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations")
+ }
if decl.Name.Value == "init" && decl.Recv == nil {
g.target.Inits = append(g.target.Inits, fn)
}
- g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
+ haveEmbed := g.haveEmbed
+ g.curDecl = ""
+ g.later(func() {
+ defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
+
+ g.haveEmbed = haveEmbed
+ if fn.Type().HasTParam() {
+ g.topFuncIsGeneric = true
+ }
+ g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
+ g.topFuncIsGeneric = false
+ if fn.Type().HasTParam() && fn.Body != nil {
+ // Set pointers to the dcls/body of a generic function/method in
+ // the Inl struct, so it is marked for export, is available for
+ // stenciling, and works with Inline_Flood().
+ fn.Inl = &ir.Inline{
+ Cost: 1,
+ Dcl: fn.Dcl,
+ Body: fn.Body,
+ }
+ }
- out.Append(fn)
+ out.Append(fn)
+ })
}
func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = g.pos(decl)
+ assert(g.curDecl == "")
+ // Set g.curDecl to the type name, as context for the type params declared
+ // during types2-to-types1 translation if this is a generic type.
+ g.curDecl = decl.Name.Value
if decl.Alias {
name, _ := g.def(decl.Name)
g.pragmaFlags(decl.Pragma, 0)
-
- // TODO(mdempsky): This matches how typecheckdef marks aliases for
- // export, but this won't generalize to exporting function-scoped
- // type aliases. We should maybe just use n.Alias() instead.
- if ir.CurFunc == nil {
- name.Sym().Def = ir.TypeNode(name.Type())
- }
+ assert(name.Alias()) // should be set by irgen.obj
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
+ g.curDecl = ""
return
}
@@ -122,8 +182,7 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
name, obj := g.def(decl.Name)
ntyp, otyp := name.Type(), obj.Type()
if ir.CurFunc != nil {
- typecheck.TypeGen++
- ntyp.Vargen = typecheck.TypeGen
+ ntyp.SetVargen()
}
pragmas := g.pragmaFlags(decl.Pragma, typePragmas)
@@ -154,21 +213,30 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
// [mdempsky: Subtleties like these are why I always vehemently
// object to new type pragmas.]
ntyp.SetUnderlying(g.typeExpr(decl.Type))
- if len(decl.TParamList) > 0 {
- // Set HasTParam if there are any tparams, even if no tparams are
- // used in the type itself (e.g., if it is an empty struct, or no
- // fields in the struct use the tparam).
- ntyp.SetHasTParam(true)
+
+ tparams := otyp.(*types2.Named).TypeParams()
+ if n := tparams.Len(); n > 0 {
+ rparams := make([]*types.Type, n)
+ for i := range rparams {
+ rparams[i] = g.typ(tparams.At(i))
+ }
+ // This will set hasTParam flag if any rparams are not concrete types.
+ ntyp.SetRParams(rparams)
}
types.ResumeCheckSize()
+ g.curDecl = ""
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
methods := make([]*types.Field, otyp.NumMethods())
for i := range methods {
m := otyp.Method(i)
+ // Set g.curDecl to recvTypeName.methName, as context for the
+ // method-specific type params in the receiver.
+ g.curDecl = decl.Name.Value + "." + m.Name()
meth := g.obj(m)
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
methods[i].Nname = meth
+ g.curDecl = ""
}
ntyp.Methods().Set(methods)
}
@@ -178,57 +246,78 @@ func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
pos := g.pos(decl)
+ // Set the position for any error messages we might print (e.g. too large types).
+ base.Pos = pos
names := make([]*ir.Name, len(decl.NameList))
for i, name := range decl.NameList {
names[i], _ = g.def(name)
}
- values := g.exprList(decl.Values)
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)
}
- var as2 *ir.AssignListStmt
- if len(values) != 0 && len(names) != len(values) {
- as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values)
- }
+ haveEmbed := g.haveEmbed
+ do := func() {
+ defer func(b bool) { g.haveEmbed = b }(g.haveEmbed)
- for i, name := range names {
- if ir.CurFunc != nil {
- out.Append(ir.NewDecl(pos, ir.ODCL, name))
+ g.haveEmbed = haveEmbed
+ values := g.exprList(decl.Values)
+
+ var as2 *ir.AssignListStmt
+ if len(values) != 0 && len(names) != len(values) {
+ as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values)
}
- if as2 != nil {
- as2.Lhs[i] = name
- name.Defn = as2
- } else {
- as := ir.NewAssignStmt(pos, name, nil)
- if len(values) != 0 {
- as.Y = values[i]
- name.Defn = as
- } else if ir.CurFunc == nil {
- name.Defn = as
+
+ for i, name := range names {
+ if ir.CurFunc != nil {
+ out.Append(ir.NewDecl(pos, ir.ODCL, name))
}
- lhs := []ir.Node{as.X}
- rhs := []ir.Node{}
- if as.Y != nil {
- rhs = []ir.Node{as.Y}
+ if as2 != nil {
+ as2.Lhs[i] = name
+ name.Defn = as2
+ } else {
+ as := ir.NewAssignStmt(pos, name, nil)
+ if len(values) != 0 {
+ as.Y = values[i]
+ name.Defn = as
+ } else if ir.CurFunc == nil {
+ name.Defn = as
+ }
+ if !g.delayTransform() {
+ lhs := []ir.Node{as.X}
+ rhs := []ir.Node{}
+ if as.Y != nil {
+ rhs = []ir.Node{as.Y}
+ }
+ transformAssign(as, lhs, rhs)
+ as.X = lhs[0]
+ if as.Y != nil {
+ as.Y = rhs[0]
+ }
+ }
+ as.SetTypecheck(1)
+ out.Append(as)
}
- transformAssign(as, lhs, rhs)
- as.X = lhs[0]
- if as.Y != nil {
- as.Y = rhs[0]
+ }
+ if as2 != nil {
+ if !g.delayTransform() {
+ transformAssign(as2, as2.Lhs, as2.Rhs)
}
- as.SetTypecheck(1)
- out.Append(as)
+ as2.SetTypecheck(1)
+ out.Append(as2)
}
}
- if as2 != nil {
- transformAssign(as2, as2.Lhs, as2.Rhs)
- as2.SetTypecheck(1)
- out.Append(as2)
+
+ // If we're within a function, we need to process the assignment
+ // part of the variable declaration right away. Otherwise, we leave
+ // it to be handled after all top-level declarations are processed.
+ if ir.CurFunc != nil {
+ do()
+ } else {
+ g.later(do)
}
}
diff --git a/src/cmd/compile/internal/noder/decoder.go b/src/cmd/compile/internal/noder/decoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c18727420aec23281eac633cb89f31e1b30efce
--- /dev/null
+++ b/src/cmd/compile/internal/noder/decoder.go
@@ -0,0 +1,302 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "math/big"
+ "os"
+ "runtime"
+ "strings"
+
+ "cmd/compile/internal/base"
+)
+
+type pkgDecoder struct {
+ pkgPath string
+
+ elemEndsEnds [numRelocs]uint32
+ elemEnds []uint32
+ elemData string
+}
+
+func newPkgDecoder(pkgPath, input string) pkgDecoder {
+ pr := pkgDecoder{
+ pkgPath: pkgPath,
+ }
+
+ // TODO(mdempsky): Implement direct indexing of input string to
+ // avoid copying the position information.
+
+ r := strings.NewReader(input)
+
+ assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil)
+
+ pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1])
+ assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil)
+
+ pos, err := r.Seek(0, os.SEEK_CUR)
+ assert(err == nil)
+
+ pr.elemData = input[pos:]
+ assert(len(pr.elemData) == int(pr.elemEnds[len(pr.elemEnds)-1]))
+
+ return pr
+}
+
+func (pr *pkgDecoder) numElems(k reloc) int {
+ count := int(pr.elemEndsEnds[k])
+ if k > 0 {
+ count -= int(pr.elemEndsEnds[k-1])
+ }
+ return count
+}
+
+func (pr *pkgDecoder) totalElems() int {
+ return len(pr.elemEnds)
+}
+
+func (pr *pkgDecoder) absIdx(k reloc, idx int) int {
+ absIdx := idx
+ if k > 0 {
+ absIdx += int(pr.elemEndsEnds[k-1])
+ }
+ if absIdx >= int(pr.elemEndsEnds[k]) {
+ base.Fatalf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
+ }
+ return absIdx
+}
+
+func (pr *pkgDecoder) dataIdx(k reloc, idx int) string {
+ absIdx := pr.absIdx(k, idx)
+
+ var start uint32
+ if absIdx > 0 {
+ start = pr.elemEnds[absIdx-1]
+ }
+ end := pr.elemEnds[absIdx]
+
+ return pr.elemData[start:end]
+}
+
+func (pr *pkgDecoder) stringIdx(idx int) string {
+ return pr.dataIdx(relocString, idx)
+}
+
+func (pr *pkgDecoder) newDecoder(k reloc, idx int, marker syncMarker) decoder {
+ r := pr.newDecoderRaw(k, idx)
+ r.sync(marker)
+ return r
+}
+
+func (pr *pkgDecoder) newDecoderRaw(k reloc, idx int) decoder {
+ r := decoder{
+ common: pr,
+ k: k,
+ idx: idx,
+ }
+
+ // TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
+ r.data = *strings.NewReader(pr.dataIdx(k, idx))
+
+ r.sync(syncRelocs)
+ r.relocs = make([]relocEnt, r.len())
+ for i := range r.relocs {
+ r.sync(syncReloc)
+ r.relocs[i] = relocEnt{reloc(r.len()), r.len()}
+ }
+
+ return r
+}
+
+type decoder struct {
+ common *pkgDecoder
+
+ relocs []relocEnt
+ data strings.Reader
+
+ k reloc
+ idx int
+}
+
+func (r *decoder) checkErr(err error) {
+ if err != nil {
+ base.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func (r *decoder) rawUvarint() uint64 {
+ x, err := binary.ReadUvarint(&r.data)
+ r.checkErr(err)
+ return x
+}
+
+func (r *decoder) rawVarint() int64 {
+ ux := r.rawUvarint()
+
+ // Zig-zag decode.
+ x := int64(ux >> 1)
+ if ux&1 != 0 {
+ x = ^x
+ }
+ return x
+}
+
+func (r *decoder) rawReloc(k reloc, idx int) int {
+ e := r.relocs[idx]
+ assert(e.kind == k)
+ return e.idx
+}
+
+func (r *decoder) sync(mWant syncMarker) {
+ if !enableSync {
+ return
+ }
+
+ pos, _ := r.data.Seek(0, os.SEEK_CUR) // TODO(mdempsky): io.SeekCurrent after #44505 is resolved
+ mHave := syncMarker(r.rawUvarint())
+ writerPCs := make([]int, r.rawUvarint())
+ for i := range writerPCs {
+ writerPCs[i] = int(r.rawUvarint())
+ }
+
+ if mHave == mWant {
+ return
+ }
+
+ // There's some tension here between printing:
+ //
+ // (1) full file paths that tools can recognize (e.g., so emacs
+ // hyperlinks the "file:line" text for easy navigation), or
+ //
+ // (2) short file paths that are easier for humans to read (e.g., by
+ // omitting redundant or irrelevant details, so it's easier to
+ // focus on the useful bits that remain).
+ //
+ // The current formatting favors the former, as it seems more
+ // helpful in practice. But perhaps the formatting could be improved
+ // to better address both concerns. For example, use relative file
+ // paths if they would be shorter, or rewrite file paths to contain
+ // "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
+ // to reliably expand that again.
+
+ fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.idx, pos)
+
+ fmt.Printf("\nfound %v, written at:\n", mHave)
+ if len(writerPCs) == 0 {
+ fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath)
+ }
+ for _, pc := range writerPCs {
+ fmt.Printf("\t%s\n", r.common.stringIdx(r.rawReloc(relocString, pc)))
+ }
+
+ fmt.Printf("\nexpected %v, reading at:\n", mWant)
+ var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size?
+ n := runtime.Callers(2, readerPCs[:])
+ for _, pc := range fmtFrames(readerPCs[:n]...) {
+ fmt.Printf("\t%s\n", pc)
+ }
+
+ // We already printed a stack trace for the reader, so now we can
+ // simply exit. Printing a second one with panic or base.Fatalf
+ // would just be noise.
+ os.Exit(1)
+}
+
+func (r *decoder) bool() bool {
+ r.sync(syncBool)
+ x, err := r.data.ReadByte()
+ r.checkErr(err)
+ assert(x < 2)
+ return x != 0
+}
+
+func (r *decoder) int64() int64 {
+ r.sync(syncInt64)
+ return r.rawVarint()
+}
+
+func (r *decoder) uint64() uint64 {
+ r.sync(syncUint64)
+ return r.rawUvarint()
+}
+
+func (r *decoder) len() int { x := r.uint64(); v := int(x); assert(uint64(v) == x); return v }
+func (r *decoder) int() int { x := r.int64(); v := int(x); assert(int64(v) == x); return v }
+func (r *decoder) uint() uint { x := r.uint64(); v := uint(x); assert(uint64(v) == x); return v }
+
+func (r *decoder) code(mark syncMarker) int {
+ r.sync(mark)
+ return r.len()
+}
+
+func (r *decoder) reloc(k reloc) int {
+ r.sync(syncUseReloc)
+ return r.rawReloc(k, r.len())
+}
+
+func (r *decoder) string() string {
+ r.sync(syncString)
+ return r.common.stringIdx(r.reloc(relocString))
+}
+
+func (r *decoder) strings() []string {
+ res := make([]string, r.len())
+ for i := range res {
+ res[i] = r.string()
+ }
+ return res
+}
+
+func (r *decoder) value() constant.Value {
+ r.sync(syncValue)
+ isComplex := r.bool()
+ val := r.scalar()
+ if isComplex {
+ val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar()))
+ }
+ return val
+}
+
+func (r *decoder) scalar() constant.Value {
+ switch tag := codeVal(r.code(syncVal)); tag {
+ default:
+ panic(fmt.Sprintf("unexpected scalar tag: %v", tag))
+
+ case valBool:
+ return constant.MakeBool(r.bool())
+ case valString:
+ return constant.MakeString(r.string())
+ case valInt64:
+ return constant.MakeInt64(r.int64())
+ case valBigInt:
+ return constant.Make(r.bigInt())
+ case valBigRat:
+ num := r.bigInt()
+ denom := r.bigInt()
+ return constant.Make(new(big.Rat).SetFrac(num, denom))
+ case valBigFloat:
+ return constant.Make(r.bigFloat())
+ }
+}
+
+func (r *decoder) bigInt() *big.Int {
+ v := new(big.Int).SetBytes([]byte(r.string()))
+ if r.bool() {
+ v.Neg(v)
+ }
+ return v
+}
+
+func (r *decoder) bigFloat() *big.Float {
+ v := new(big.Float).SetPrec(512)
+ assert(v.UnmarshalText([]byte(r.string())) == nil)
+ return v
+}
diff --git a/src/cmd/compile/internal/noder/encoder.go b/src/cmd/compile/internal/noder/encoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..b07b3a4a480573b8a2279bf9dce2df1445608e22
--- /dev/null
+++ b/src/cmd/compile/internal/noder/encoder.go
@@ -0,0 +1,285 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "io"
+ "math/big"
+ "runtime"
+
+ "cmd/compile/internal/base"
+)
+
+type pkgEncoder struct {
+ elems [numRelocs][]string
+
+ stringsIdx map[string]int
+}
+
+func newPkgEncoder() pkgEncoder {
+ return pkgEncoder{
+ stringsIdx: make(map[string]int),
+ }
+}
+
+func (pw *pkgEncoder) dump(out io.Writer) {
+ writeUint32 := func(x uint32) {
+ assert(binary.Write(out, binary.LittleEndian, x) == nil)
+ }
+
+ var sum uint32
+ for _, elems := range &pw.elems {
+ sum += uint32(len(elems))
+ writeUint32(sum)
+ }
+
+ sum = 0
+ for _, elems := range &pw.elems {
+ for _, elem := range elems {
+ sum += uint32(len(elem))
+ writeUint32(sum)
+ }
+ }
+
+ for _, elems := range &pw.elems {
+ for _, elem := range elems {
+ _, err := io.WriteString(out, elem)
+ assert(err == nil)
+ }
+ }
+}
+
+func (pw *pkgEncoder) stringIdx(s string) int {
+ if idx, ok := pw.stringsIdx[s]; ok {
+ assert(pw.elems[relocString][idx] == s)
+ return idx
+ }
+
+ idx := len(pw.elems[relocString])
+ pw.elems[relocString] = append(pw.elems[relocString], s)
+ pw.stringsIdx[s] = idx
+ return idx
+}
+
+func (pw *pkgEncoder) newEncoder(k reloc, marker syncMarker) encoder {
+ e := pw.newEncoderRaw(k)
+ e.sync(marker)
+ return e
+}
+
+func (pw *pkgEncoder) newEncoderRaw(k reloc) encoder {
+ idx := len(pw.elems[k])
+ pw.elems[k] = append(pw.elems[k], "") // placeholder
+
+ return encoder{
+ p: pw,
+ k: k,
+ idx: idx,
+ }
+}
+
+// Encoders
+
+type encoder struct {
+ p *pkgEncoder
+
+ relocs []relocEnt
+ data bytes.Buffer
+
+ encodingRelocHeader bool
+
+ k reloc
+ idx int
+}
+
+func (w *encoder) flush() int {
+ var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
+
+ // Backup the data so we write the relocations at the front.
+ var tmp bytes.Buffer
+ io.Copy(&tmp, &w.data)
+
+ // TODO(mdempsky): Consider writing these out separately so they're
+ // easier to strip, along with function bodies, so that we can prune
+ // down to just the data that's relevant to go/types.
+ if w.encodingRelocHeader {
+ base.Fatalf("encodingRelocHeader already true; recursive flush?")
+ }
+ w.encodingRelocHeader = true
+ w.sync(syncRelocs)
+ w.len(len(w.relocs))
+ for _, rent := range w.relocs {
+ w.sync(syncReloc)
+ w.len(int(rent.kind))
+ w.len(rent.idx)
+ }
+
+ io.Copy(&sb, &w.data)
+ io.Copy(&sb, &tmp)
+ w.p.elems[w.k][w.idx] = sb.String()
+
+ return w.idx
+}
+
+func (w *encoder) checkErr(err error) {
+ if err != nil {
+ base.Fatalf("unexpected error: %v", err)
+ }
+}
+
+func (w *encoder) rawUvarint(x uint64) {
+ var buf [binary.MaxVarintLen64]byte
+ n := binary.PutUvarint(buf[:], x)
+ _, err := w.data.Write(buf[:n])
+ w.checkErr(err)
+}
+
+func (w *encoder) rawVarint(x int64) {
+ // Zig-zag encode.
+ ux := uint64(x) << 1
+ if x < 0 {
+ ux = ^ux
+ }
+
+ w.rawUvarint(ux)
+}
+
+func (w *encoder) rawReloc(r reloc, idx int) int {
+ // TODO(mdempsky): Use map for lookup.
+ for i, rent := range w.relocs {
+ if rent.kind == r && rent.idx == idx {
+ return i
+ }
+ }
+
+ i := len(w.relocs)
+ w.relocs = append(w.relocs, relocEnt{r, idx})
+ return i
+}
+
+func (w *encoder) sync(m syncMarker) {
+ if !enableSync {
+ return
+ }
+
+ // Writing out stack frame string references requires working
+ // relocations, but writing out the relocations themselves involves
+ // sync markers. To prevent infinite recursion, we simply trim the
+ // stack frame for sync markers within the relocation header.
+ var frames []string
+ if !w.encodingRelocHeader && base.Debug.SyncFrames > 0 {
+ pcs := make([]uintptr, base.Debug.SyncFrames)
+ n := runtime.Callers(2, pcs)
+ frames = fmtFrames(pcs[:n]...)
+ }
+
+ // TODO(mdempsky): Save space by writing out stack frames as a
+ // linked list so we can share common stack frames.
+ w.rawUvarint(uint64(m))
+ w.rawUvarint(uint64(len(frames)))
+ for _, frame := range frames {
+ w.rawUvarint(uint64(w.rawReloc(relocString, w.p.stringIdx(frame))))
+ }
+}
+
+func (w *encoder) bool(b bool) bool {
+ w.sync(syncBool)
+ var x byte
+ if b {
+ x = 1
+ }
+ err := w.data.WriteByte(x)
+ w.checkErr(err)
+ return b
+}
+
+func (w *encoder) int64(x int64) {
+ w.sync(syncInt64)
+ w.rawVarint(x)
+}
+
+func (w *encoder) uint64(x uint64) {
+ w.sync(syncUint64)
+ w.rawUvarint(x)
+}
+
+func (w *encoder) len(x int) { assert(x >= 0); w.uint64(uint64(x)) }
+func (w *encoder) int(x int) { w.int64(int64(x)) }
+func (w *encoder) uint(x uint) { w.uint64(uint64(x)) }
+
+func (w *encoder) reloc(r reloc, idx int) {
+ w.sync(syncUseReloc)
+ w.len(w.rawReloc(r, idx))
+}
+
+func (w *encoder) code(c code) {
+ w.sync(c.marker())
+ w.len(c.value())
+}
+
+func (w *encoder) string(s string) {
+ w.sync(syncString)
+ w.reloc(relocString, w.p.stringIdx(s))
+}
+
+func (w *encoder) strings(ss []string) {
+ w.len(len(ss))
+ for _, s := range ss {
+ w.string(s)
+ }
+}
+
+func (w *encoder) value(val constant.Value) {
+ w.sync(syncValue)
+ if w.bool(val.Kind() == constant.Complex) {
+ w.scalar(constant.Real(val))
+ w.scalar(constant.Imag(val))
+ } else {
+ w.scalar(val)
+ }
+}
+
+func (w *encoder) scalar(val constant.Value) {
+ switch v := constant.Val(val).(type) {
+ default:
+ panic(fmt.Sprintf("unhandled %v (%v)", val, val.Kind()))
+ case bool:
+ w.code(valBool)
+ w.bool(v)
+ case string:
+ w.code(valString)
+ w.string(v)
+ case int64:
+ w.code(valInt64)
+ w.int64(v)
+ case *big.Int:
+ w.code(valBigInt)
+ w.bigInt(v)
+ case *big.Rat:
+ w.code(valBigRat)
+ w.bigInt(v.Num())
+ w.bigInt(v.Denom())
+ case *big.Float:
+ w.code(valBigFloat)
+ w.bigFloat(v)
+ }
+}
+
+func (w *encoder) bigInt(v *big.Int) {
+ b := v.Bytes()
+ w.string(string(b)) // TODO: More efficient encoding.
+ w.bool(v.Sign() < 0)
+}
+
+func (w *encoder) bigFloat(v *big.Float) {
+ b := v.Append(nil, 'p', -1)
+ w.string(string(b)) // TODO: More efficient encoding.
+}
diff --git a/src/cmd/compile/internal/noder/export.go b/src/cmd/compile/internal/noder/export.go
new file mode 100644
index 0000000000000000000000000000000000000000..1a296e22c8489b803fedf0ed2965daa1fe8be5c1
--- /dev/null
+++ b/src/cmd/compile/internal/noder/export.go
@@ -0,0 +1,65 @@
+// 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 noder
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/typecheck"
+ "cmd/internal/bio"
+)
+
+// writeNewExportFunc is a hook that can be added to append extra
+// export data after the normal export data section. It allows
+// experimenting with new export data format designs without requiring
+// immediate support in the go/internal or x/tools importers.
+var writeNewExportFunc func(out io.Writer)
+
+func WriteExports(out *bio.Writer) {
+ // When unified IR exports are enable, we simply append it to the
+ // end of the normal export data (with compiler extensions
+ // disabled), and write an extra header giving its size.
+ //
+ // If the compiler sees this header, it knows to read the new data
+ // instead; meanwhile the go/types importers will silently ignore it
+ // and continue processing the old export instead.
+ //
+ // This allows us to experiment with changes to the new export data
+ // format without needing to update the go/internal/gcimporter or
+ // (worse) x/tools/go/gcexportdata.
+
+ useNewExport := writeNewExportFunc != nil
+
+ var old, new bytes.Buffer
+
+ typecheck.WriteExports(&old, !useNewExport)
+
+ if useNewExport {
+ writeNewExportFunc(&new)
+ }
+
+ oldLen := old.Len()
+ newLen := new.Len()
+
+ if useNewExport {
+ fmt.Fprintf(out, "\nnewexportsize %v\n", newLen)
+ }
+
+ // The linker also looks for the $$ marker - use char after $$ to distinguish format.
+ out.WriteString("\n$$B\n") // indicate binary export format
+ io.Copy(out, &old)
+ out.WriteString("\n$$\n")
+ io.Copy(out, &new)
+
+ if base.Debug.Export != 0 {
+ fmt.Printf("BenchmarkExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, oldLen)
+ if useNewExport {
+ fmt.Printf("BenchmarkNewExportSize:%s 1 %d bytes\n", base.Ctxt.Pkgpath, newLen)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index c7695ed920435291bab7c64a0cbfdfb08b8b8a7e..6891d1ec30dac2244a87ebe494ace67058b2f611 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -5,6 +5,8 @@
package noder
import (
+ "fmt"
+
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
@@ -15,6 +17,8 @@ import (
)
func (g *irgen) expr(expr syntax.Expr) ir.Node {
+ expr = unparen(expr) // skip parens; unneeded after parse+typecheck
+
if expr == nil {
return nil
}
@@ -46,6 +50,8 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
}
+ base.Assert(g.exprStmtOK)
+
// The gc backend expects all expressions to have a concrete type, and
// types2 mostly satisfies this expectation already. But there are a few
// cases where the Go spec doesn't require converting to concrete type,
@@ -67,14 +73,16 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
// Constant expression.
if tv.Value != nil {
- return Const(g.pos(expr), g.typ(typ), tv.Value)
+ typ := g.typ(typ)
+ value := FixValue(typ, tv.Value)
+ return OrigConst(g.pos(expr), typ, value, constExprOp(expr), syntax.String(expr))
}
n := g.expr0(typ, expr)
if n.Typecheck() != 1 && n.Typecheck() != 3 {
base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n)
}
- if !g.match(n.Type(), typ, tv.HasOk()) {
+ if n.Op() != ir.OFUNCINST && !g.match(n.Type(), typ, tv.HasOk()) {
base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ)
}
return n
@@ -82,6 +90,11 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
pos := g.pos(expr)
+ assert(pos.IsKnown())
+
+ // Set base.Pos for transformation code that still uses base.Pos, rather than
+ // the pos of the node being converted.
+ base.Pos = pos
switch expr := expr.(type) {
case *syntax.Name:
@@ -101,68 +114,27 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
case *syntax.CallExpr:
fun := g.expr(expr.Fun)
-
- // The key for the Inferred map is the CallExpr (if inferring
- // types required the function arguments) or the IndexExpr below
- // (if types could be inferred without the function arguments).
- if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 {
- // This is the case where inferring types required the
- // types of the function arguments.
- targs := make([]ir.Node, len(inferred.Targs))
- for i, targ := range inferred.Targs {
- targs[i] = ir.TypeNode(g.typ(targ))
- }
- if fun.Op() == ir.OFUNCINST {
- // Replace explicit type args with the full list that
- // includes the additional inferred type args
- fun.(*ir.InstExpr).Targs = targs
- } else {
- // Create a function instantiation here, given
- // there are only inferred type args (e.g.
- // min(5,6), where min is a generic function)
- inst := ir.NewInstExpr(pos, ir.OFUNCINST, fun, targs)
- typed(fun.Type(), inst)
- fun = inst
- }
-
- }
return Call(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots)
case *syntax.IndexExpr:
- var targs []ir.Node
-
- if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 {
- // This is the partial type inference case where the types
- // can be inferred from other type arguments without using
- // the types of the function arguments.
- targs = make([]ir.Node, len(inferred.Targs))
- for i, targ := range inferred.Targs {
- targs[i] = ir.TypeNode(g.typ(targ))
- }
- } else if _, ok := expr.Index.(*syntax.ListExpr); ok {
- targs = g.exprList(expr.Index)
- } else {
- index := g.expr(expr.Index)
- if index.Op() != ir.OTYPE {
+ args := unpackListExpr(expr.Index)
+ if len(args) == 1 {
+ tv, ok := g.info.Types[args[0]]
+ assert(ok)
+ if tv.IsValue() {
// This is just a normal index expression
- return Index(pos, g.typ(typ), g.expr(expr.X), index)
+ n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0]))
+ if !g.delayTransform() {
+ // transformIndex will modify n.Type() for OINDEXMAP.
+ transformIndex(n)
+ }
+ return n
}
- // This is generic function instantiation with a single type
- targs = []ir.Node{index}
}
- // This is a generic function instantiation (e.g. min[int]).
- // Generic type instantiation is handled in the type
- // section of expr() above (using g.typ).
- x := g.expr(expr.X)
- if x.Op() != ir.ONAME || x.Type().Kind() != types.TFUNC {
- panic("Incorrect argument for generic func instantiation")
- }
- n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
- typed(g.typ(typ), n)
- return n
- case *syntax.ParenExpr:
- return g.expr(expr.X) // skip parens; unneeded after parse+typecheck
+ // expr.Index is a list of type args, so we ignore it, since types2 has
+ // already provided this info with the Info.Instances map.
+ return g.expr(expr.X)
case *syntax.SelectorExpr:
// Qualified identifier.
@@ -174,17 +146,37 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
return g.selectorExpr(pos, typ, expr)
case *syntax.SliceExpr:
- return Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
+ n := Slice(pos, g.typ(typ), g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
+ if !g.delayTransform() {
+ transformSlice(n)
+ }
+ return n
case *syntax.Operation:
if expr.Y == nil {
- return Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X))
+ n := Unary(pos, g.typ(typ), g.op(expr.Op, unOps[:]), g.expr(expr.X))
+ if n.Op() == ir.OADDR && !g.delayTransform() {
+ transformAddr(n.(*ir.AddrExpr))
+ }
+ return n
}
switch op := g.op(expr.Op, binOps[:]); op {
case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
- return Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
+ n := Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
+ if !g.delayTransform() {
+ transformCompare(n)
+ }
+ return n
+ case ir.OANDAND, ir.OOROR:
+ x := g.expr(expr.X)
+ y := g.expr(expr.Y)
+ return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
default:
- return Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y))
+ n := Binary(pos, op, g.typ(typ), g.expr(expr.X), g.expr(expr.Y))
+ if op == ir.OADD && !g.delayTransform() {
+ return transformAdd(n)
+ }
+ return n
}
default:
@@ -193,7 +185,28 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
}
}
-// selectorExpr resolves the choice of ODOT, ODOTPTR, OCALLPART (eventually
+// substType does a normal type substition, but tparams is in the form of a field
+// list, and targs is in terms of a slice of type nodes. substType records any newly
+// instantiated types into g.instTypeList.
+func (g *irgen) substType(typ *types.Type, tparams *types.Type, targs []ir.Node) *types.Type {
+ fields := tparams.FieldSlice()
+ tparams1 := make([]*types.Type, len(fields))
+ for i, f := range fields {
+ tparams1[i] = f.Type
+ }
+ targs1 := make([]*types.Type, len(targs))
+ for i, n := range targs {
+ targs1[i] = n.Type()
+ }
+ ts := typecheck.Tsubster{
+ Tparams: tparams1,
+ Targs: targs1,
+ }
+ newt := ts.Typ(typ)
+ return newt
+}
+
+// selectorExpr resolves the choice of ODOT, ODOTPTR, OMETHVALUE (eventually
// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather
// than in typecheck.go.
func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node {
@@ -222,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)
@@ -259,24 +266,26 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
if wantPtr {
recvType2Base = types2.AsPointer(recvType2).Elem()
}
- if len(types2.AsNamed(recvType2Base).TParams()) > 0 {
+ if recvType2Base.(*types2.Named).TypeParams().Len() > 0 {
// recvType2 is the original generic type that is
// instantiated for this method call.
// selinfo.Recv() is the instantiated type
recvType2 = recvType2Base
- // method is the generic method associated with the gen type
- method := g.obj(types2.AsNamed(recvType2).Method(last))
- n = ir.NewSelectorExpr(pos, ir.OCALLPART, x, method.Sym())
+ recvTypeSym := g.pkg(method2.Pkg()).Lookup(recvType2.(*types2.Named).Obj().Name())
+ recvType := recvTypeSym.Def.(*ir.Name).Type()
+ // method is the generic method associated with
+ // the base generic type. The instantiated type may not
+ // have method bodies filled in, if it was imported.
+ method := recvType.Methods().Index(last).Nname.(*ir.Name)
+ n = ir.NewSelectorExpr(pos, ir.OMETHVALUE, x, typecheck.Lookup(expr.Sel.Value))
n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type())
n.(*ir.SelectorExpr).Selection.Nname = method
typed(method.Type(), n)
- // selinfo.Targs() are the types used to
- // instantiate the type of receiver
- targs2 := getTargs(selinfo)
- targs := make([]ir.Node, len(targs2))
- for i, targ2 := range targs2 {
- targs[i] = ir.TypeNode(g.typ(targ2))
+ xt := deref(x.Type())
+ targs := make([]ir.Node, len(xt.RParams()))
+ for i := range targs {
+ targs[i] = ir.TypeNode(xt.RParams()[i])
}
// Create function instantiation with the type
@@ -299,27 +308,18 @@ func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.Selecto
return n
}
-// getTargs gets the targs associated with the receiver of a selected method
-func getTargs(selinfo *types2.Selection) []types2.Type {
- r := selinfo.Recv()
- if p := types2.AsPointer(r); p != nil {
- r = p.Elem()
- }
- n := types2.AsNamed(r)
- if n == nil {
- base.Fatalf("Incorrect type for selinfo %v", selinfo)
- }
- return n.TArgs()
+func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
+ return g.exprs(unpackListExpr(expr))
}
-func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
+func unpackListExpr(expr syntax.Expr) []syntax.Expr {
switch expr := expr.(type) {
case nil:
return nil
case *syntax.ListExpr:
- return g.exprs(expr.ElemList)
+ return expr.ElemList
default:
- return []ir.Node{g.expr(expr)}
+ return []syntax.Expr{expr}
}
}
@@ -338,41 +338,50 @@ 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.StructuralType(typ).(*types2.Struct)
exprs := make([]ir.Node, len(lit.ElemList))
for i, elem := range lit.ElemList {
switch elem := elem.(type) {
case *syntax.KeyValueExpr:
+ var key ir.Node
if isStruct {
- exprs[i] = ir.NewStructKeyExpr(g.pos(elem), g.name(elem.Key.(*syntax.Name)), g.expr(elem.Value))
+ key = ir.NewIdent(g.pos(elem.Key), g.name(elem.Key.(*syntax.Name)))
} else {
- exprs[i] = ir.NewKeyExpr(g.pos(elem), g.expr(elem.Key), g.expr(elem.Value))
+ key = g.expr(elem.Key)
}
+ value := wrapname(g.pos(elem.Value), g.expr(elem.Value))
+ if value.Op() == ir.OPAREN {
+ // Make sure any PAREN node added by wrapper has a type
+ typed(value.(*ir.ParenExpr).X.Type(), value)
+ }
+ exprs[i] = ir.NewKeyExpr(g.pos(elem), key, value)
default:
- exprs[i] = g.expr(elem)
+ exprs[i] = wrapname(g.pos(elem), g.expr(elem))
+ if exprs[i].Op() == ir.OPAREN {
+ // Make sure any PAREN node added by wrapper has a type
+ typed(exprs[i].(*ir.ParenExpr).X.Type(), exprs[i])
+ }
}
}
n := ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, nil, exprs)
typed(g.typ(typ), n)
- return transformCompLit(n)
+ var r ir.Node = n
+ if !g.delayTransform() {
+ r = transformCompLit(n)
+ }
+ return r
}
func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
- fn := ir.NewFunc(g.pos(expr))
- fn.SetIsHiddenClosure(ir.CurFunc != nil)
+ fn := ir.NewClosureFunc(g.pos(expr), ir.CurFunc != nil)
+ ir.NameClosure(fn.OClosure, ir.CurFunc)
- fn.Nname = ir.NewNameAt(g.pos(expr), typecheck.ClosureName(ir.CurFunc))
- ir.MarkFunc(fn.Nname)
typ := g.typ(typ2)
- fn.Nname.Func = fn
- fn.Nname.Defn = fn
typed(typ, fn.Nname)
- fn.SetTypecheck(1)
-
- fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn)
typed(typ, fn.OClosure)
+ fn.SetTypecheck(1)
g.funcBody(fn, nil, expr.Type, expr.Body)
@@ -386,9 +395,14 @@ func (g *irgen) funcLit(typ2 types2.Type, expr *syntax.FuncLit) ir.Node {
cv.SetWalkdef(1)
}
- g.target.Decls = append(g.target.Decls, fn)
-
- return fn.OClosure
+ if g.topFuncIsGeneric {
+ // Don't add any closure inside a generic function/method to the
+ // g.target.Decls list, even though it may not be generic itself.
+ // See issue #47514.
+ return ir.UseClosure(fn.OClosure, nil)
+ } else {
+ return ir.UseClosure(fn.OClosure, g.target)
+ }
}
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
@@ -398,3 +412,35 @@ func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
}
return n.Type()
}
+
+// constExprOp returns an ir.Op that represents the outermost
+// operation of the given constant expression. It's intended for use
+// with ir.RawOrigExpr.
+func constExprOp(expr syntax.Expr) ir.Op {
+ switch expr := expr.(type) {
+ default:
+ panic(fmt.Sprintf("%s: unexpected expression: %T", expr.Pos(), expr))
+
+ case *syntax.BasicLit:
+ return ir.OLITERAL
+ case *syntax.Name, *syntax.SelectorExpr:
+ return ir.ONAME
+ case *syntax.CallExpr:
+ return ir.OCALL
+ case *syntax.Operation:
+ if expr.Y == nil {
+ return unOps[expr.Op]
+ }
+ return binOps[expr.Op]
+ }
+}
+
+func unparen(expr syntax.Expr) syntax.Expr {
+ for {
+ paren, ok := expr.(*syntax.ParenExpr)
+ if !ok {
+ return expr
+ }
+ expr = paren.X
+ }
+}
diff --git a/src/cmd/compile/internal/noder/frames_go1.go b/src/cmd/compile/internal/noder/frames_go1.go
new file mode 100644
index 0000000000000000000000000000000000000000..d00e0f51f9f8f8d4fda8dc9fe3a26d6b8fb13332
--- /dev/null
+++ b/src/cmd/compile/internal/noder/frames_go1.go
@@ -0,0 +1,21 @@
+// 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.
+
+//go:build !go1.7
+// +build !go1.7
+
+// TODO(mdempsky): Remove after #44505 is resolved
+
+package noder
+
+import "runtime"
+
+func walkFrames(pcs []uintptr, visit frameVisitor) {
+ for _, pc := range pcs {
+ fn := runtime.FuncForPC(pc)
+ file, line := fn.FileLine(pc)
+
+ visit(file, line, fn.Name(), pc-fn.Entry())
+ }
+}
diff --git a/src/cmd/compile/internal/noder/frames_go17.go b/src/cmd/compile/internal/noder/frames_go17.go
new file mode 100644
index 0000000000000000000000000000000000000000..48d77625b41d11105e73f218164c3c3395832574
--- /dev/null
+++ b/src/cmd/compile/internal/noder/frames_go17.go
@@ -0,0 +1,25 @@
+// 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.
+
+//go:build go1.7
+// +build go1.7
+
+package noder
+
+import "runtime"
+
+func walkFrames(pcs []uintptr, visit frameVisitor) {
+ if len(pcs) == 0 {
+ return
+ }
+
+ frames := runtime.CallersFrames(pcs)
+ for {
+ frame, more := frames.Next()
+ visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry)
+ if !more {
+ return
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/noder/func.go b/src/cmd/compile/internal/noder/func.go
index 702138157c53b8186760e616c9541aa0e90670d6..6077b348a5c73e6bc123ebb2091d20212ada72df 100644
--- a/src/cmd/compile/internal/noder/func.go
+++ b/src/cmd/compile/internal/noder/func.go
@@ -37,8 +37,7 @@ func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType,
// calculated its size, including parameter offsets. Now that we've
// created the parameter Names, force a recalculation to ensure
// their offsets are correct.
- typ.Align = 0
- types.CalcSize(typ)
+ types.RecalcSize(typ)
if block != nil {
typecheck.DeclContext = ir.PAUTO
diff --git a/src/cmd/compile/internal/noder/helpers.go b/src/cmd/compile/internal/noder/helpers.go
index 9da0e493007a0687899eb59fe64f2eaa03d95e0c..5524673e6679666f2befb111f8a10ec3af680c85 100644
--- a/src/cmd/compile/internal/noder/helpers.go
+++ b/src/cmd/compile/internal/noder/helpers.go
@@ -43,6 +43,32 @@ func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
return typed(typ, ir.NewBasicLit(pos, val))
}
+func OrigConst(pos src.XPos, typ *types.Type, val constant.Value, op ir.Op, raw string) ir.Node {
+ orig := ir.NewRawOrigExpr(pos, op, raw)
+ return ir.NewConstExpr(val, typed(typ, orig))
+}
+
+// FixValue returns val after converting and truncating it as
+// appropriate for typ.
+func FixValue(typ *types.Type, val constant.Value) constant.Value {
+ assert(typ.Kind() != types.TFORW)
+ switch {
+ case typ.IsInteger():
+ val = constant.ToInt(val)
+ case typ.IsFloat():
+ val = constant.ToFloat(val)
+ case typ.IsComplex():
+ val = constant.ToComplex(val)
+ }
+ if !typ.IsUntyped() {
+ val = typecheck.DefaultLit(ir.NewBasicLit(src.NoXPos, val), typ).Val()
+ }
+ if !typ.IsTypeParam() {
+ ir.AssertValidTypeForConst(typ, val)
+ }
+ return val
+}
+
func Nil(pos src.XPos, typ *types.Type) ir.Node {
return typed(typ, ir.NewNilExpr(pos))
}
@@ -51,10 +77,6 @@ func Nil(pos src.XPos, typ *types.Type) ir.Node {
func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
n := typecheck.NodAddrAt(pos, x)
- switch x.Op() {
- case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
- n.SetOp(ir.OPTRLIT)
- }
typed(types.NewPtr(x.Type()), n)
return n
}
@@ -63,39 +85,31 @@ func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
}
-func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) ir.Node {
+func Binary(pos src.XPos, op ir.Op, typ *types.Type, x, y ir.Node) *ir.BinaryExpr {
switch op {
- case ir.OANDAND, ir.OOROR:
- return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
case ir.OADD:
n := ir.NewBinaryExpr(pos, op, x, y)
- if x.Type().HasTParam() || y.Type().HasTParam() {
- // Delay transformAdd() if either arg has a type param,
- // since it needs to know the exact types to decide whether
- // to transform OADD to OADDSTR.
- n.SetType(typ)
- n.SetTypecheck(3)
- return n
- }
typed(typ, n)
- return transformAdd(n)
+ return n
default:
- return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
+ n := ir.NewBinaryExpr(pos, op, x, y)
+ typed(x.Type(), n)
+ return n
}
}
func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
n.IsDDD = dots
- // n.Use will be changed to ir.CallUseStmt in g.stmt() if this call is
- // just a statement (any return values are ignored).
- n.Use = ir.CallUseExpr
if fun.Op() == ir.OTYPE {
// Actually a type conversion, not a function call.
- if fun.Type().HasTParam() || args[0].Type().HasTParam() {
- // For type params, don't typecheck until we actually know
- // the type.
+ if !fun.Type().IsInterface() &&
+ (fun.Type().HasTParam() || args[0].Type().HasTParam()) {
+ // For type params, we can transform if fun.Type() is known
+ // to be an interface (in which case a CONVIFACE node will be
+ // inserted). Otherwise, don't typecheck until we actually
+ // know the type.
return typed(typ, n)
}
typed(typ, n)
@@ -103,24 +117,27 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
}
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
- // For Builtin ops, we currently stay with using the old
- // typechecker to transform the call to a more specific expression
- // and possibly use more specific ops. However, for a bunch of the
- // ops, we delay doing the old typechecker if any of the args have
- // type params, for a variety of reasons:
- //
- // OMAKE: hard to choose specific ops OMAKESLICE, etc. until arg type is known
- // OREAL/OIMAG: can't determine type float32/float64 until arg type know
- // OLEN/OCAP: old typechecker will complain if arg is not obviously a slice/array.
- // OAPPEND: old typechecker will complain if arg is not obviously slice, etc.
+ // For most Builtin ops, we delay doing transformBuiltin if any of the
+ // args have type params, for a variety of reasons:
//
- // We will eventually break out the transforming functionality
- // needed for builtin's, and call it here or during stenciling, as
- // appropriate.
+ // OMAKE: transformMake can't choose specific ops OMAKESLICE, etc.
+ // until arg type is known
+ // OREAL/OIMAG: transformRealImag can't determine type float32/float64
+ // until arg type known
+ // OAPPEND: transformAppend requires that the arg is a slice
+ // ODELETE: transformDelete requires that the arg is a map
+ // OALIGNOF, OSIZEOF: can be eval'ed to a constant until types known.
switch fun.BuiltinOp {
- case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
+ case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
hasTParam := false
for _, arg := range args {
+ if fun.BuiltinOp == ir.OOFFSETOF {
+ // It's the type of left operand of the
+ // selection that matters, not the type of
+ // the field itself (which is irrelevant for
+ // offsetof).
+ arg = arg.(*ir.SelectorExpr).X
+ }
if arg.Type().HasTParam() {
hasTParam = true
break
@@ -137,10 +154,8 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
// Add information, now that we know that fun is actually being called.
switch fun := fun.(type) {
- case *ir.ClosureExpr:
- fun.Func.SetClosureCalled(true)
case *ir.SelectorExpr:
- if fun.Op() == ir.OCALLPART {
+ if fun.Op() == ir.OMETHVALUE {
op := ir.ODOTMETH
if fun.X.Type().IsInterface() {
op = ir.ODOTINTER
@@ -152,49 +167,29 @@ func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool)
}
}
- if fun.Type().HasTParam() {
- // If the fun arg is or has a type param, don't do any extra
+ if fun.Type().HasTParam() || fun.Op() == ir.OXDOT || fun.Op() == ir.OFUNCINST {
+ // If the fun arg is or has a type param, we can't do all the
// transformations, since we may not have needed properties yet
- // (e.g. number of return values, etc). The type param is probably
- // described by a structural constraint that requires it to be a
- // certain function type, etc., but we don't want to analyze that.
+ // (e.g. number of return values, etc). The same applies if a fun
+ // which is an XDOT could not be transformed yet because of a generic
+ // type in the X of the selector expression.
+ //
+ // A function instantiation (even if fully concrete) shouldn't be
+ // transformed yet, because we need to add the dictionary during the
+ // transformation.
return typed(typ, n)
}
- if fun.Op() == ir.OXDOT {
- if !fun.(*ir.SelectorExpr).X.Type().HasTParam() {
- base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
- }
- // For methods called in a generic function, don't do any extra
- // transformations. We will do those later when we create the
- // instantiated function and have the correct receiver type.
- typed(typ, n)
- return n
- }
- if fun.Op() != ir.OFUNCINST {
- // If no type params, do the normal call transformations. This
- // will convert OCALL to OCALLFUNC.
- typed(typ, n)
- transformCall(n)
- return n
- }
-
- // Leave the op as OCALL, which indicates the call still needs typechecking.
+ // If no type params, do the normal call transformations. This
+ // will convert OCALL to OCALLFUNC.
typed(typ, n)
+ transformCall(n)
return n
}
-func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
+func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) *ir.BinaryExpr {
n := ir.NewBinaryExpr(pos, op, x, y)
- if x.Type().HasTParam() || y.Type().HasTParam() {
- // Delay transformCompare() if either arg has a type param, since
- // it needs to know the exact types to decide on any needed conversions.
- n.SetType(typ)
- n.SetTypecheck(3)
- return n
- }
typed(typ, n)
- transformCompare(n)
return n
}
@@ -225,7 +220,7 @@ func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
// Method value.
typ := typecheck.NewMethodType(method.Type, nil)
- return dot(pos, typ, ir.OCALLPART, x, method)
+ return dot(pos, typ, ir.OMETHVALUE, x, method)
}
// MethodExpr returns a OMETHEXPR node with the indicated index into the methods
@@ -264,34 +259,19 @@ func method(typ *types.Type, index int) *types.Field {
return types.ReceiverBaseType(typ).Methods().Index(index)
}
-func Index(pos src.XPos, typ *types.Type, x, index ir.Node) ir.Node {
+func Index(pos src.XPos, typ *types.Type, x, index ir.Node) *ir.IndexExpr {
n := ir.NewIndexExpr(pos, x, index)
- if x.Type().HasTParam() {
- // transformIndex needs to know exact type
- n.SetType(typ)
- n.SetTypecheck(3)
- return n
- }
typed(typ, n)
- // transformIndex will modify n.Type() for OINDEXMAP.
- transformIndex(n)
return n
}
-func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) ir.Node {
+func Slice(pos src.XPos, typ *types.Type, x, low, high, max ir.Node) *ir.SliceExpr {
op := ir.OSLICE
if max != nil {
op = ir.OSLICE3
}
n := ir.NewSliceExpr(pos, op, x, low, high, max)
- if x.Type().HasTParam() {
- // transformSlice needs to know if x.Type() is a string or an array or a slice.
- n.SetType(typ)
- n.SetTypecheck(3)
- return n
- }
typed(typ, n)
- transformSlice(n)
return n
}
@@ -321,5 +301,15 @@ var one = constant.MakeInt64(1)
func IncDec(pos src.XPos, op ir.Op, x ir.Node) *ir.AssignOpStmt {
assert(x.Type() != nil)
- return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
+ bl := ir.NewBasicLit(pos, one)
+ if x.Type().HasTParam() {
+ // If the operand is generic, then types2 will have proved it must be
+ // a type that fits with increment/decrement, so just set the type of
+ // "one" to n.Type(). This works even for types that are eventually
+ // float or complex.
+ typed(x.Type(), bl)
+ } else {
+ bl = typecheck.DefaultLit(bl, x.Type())
+ }
+ return ir.NewAssignOpStmt(pos, op, x, bl)
}
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index 701e9001c859ea8d2819b0e2850a1604e8e0c978..58dffbad1e4d5c0c11850383ea5e391d9d6ea59d 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"internal/buildcfg"
- "io"
"os"
pathpkg "path"
"runtime"
@@ -32,8 +31,24 @@ import (
"cmd/internal/src"
)
-// Temporary import helper to get type2-based type-checking going.
+// haveLegacyImports records whether we've imported any packages
+// without a new export data section. This is useful for experimenting
+// with new export data format designs, when you need to support
+// existing tests that manually compile files with inconsistent
+// compiler flags.
+var haveLegacyImports = false
+
+// newReadImportFunc is an extension hook for experimenting with new
+// export data formats. If a new export data payload was written out
+// for an imported package by overloading writeNewExportFunc, then
+// that payload will be mapped into memory and passed to
+// newReadImportFunc.
+var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+ panic("unexpected new export data payload")
+}
+
type gcimports struct {
+ ctxt *types2.Context
packages map[string]*types2.Package
}
@@ -46,13 +61,8 @@ func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*ty
panic("mode must be 0")
}
- path, err := resolveImportPath(path)
- if err != nil {
- return nil, err
- }
-
- lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
- return importer.Import(m.packages, path, srcDir, lookup)
+ _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages)
+ return pkg, err
}
func isDriveLetter(b byte) bool {
@@ -117,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 {
@@ -175,160 +187,242 @@ func resolveImportPath(path string) (string, error) {
return path, nil
}
-// TODO(mdempsky): Return an error instead.
func importfile(decl *syntax.ImportDecl) *types.Pkg {
- if decl.Path.Kind != syntax.StringLit {
- base.Errorf("import path must be a string")
+ path, err := parseImportPath(decl.Path)
+ if err != nil {
+ base.Errorf("%s", err)
return nil
}
- path, err := strconv.Unquote(decl.Path.Value)
+ pkg, _, err := readImportFile(path, typecheck.Target, nil, nil)
if err != nil {
- base.Errorf("import path must be a string")
+ base.Errorf("%s", err)
return nil
}
+ if pkg != types.UnsafePkg && pkg.Height >= myheight {
+ myheight = pkg.Height + 1
+ }
+ return pkg
+}
+
+func parseImportPath(pathLit *syntax.BasicLit) (string, error) {
+ if pathLit.Kind != syntax.StringLit {
+ return "", errors.New("import path must be a string")
+ }
+
+ path, err := strconv.Unquote(pathLit.Value)
+ if err != nil {
+ return "", errors.New("import path must be a string")
+ }
+
if err := checkImportPath(path, false); err != nil {
- base.Errorf("%s", err.Error())
- return nil
+ return "", err
}
+ return path, err
+}
+
+// readImportFile reads the import file for the given package path and
+// returns its types.Pkg representation. If packages is non-nil, the
+// types2.Package representation is also returned.
+func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
path, err = resolveImportPath(path)
if err != nil {
- base.Errorf("%s", err)
- return nil
+ return
+ }
+
+ if path == "unsafe" {
+ pkg1, pkg2 = types.UnsafePkg, types2.Unsafe
+
+ // TODO(mdempsky): Investigate if this actually matters. Why would
+ // the linker or runtime care whether a package imported unsafe?
+ if !pkg1.Direct {
+ pkg1.Direct = true
+ target.Imports = append(target.Imports, pkg1)
+ }
+
+ return
}
- importpkg := types.NewPkg(path, "")
- if importpkg.Direct {
- return importpkg // already fully loaded
+ pkg1 = types.NewPkg(path, "")
+ if packages != nil {
+ pkg2 = packages[path]
+ assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete()))
}
- importpkg.Direct = true
- typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
- if path == "unsafe" {
- return importpkg // initialized with universe
+ if pkg1.Direct {
+ return
}
+ pkg1.Direct = true
+ target.Imports = append(target.Imports, pkg1)
f, err := openPackage(path)
if err != nil {
- base.Errorf("could not import %q: %v", path, err)
- base.ErrorExit()
+ return
}
- imp := bio.NewReader(f)
- defer imp.Close()
- file := f.Name()
+ defer f.Close()
- // check object header
- p, err := imp.ReadString('\n')
+ r, end, newsize, err := findExportData(f)
if err != nil {
- base.Errorf("import %s: reading input: %v", file, err)
- base.ErrorExit()
+ return
}
- if p == "!\n" { // package archive
- // package export block should be first
- sz := archive.ReadHeader(imp.Reader, "__.PKGDEF")
- if sz <= 0 {
- base.Errorf("import %s: not a package file", file)
- base.ErrorExit()
- }
- p, err = imp.ReadString('\n')
+ if base.Debug.Export != 0 {
+ fmt.Printf("importing %s (%s)\n", path, f.Name())
+ }
+
+ if newsize != 0 {
+ // We have unified IR data. Map it, and feed to the importers.
+ end -= newsize
+ var data string
+ data, err = base.MapFile(r.File(), end, newsize)
if err != nil {
- base.Errorf("import %s: reading input: %v", file, err)
- base.ErrorExit()
+ return
}
- }
- if !strings.HasPrefix(p, "go object ") {
- base.Errorf("import %s: not a go object file: %s", file, p)
- base.ErrorExit()
- }
- q := objabi.HeaderString()
- if p != q {
- base.Errorf("import %s: object is [%s] expected [%s]", file, p, q)
- base.ErrorExit()
- }
+ pkg2, err = newReadImportFunc(data, pkg1, env, packages)
+ } else {
+ // We only have old data. Oh well, fall back to the legacy importers.
+ haveLegacyImports = true
- // process header lines
- for {
- p, err = imp.ReadString('\n')
+ var c byte
+ switch c, err = r.ReadByte(); {
+ case err != nil:
+ return
+
+ case c != 'i':
+ // Indexed format is distinguished by an 'i' byte,
+ // whereas previous export formats started with 'c', 'd', or 'v'.
+ err = fmt.Errorf("unexpected package format byte: %v", c)
+ return
+ }
+
+ pos := r.Offset()
+
+ // Map string (and data) section into memory as a single large
+ // string. This reduces heap fragmentation and allows
+ // returning individual substrings very efficiently.
+ var data string
+ data, err = base.MapFile(r.File(), pos, end-pos)
if err != nil {
- base.Errorf("import %s: reading input: %v", file, err)
- base.ErrorExit()
+ return
}
- if p == "\n" {
- break // header ends with blank line
+
+ typecheck.ReadImports(pkg1, data)
+
+ if packages != nil {
+ pkg2, err = importer.ImportData(packages, data, path)
+ if err != nil {
+ return
+ }
}
}
- // Expect $$B\n to signal binary import format.
+ err = addFingerprint(path, f, end)
+ return
+}
+
+// findExportData returns a *bio.Reader positioned at the start of the
+// binary export data section, and a file offset for where to stop
+// reading.
+func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) {
+ r = bio.NewReader(f)
+
+ // check object header
+ line, err := r.ReadString('\n')
+ if err != nil {
+ return
+ }
- // look for $$
- var c byte
- for {
- c, err = imp.ReadByte()
+ if line == "!\n" { // package archive
+ // package export block should be first
+ sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF"))
+ if sz <= 0 {
+ err = errors.New("not a package file")
+ return
+ }
+ end = r.Offset() + sz
+ line, err = r.ReadString('\n')
if err != nil {
- break
+ return
}
- if c == '$' {
- c, err = imp.ReadByte()
- if c == '$' || err != nil {
- break
- }
+ } else {
+ // Not an archive; provide end of file instead.
+ // TODO(mdempsky): I don't think this happens anymore.
+ var fi os.FileInfo
+ fi, err = f.Stat()
+ if err != nil {
+ return
}
+ end = fi.Size()
}
- // get character after $$
- if err == nil {
- c, _ = imp.ReadByte()
+ if !strings.HasPrefix(line, "go object ") {
+ err = fmt.Errorf("not a go object file: %s", line)
+ return
+ }
+ if expect := objabi.HeaderString(); line != expect {
+ err = fmt.Errorf("object is [%s] expected [%s]", line, expect)
+ return
}
- var fingerprint goobj.FingerprintType
- switch c {
- case '\n':
- base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
- return nil
-
- case 'B':
- if base.Debug.Export != 0 {
- fmt.Printf("importing %s (%s)\n", path, file)
+ // process header lines
+ for !strings.HasPrefix(line, "$$") {
+ if strings.HasPrefix(line, "newexportsize ") {
+ fields := strings.Fields(line)
+ newsize, err = strconv.ParseInt(fields[1], 10, 64)
+ if err != nil {
+ return
+ }
}
- imp.ReadByte() // skip \n after $$B
- c, err = imp.ReadByte()
+ line, err = r.ReadString('\n')
if err != nil {
- base.Errorf("import %s: reading input: %v", file, err)
- base.ErrorExit()
+ return
}
+ }
- // Indexed format is distinguished by an 'i' byte,
- // whereas previous export formats started with 'c', 'd', or 'v'.
- if c != 'i' {
- base.Errorf("import %s: unexpected package format byte: %v", file, c)
- base.ErrorExit()
- }
- fingerprint = typecheck.ReadImports(importpkg, imp)
+ // Expect $$B\n to signal binary import format.
+ if line != "$$B\n" {
+ err = errors.New("old export format no longer supported (recompile library)")
+ return
+ }
+
+ return
+}
+
+// addFingerprint reads the linker fingerprint included at the end of
+// the exportdata.
+func addFingerprint(path string, f *os.File, end int64) error {
+ const eom = "\n$$\n"
+ var fingerprint goobj.FingerprintType
+
+ var buf [len(fingerprint) + len(eom)]byte
+ if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil {
+ return err
+ }
- default:
- base.Errorf("no import in %q", path)
- base.ErrorExit()
+ // Caller should have given us the end position of the export data,
+ // which should end with the "\n$$\n" marker. As a consistency check
+ // to make sure we're reading at the right offset, make sure we
+ // found the marker.
+ if s := string(buf[len(fingerprint):]); s != eom {
+ return fmt.Errorf("expected $$ marker, but found %q", s)
}
+ copy(fingerprint[:], buf[:])
+
// assume files move (get installed) so don't record the full path
if base.Flag.Cfg.PackageFile != nil {
// If using a packageFile map, assume path_ can be recorded directly.
base.Ctxt.AddImport(path, fingerprint)
} else {
// For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
+ file := f.Name()
base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
}
-
- if importpkg.Height >= myheight {
- myheight = importpkg.Height + 1
- }
-
- return importpkg
+ return nil
}
// The linker uses the magic symbol prefixes "go." and "type."
@@ -431,7 +525,7 @@ func clearImports() {
s.Def = nil
continue
}
- if types.IsDotAlias(s) {
+ if s.Def != nil && s.Def.Sym() != s {
// throw away top-level name left over
// from previous import . "x"
// We'll report errors after type checking in CheckDotImports.
diff --git a/src/cmd/compile/internal/noder/irgen.go b/src/cmd/compile/internal/noder/irgen.go
index 3e0d3285ab916ec0661ba9601149a3209138f02c..e20939de6639026536b5d5a4fdb85938d689a6c9 100644
--- a/src/cmd/compile/internal/noder/irgen.go
+++ b/src/cmd/compile/internal/noder/irgen.go
@@ -18,9 +18,9 @@ import (
"cmd/internal/src"
)
-// check2 type checks a Go package using types2, and then generates IR
-// using the results.
-func check2(noders []*noder) {
+// checkFiles configures and runs the types2 checker on the given
+// parsed source files and then returns the result.
+func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
if base.SyntaxErrors() != 0 {
base.ErrorExit()
}
@@ -34,7 +34,13 @@ func check2(noders []*noder) {
}
// typechecking
+ ctxt := types2.NewContext()
+ importer := gcimports{
+ ctxt: ctxt,
+ packages: map[string]*types2.Package{"unsafe": types2.Unsafe},
+ }
conf := types2.Config{
+ Context: ctxt,
GoVersion: base.Flag.Lang,
IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode
CompilerErrorMessages: true, // use error strings matching existing compiler errors
@@ -42,27 +48,35 @@ func check2(noders []*noder) {
terr := err.(types2.Error)
base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
},
- Importer: &gcimports{
- packages: make(map[string]*types2.Package),
- },
- Sizes: &gcSizes{},
+ Importer: &importer,
+ Sizes: &gcSizes{},
}
- info := types2.Info{
+ info := &types2.Info{
Types: make(map[syntax.Expr]types2.TypeAndValue),
Defs: make(map[*syntax.Name]types2.Object),
Uses: make(map[*syntax.Name]types2.Object),
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
Implicits: make(map[syntax.Node]types2.Object),
Scopes: make(map[syntax.Node]*types2.Scope),
- Inferred: make(map[syntax.Expr]types2.Inferred),
+ Instances: make(map[*syntax.Name]types2.Instance),
// expand as needed
}
- pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
- files = nil
+
+ pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
+
base.ExitIfErrors()
if err != nil {
base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
}
+
+ return m, pkg, info
+}
+
+// check2 type checks a Go package using types2, and then generates IR
+// using the results.
+func check2(noders []*noder) {
+ m, pkg, info := checkFiles(noders)
+
if base.Flag.G < 2 {
os.Exit(0)
}
@@ -70,7 +84,7 @@ func check2(noders []*noder) {
g := irgen{
target: typecheck.Target,
self: pkg,
- info: &info,
+ info: info,
posMap: m,
objs: make(map[types2.Object]*ir.Name),
typs: make(map[types2.Type]*types.Type),
@@ -82,6 +96,44 @@ func check2(noders []*noder) {
}
}
+// dictInfo is the dictionary format for an instantiation of a generic function with
+// particular shapes. shapeParams, derivedTypes, subDictCalls, and itabConvs describe
+// the actual dictionary entries in order, and the remaining fields are other info
+// needed in doing dictionary processing during compilation.
+type dictInfo struct {
+ // Types substituted for the type parameters, which are shape types.
+ shapeParams []*types.Type
+ // All types derived from those typeparams used in the instantiation.
+ derivedTypes []*types.Type
+ // Nodes in the instantiation that requires a subdictionary. Includes
+ // method and function calls (OCALL), function values (OFUNCINST), method
+ // values/expressions (OXDOT).
+ subDictCalls []ir.Node
+ // Nodes in the instantiation that are a conversion from a typeparam/derived
+ // type to a specific interface.
+ itabConvs []ir.Node
+
+ // Mapping from each shape type that substitutes a type param, to its
+ // type bound (which is also substituted with shapes if it is parameterized)
+ shapeToBound map[*types.Type]*types.Type
+
+ // For type switches on nonempty interfaces, a map from OTYPE entries of
+ // HasShape type, to the interface type we're switching from.
+ type2switchType map[ir.Node]*types.Type
+
+ startSubDict int // Start of dict entries for subdictionaries
+ startItabConv int // Start of dict entries for itab conversions
+ dictLen int // Total number of entries in dictionary
+}
+
+// instInfo is information gathered on an shape instantiation of a function.
+type instInfo struct {
+ fun *ir.Func // The instantiated function (with body)
+ dictParam *ir.Name // The node inside fun that refers to the dictionary param
+
+ dictInfo *dictInfo
+}
+
type irgen struct {
target *ir.Package
self *types2.Package
@@ -92,12 +144,69 @@ type irgen struct {
typs map[types2.Type]*types.Type
marker dwarfgen.ScopeMarker
- // Fully-instantiated generic types whose methods should be instantiated
- instTypeList []*types.Type
+ // 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.
+ exprStmtOK bool
+
+ // types which we need to finish, by doing g.fillinMethods.
+ typesToFinalize []*typeDelayInfo
+
+ // 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.
+ topFuncIsGeneric bool
+
+ // The context during type/function/method declarations that is used to
+ // uniquely name type parameters. We need unique names for type params so we
+ // can be sure they match up correctly between types2-to-types1 translation
+ // and types1 importing.
+ 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)
+}
+
+type delayInfo struct {
+ gf *ir.Name
+ targs []*types.Type
+ sym *types.Sym
+ off int
+ isMeth bool
+}
+
+type typeDelayInfo struct {
+ typ *types2.Named
+ ntyp *types.Type
}
func (g *irgen) generate(noders []*noder) {
types.LocalPkg.Name = g.self.Name()
+ types.LocalPkg.Height = g.self.Height()
typecheck.TypecheckAllowed = true
// Prevent size calculations until we set the underlying type
@@ -107,7 +216,7 @@ func (g *irgen) generate(noders []*noder) {
// At this point, types2 has already handled name resolution and
// type checking. We just need to map from its object and type
// representations to those currently used by the rest of the
- // compiler. This happens mostly in 3 passes.
+ // compiler. This happens in a few passes.
// 1. Process all import declarations. We use the compiler's own
// importer for this, rather than types2's gcimporter-derived one,
@@ -132,7 +241,6 @@ Outer:
}
}
}
- types.LocalPkg.Height = myheight
// 2. Process all package-block type declarations. As with imports,
// we need to make sure all types are properly instantiated before
@@ -156,8 +264,20 @@ Outer:
types.ResumeCheckSize()
// 3. Process all remaining declarations.
- for _, declList := range declLists {
- g.target.Decls = append(g.target.Decls, g.decls(declList)...)
+ 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
+
+ // 4. Run any "later" tasks. Avoid using 'range' so that tasks can
+ // recursively queue further tasks. (Not currently utilized though.)
+ for len(g.laterFuncs) > 0 {
+ fn := g.laterFuncs[0]
+ g.laterFuncs = g.laterFuncs[1:]
+ fn()
}
if base.Flag.W > 1 {
@@ -167,26 +287,41 @@ Outer:
}
}
- typecheck.DeclareUniverse()
-
for _, p := range noders {
// Process linkname and cgo pragmas.
p.processPragmas()
// Double check for any type-checking inconsistencies. This can be
// removed once we're confident in IR generation results.
- syntax.Walk(p.file, func(n syntax.Node) bool {
+ syntax.Crawl(p.file, func(n syntax.Node) bool {
g.validate(n)
return false
})
}
- // Create any needed stencils of generic functions
- g.stencil()
+ if base.Flag.Complete {
+ for _, n := range g.target.Decls {
+ if fn, ok := n.(*ir.Func); ok {
+ if fn.Body == nil && fn.Nname.Sym().Linkname == "" {
+ base.ErrorfAt(fn.Pos(), "missing function body")
+ }
+ }
+ }
+ }
+
+ // Check for unusual case where noder2 encounters a type error that types2
+ // doesn't check for (e.g. notinheap incompatibility).
+ base.ExitIfErrors()
- // For now, remove all generic functions from g.target.Decl, since they
- // have been used for stenciling, but don't compile. TODO: We will
- // eventually export any exportable generic functions.
+ typecheck.DeclareUniverse()
+
+ // 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
+ // have been marked for export as appropriate.
j := 0
for i, decl := range g.target.Decls {
if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
@@ -195,9 +330,17 @@ Outer:
}
}
g.target.Decls = g.target.Decls[:j]
+
+ base.Assertf(len(g.laterFuncs) == 0, "still have %d later funcs", len(g.laterFuncs))
}
func (g *irgen) unhandled(what string, p poser) {
base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
panic("unreachable")
}
+
+// delayTransform returns true if we should delay all transforms, because we are
+// creating the nodes for a generic function/method.
+func (g *irgen) delayTransform() bool {
+ return g.topFuncIsGeneric
+}
diff --git a/src/cmd/compile/internal/noder/linker.go b/src/cmd/compile/internal/noder/linker.go
new file mode 100644
index 0000000000000000000000000000000000000000..2bc7f7c608ead06af328e49551dfb6f66e607791
--- /dev/null
+++ b/src/cmd/compile/internal/noder/linker.go
@@ -0,0 +1,296 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "io"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/types"
+ "cmd/internal/goobj"
+ "cmd/internal/obj"
+)
+
+// This file implements the unified IR linker, which combines the
+// local package's stub data with imported package data to produce a
+// complete export data file. It also rewrites the compiler's
+// extension data sections based on the results of compilation (e.g.,
+// the function inlining cost and linker symbol index assignments).
+//
+// TODO(mdempsky): Using the name "linker" here is confusing, because
+// readers are likely to mistake references to it for cmd/link. But
+// there's a shortage of good names for "something that combines
+// multiple parts into a cohesive whole"... e.g., "assembler" and
+// "compiler" are also already taken.
+
+type linker struct {
+ pw pkgEncoder
+
+ pkgs map[string]int
+ decls map[*types.Sym]int
+}
+
+func (l *linker) relocAll(pr *pkgReader, relocs []relocEnt) []relocEnt {
+ res := make([]relocEnt, len(relocs))
+ for i, rent := range relocs {
+ rent.idx = l.relocIdx(pr, rent.kind, rent.idx)
+ res[i] = rent
+ }
+ return res
+}
+
+func (l *linker) relocIdx(pr *pkgReader, k reloc, idx int) int {
+ assert(pr != nil)
+
+ absIdx := pr.absIdx(k, idx)
+
+ if newidx := pr.newindex[absIdx]; newidx != 0 {
+ return ^newidx
+ }
+
+ var newidx int
+ switch k {
+ case relocString:
+ newidx = l.relocString(pr, idx)
+ case relocPkg:
+ newidx = l.relocPkg(pr, idx)
+ case relocObj:
+ newidx = l.relocObj(pr, idx)
+
+ default:
+ // Generic relocations.
+ //
+ // TODO(mdempsky): Deduplicate more sections? In fact, I think
+ // every section could be deduplicated. This would also be easier
+ // if we do external relocations.
+
+ w := l.pw.newEncoderRaw(k)
+ l.relocCommon(pr, &w, k, idx)
+ newidx = w.idx
+ }
+
+ pr.newindex[absIdx] = ^newidx
+
+ return newidx
+}
+
+func (l *linker) relocString(pr *pkgReader, idx int) int {
+ return l.pw.stringIdx(pr.stringIdx(idx))
+}
+
+func (l *linker) relocPkg(pr *pkgReader, idx int) int {
+ path := pr.peekPkgPath(idx)
+
+ if newidx, ok := l.pkgs[path]; ok {
+ return newidx
+ }
+
+ r := pr.newDecoder(relocPkg, idx, syncPkgDef)
+ w := l.pw.newEncoder(relocPkg, syncPkgDef)
+ l.pkgs[path] = w.idx
+
+ // TODO(mdempsky): We end up leaving an empty string reference here
+ // from when the package was originally written as "". Probably not
+ // a big deal, but a little annoying. Maybe relocating
+ // cross-references in place is the way to go after all.
+ w.relocs = l.relocAll(pr, r.relocs)
+
+ _ = r.string() // original path
+ w.string(path)
+
+ io.Copy(&w.data, &r.data)
+
+ return w.flush()
+}
+
+func (l *linker) relocObj(pr *pkgReader, idx int) int {
+ path, name, tag := pr.peekObj(idx)
+ sym := types.NewPkg(path, "").Lookup(name)
+
+ if newidx, ok := l.decls[sym]; ok {
+ return newidx
+ }
+
+ if tag == objStub && path != "builtin" && path != "unsafe" {
+ pri, ok := objReader[sym]
+ if !ok {
+ base.Fatalf("missing reader for %q.%v", path, name)
+ }
+ assert(ok)
+
+ pr = pri.pr
+ idx = pri.idx
+
+ path2, name2, tag2 := pr.peekObj(idx)
+ sym2 := types.NewPkg(path2, "").Lookup(name2)
+ assert(sym == sym2)
+ assert(tag2 != objStub)
+ }
+
+ w := l.pw.newEncoderRaw(relocObj)
+ wext := l.pw.newEncoderRaw(relocObjExt)
+ wname := l.pw.newEncoderRaw(relocName)
+ wdict := l.pw.newEncoderRaw(relocObjDict)
+
+ l.decls[sym] = w.idx
+ assert(wext.idx == w.idx)
+ assert(wname.idx == w.idx)
+ assert(wdict.idx == w.idx)
+
+ l.relocCommon(pr, &w, relocObj, idx)
+ l.relocCommon(pr, &wname, relocName, idx)
+ l.relocCommon(pr, &wdict, relocObjDict, idx)
+
+ var obj *ir.Name
+ if path == "" {
+ var ok bool
+ obj, ok = sym.Def.(*ir.Name)
+
+ // Generic types and functions and declared constraint types won't
+ // have definitions.
+ // For now, just generically copy their extension data.
+ // TODO(mdempsky): Restore assertion.
+ if !ok && false {
+ base.Fatalf("missing definition for %v", sym)
+ }
+ }
+
+ if obj != nil {
+ wext.sync(syncObject1)
+ switch tag {
+ case objFunc:
+ l.relocFuncExt(&wext, obj)
+ case objType:
+ l.relocTypeExt(&wext, obj)
+ case objVar:
+ l.relocVarExt(&wext, obj)
+ }
+ wext.flush()
+ } else {
+ l.relocCommon(pr, &wext, relocObjExt, idx)
+ }
+
+ return w.idx
+}
+
+func (l *linker) relocCommon(pr *pkgReader, w *encoder, k reloc, idx int) {
+ r := pr.newDecoderRaw(k, idx)
+ w.relocs = l.relocAll(pr, r.relocs)
+ io.Copy(&w.data, &r.data)
+ w.flush()
+}
+
+func (l *linker) pragmaFlag(w *encoder, pragma ir.PragmaFlag) {
+ w.sync(syncPragma)
+ w.int(int(pragma))
+}
+
+func (l *linker) relocFuncExt(w *encoder, name *ir.Name) {
+ w.sync(syncFuncExt)
+
+ l.pragmaFlag(w, name.Func.Pragma)
+ l.linkname(w, name)
+
+ // Relocated extension data.
+ w.bool(true)
+
+ // Record definition ABI so cross-ABI calls can be direct.
+ // This is important for the performance of calling some
+ // common functions implemented in assembly (e.g., bytealg).
+ w.uint64(uint64(name.Func.ABI))
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(name.Type()).FieldSlice() {
+ w.string(f.Note)
+ }
+ }
+
+ if inl := name.Func.Inl; w.bool(inl != nil) {
+ w.len(int(inl.Cost))
+ w.bool(inl.CanDelayResults)
+
+ pri, ok := bodyReader[name.Func]
+ assert(ok)
+ w.reloc(relocBody, l.relocIdx(pri.pr, relocBody, pri.idx))
+ }
+
+ w.sync(syncEOF)
+}
+
+func (l *linker) relocTypeExt(w *encoder, name *ir.Name) {
+ w.sync(syncTypeExt)
+
+ typ := name.Type()
+
+ l.pragmaFlag(w, name.Pragma())
+
+ // For type T, export the index of type descriptor symbols of T and *T.
+ l.lsymIdx(w, "", reflectdata.TypeLinksym(typ))
+ l.lsymIdx(w, "", reflectdata.TypeLinksym(typ.PtrTo()))
+
+ if typ.Kind() != types.TINTER {
+ for _, method := range typ.Methods().Slice() {
+ l.relocFuncExt(w, method.Nname.(*ir.Name))
+ }
+ }
+}
+
+func (l *linker) relocVarExt(w *encoder, name *ir.Name) {
+ w.sync(syncVarExt)
+ l.linkname(w, name)
+}
+
+func (l *linker) linkname(w *encoder, name *ir.Name) {
+ w.sync(syncLinkname)
+
+ linkname := name.Sym().Linkname
+ if !l.lsymIdx(w, linkname, name.Linksym()) {
+ w.string(linkname)
+ }
+}
+
+func (l *linker) lsymIdx(w *encoder, linkname string, lsym *obj.LSym) bool {
+ if lsym.PkgIdx > goobj.PkgIdxSelf || (lsym.PkgIdx == goobj.PkgIdxInvalid && !lsym.Indexed()) || linkname != "" {
+ w.int64(-1)
+ return false
+ }
+
+ // For a defined symbol, export its index.
+ // For re-exporting an imported symbol, pass its index through.
+ w.int64(int64(lsym.SymIdx))
+ return true
+}
+
+// @@@ Helpers
+
+// TODO(mdempsky): These should probably be removed. I think they're a
+// smell that the export data format is not yet quite right.
+
+func (pr *pkgDecoder) peekPkgPath(idx int) string {
+ r := pr.newDecoder(relocPkg, idx, syncPkgDef)
+ path := r.string()
+ if path == "" {
+ path = pr.pkgPath
+ }
+ return path
+}
+
+func (pr *pkgDecoder) peekObj(idx int) (string, string, codeObj) {
+ r := pr.newDecoder(relocName, idx, syncObject1)
+ r.sync(syncSym)
+ r.sync(syncPkg)
+ path := pr.peekPkgPath(r.reloc(relocPkg))
+ name := r.string()
+ assert(name != "")
+
+ tag := codeObj(r.code(syncCodeObj))
+
+ return path, name, tag
+}
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 5fcad096c28ae5f5aa413bfc1a0ead3f7212e630..b36db67a507a87f8cd6bd0a56791b4f34399cedc 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -5,9 +5,11 @@
package noder
import (
+ "errors"
"fmt"
"go/constant"
"go/token"
+ "internal/buildcfg"
"os"
"path/filepath"
"runtime"
@@ -29,8 +31,11 @@ import (
func LoadPackage(filenames []string) {
base.Timer.Start("fe", "parse")
+ // -G=3 and unified expect generics syntax, but -G=0 does not.
+ supportsGenerics := base.Flag.G != 0 || buildcfg.Experiment.Unified
+
mode := syntax.CheckBranches
- if base.Flag.G != 0 {
+ if supportsGenerics {
mode |= syntax.AllowGenerics
}
@@ -75,6 +80,11 @@ func LoadPackage(filenames []string) {
}
base.Timer.AddEvent(int64(lines), "lines")
+ if base.Debug.Unified != 0 {
+ unified(noders)
+ return
+ }
+
if base.Flag.G != 0 {
// Use types2 to type-check and possibly generate IR.
check2(noders)
@@ -109,45 +119,52 @@ func LoadPackage(filenames []string) {
// We also defer type alias declarations until phase 2
// to avoid cycles like #18640.
// TODO(gri) Remove this again once we have a fix for #25838.
-
- // Don't use range--typecheck can add closures to Target.Decls.
- base.Timer.Start("fe", "typecheck", "top1")
- for i := 0; i < len(typecheck.Target.Decls); i++ {
- n := typecheck.Target.Decls[i]
- if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).X.Alias()) {
- typecheck.Target.Decls[i] = typecheck.Stmt(n)
- }
- }
-
+ //
// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.
// Don't use range--typecheck can add closures to Target.Decls.
- base.Timer.Start("fe", "typecheck", "top2")
- for i := 0; i < len(typecheck.Target.Decls); i++ {
- n := typecheck.Target.Decls[i]
- if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias() {
- typecheck.Target.Decls[i] = typecheck.Stmt(n)
+ for phase, name := range []string{"top1", "top2"} {
+ base.Timer.Start("fe", "typecheck", name)
+ for i := 0; i < len(typecheck.Target.Decls); i++ {
+ n := typecheck.Target.Decls[i]
+ op := n.Op()
+
+ // Closure function declarations are typechecked as part of the
+ // closure expression.
+ if fn, ok := n.(*ir.Func); ok && fn.OClosure != nil {
+ continue
+ }
+
+ // We don't actually add ir.ODCL nodes to Target.Decls. Make sure of that.
+ if op == ir.ODCL {
+ base.FatalfAt(n.Pos(), "unexpected top declaration: %v", op)
+ }
+
+ // Identify declarations that should be deferred to the second
+ // iteration.
+ late := op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).X.Alias()
+
+ if late == (phase == 1) {
+ typecheck.Target.Decls[i] = typecheck.Stmt(n)
+ }
}
}
// Phase 3: Type check function bodies.
// Don't use range--typecheck can add closures to Target.Decls.
base.Timer.Start("fe", "typecheck", "func")
- var fcount int64
for i := 0; i < len(typecheck.Target.Decls); i++ {
- n := typecheck.Target.Decls[i]
- if n.Op() == ir.ODCLFUNC {
+ if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
if base.Flag.W > 1 {
- s := fmt.Sprintf("\nbefore typecheck %v", n)
- ir.Dump(s, n)
+ s := fmt.Sprintf("\nbefore typecheck %v", fn)
+ ir.Dump(s, fn)
}
- typecheck.FuncBody(n.(*ir.Func))
+ typecheck.FuncBody(fn)
if base.Flag.W > 1 {
- s := fmt.Sprintf("\nafter typecheck %v", n)
- ir.Dump(s, n)
+ s := fmt.Sprintf("\nafter typecheck %v", fn)
+ ir.Dump(s, fn)
}
- fcount++
}
}
@@ -172,13 +189,23 @@ func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {
base.ErrorfAt(p.makeXPos(pos), format, args...)
}
-// TODO(gri) Can we eliminate fileh in favor of absFilename?
-func fileh(name string) string {
- return objabi.AbsFile("", name, base.Flag.TrimPath)
-}
-
-func absFilename(name string) string {
- return objabi.AbsFile(base.Ctxt.Pathname, name, base.Flag.TrimPath)
+// trimFilename returns the "trimmed" filename of b, which is the
+// absolute filename after applying -trimpath processing. This
+// filename form is suitable for use in object files and export data.
+//
+// If b's filename has already been trimmed (i.e., because it was read
+// in from an imported package's export data), then the filename is
+// returned unchanged.
+func trimFilename(b *syntax.PosBase) string {
+ filename := b.Filename()
+ if !b.Trimmed() {
+ dir := ""
+ if b.IsFileBase() {
+ dir = base.Ctxt.Pathname
+ }
+ filename = objabi.AbsFile(dir, filename, base.Flag.TrimPath)
+ }
+ return filename
}
// noder transforms package syntax's AST into a Node tree.
@@ -296,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 != "" {
@@ -355,7 +381,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
return
}
- if ipkg == ir.Pkgs.Unsafe {
+ if ipkg == types.UnsafePkg {
p.importedUnsafe = true
}
if ipkg.Path == "embed" {
@@ -449,7 +475,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []ir.Node {
type constState struct {
group *syntax.Group
typ ir.Ntype
- values []ir.Node
+ values syntax.Expr
iota int64
}
@@ -467,16 +493,15 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
names := p.declNames(ir.OLITERAL, decl.NameList)
typ := p.typeExprOrNil(decl.Type)
- var values []ir.Node
if decl.Values != nil {
- values = p.exprList(decl.Values)
- cs.typ, cs.values = typ, values
+ cs.typ, cs.values = typ, decl.Values
} else {
if typ != nil {
base.Errorf("const declaration cannot have type without expression")
}
- typ, values = cs.typ, cs.values
+ typ = cs.typ
}
+ values := p.exprList(cs.values)
nn := make([]ir.Node, 0, len(names))
for i, n := range names {
@@ -484,10 +509,16 @@ func (p *noder) constDecl(decl *syntax.ConstDecl, cs *constState) []ir.Node {
base.Errorf("missing value in const declaration")
break
}
+
v := values[i]
if decl.Values == nil {
- v = ir.DeepCopy(n.Pos(), v)
+ ir.Visit(v, func(v ir.Node) {
+ if ir.HasUniquePos(v) {
+ v.SetPos(n.Pos())
+ }
+ })
}
+
typecheck.Declare(n, typecheck.DeclContext)
n.Ntype = typ
@@ -625,6 +656,9 @@ func (p *noder) params(params []*syntax.Field, dddOk bool) []*ir.Field {
for i, param := range params {
p.setlineno(param)
nodes = append(nodes, p.param(param, dddOk, i+1 == len(params)))
+ if i > 0 && params[i].Type == params[i-1].Type {
+ nodes[i].Ntype = nodes[i-1].Ntype
+ }
}
return nodes
}
@@ -914,6 +948,9 @@ func (p *noder) structType(expr *syntax.StructType) ir.Node {
} else {
n = ir.NewField(p.pos(field), p.name(field.Name), p.typeExpr(field.Type), nil)
}
+ if i > 0 && expr.FieldList[i].Type == expr.FieldList[i-1].Type {
+ n.Ntype = l[i-1].Ntype
+ }
if i < len(expr.TagList) && expr.TagList[i] != nil {
n.Note = constant.StringVal(p.basicLit(expr.TagList[i]))
}
@@ -977,6 +1014,8 @@ func (p *noder) packname(expr syntax.Expr) *types.Sym {
}
func (p *noder) embedded(typ syntax.Expr) *ir.Field {
+ pos := p.pos(syntax.StartPos(typ))
+
op, isStar := typ.(*syntax.Operation)
if isStar {
if op.Op != syntax.Mul || op.Y != nil {
@@ -986,11 +1025,11 @@ func (p *noder) embedded(typ syntax.Expr) *ir.Field {
}
sym := p.packname(typ)
- n := ir.NewField(p.pos(typ), typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil)
+ n := ir.NewField(pos, typecheck.Lookup(sym.Name), importName(sym).(ir.Ntype), nil)
n.Embedded = true
if isStar {
- n.Ntype = ir.NewStarExpr(p.pos(op), n.Ntype)
+ n.Ntype = ir.NewStarExpr(pos, n.Ntype)
}
return n
}
@@ -1198,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)
@@ -1245,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
@@ -1497,7 +1536,7 @@ func (p *noder) mkname(name *syntax.Name) ir.Node {
return mkname(p.name(name))
}
-func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
+func wrapname(pos src.XPos, x ir.Node) ir.Node {
// These nodes do not carry line numbers.
// Introduce a wrapper node to give them the correct line.
switch x.Op() {
@@ -1507,13 +1546,17 @@ func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
}
fallthrough
case ir.ONAME, ir.ONONAME, ir.OPACK:
- p := ir.NewParenExpr(p.pos(n), x)
+ p := ir.NewParenExpr(pos, x)
p.SetImplicit(true)
return p
}
return x
}
+func (p *noder) wrapname(n syntax.Node, x ir.Node) ir.Node {
+ return wrapname(p.pos(n), x)
+}
+
func (p *noder) setlineno(n syntax.Node) {
if n != nil {
base.Pos = p.pos(n)
@@ -1691,7 +1734,7 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P
// (primarily misuse of linker flags), other files are not.
// See golang.org/issue/23672.
func isCgoGeneratedFile(pos syntax.Pos) bool {
- return strings.HasPrefix(filepath.Base(filepath.Clean(fileh(pos.Base().Filename()))), "_cgo_")
+ return strings.HasPrefix(filepath.Base(trimFilename(pos.Base())), "_cgo_")
}
// safeArg reports whether arg is a "safe" command-line argument,
@@ -1780,24 +1823,14 @@ func fakeRecv() *ir.Field {
}
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
- xtype := p.typeExpr(expr.Type)
-
- fn := ir.NewFunc(p.pos(expr))
- fn.SetIsHiddenClosure(ir.CurFunc != nil)
-
- fn.Nname = ir.NewNameAt(p.pos(expr), ir.BlankNode.Sym()) // filled in by tcClosure
- fn.Nname.Func = fn
- fn.Nname.Ntype = xtype
- fn.Nname.Defn = fn
-
- clo := ir.NewClosureExpr(p.pos(expr), fn)
- fn.OClosure = clo
+ fn := ir.NewClosureFunc(p.pos(expr), ir.CurFunc != nil)
+ fn.Nname.Ntype = p.typeExpr(expr.Type)
p.funcBody(fn, expr.Body)
ir.FinishCaptureNames(base.Pos, ir.CurFunc, fn)
- return clo
+ return fn.OClosure
}
// A function named init is a special case.
@@ -1841,33 +1874,14 @@ func oldname(s *types.Sym) ir.Node {
}
func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.VarDecl, pragma *pragmas, haveEmbed bool) {
- if pragma.Embeds == nil {
- return
- }
-
pragmaEmbeds := pragma.Embeds
pragma.Embeds = nil
- pos := makeXPos(pragmaEmbeds[0].Pos)
-
- if !haveEmbed {
- base.ErrorfAt(pos, "go:embed only allowed in Go files that import \"embed\"")
+ if len(pragmaEmbeds) == 0 {
return
}
- if len(decl.NameList) > 1 {
- base.ErrorfAt(pos, "go:embed cannot apply to multiple vars")
- return
- }
- if decl.Values != nil {
- base.ErrorfAt(pos, "go:embed cannot apply to var with initializer")
- return
- }
- if decl.Type == nil {
- // Should not happen, since Values == nil now.
- base.ErrorfAt(pos, "go:embed cannot apply to var without type")
- return
- }
- if typecheck.DeclContext != ir.PEXTERN {
- base.ErrorfAt(pos, "go:embed cannot apply to var inside func")
+
+ if err := checkEmbed(decl, haveEmbed, typecheck.DeclContext != ir.PEXTERN); err != nil {
+ base.ErrorfAt(makeXPos(pragmaEmbeds[0].Pos), "%s", err)
return
}
@@ -1878,3 +1892,24 @@ func varEmbed(makeXPos func(syntax.Pos) src.XPos, name *ir.Name, decl *syntax.Va
typecheck.Target.Embeds = append(typecheck.Target.Embeds, name)
name.Embed = &embeds
}
+
+func checkEmbed(decl *syntax.VarDecl, haveEmbed, withinFunc bool) error {
+ switch {
+ case !haveEmbed:
+ return errors.New("go:embed only allowed in Go files that import \"embed\"")
+ case len(decl.NameList) > 1:
+ return errors.New("go:embed cannot apply to multiple vars")
+ case decl.Values != nil:
+ return errors.New("go:embed cannot apply to var with initializer")
+ case decl.Type == nil:
+ // Should not happen, since Values == nil now.
+ return errors.New("go:embed cannot apply to var without type")
+ case withinFunc:
+ return errors.New("go:embed cannot apply to var inside func")
+ case !types.AllowsGoVersion(types.LocalPkg, 1, 16):
+ return fmt.Errorf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", base.Flag.Lang)
+
+ default:
+ return nil
+ }
+}
diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go
index 82cce1ace0fcff81856f1751498f009c2d5fa84b..37a995b5199cf70f127fd03feb92b9e27ef2973c 100644
--- a/src/cmd/compile/internal/noder/object.go
+++ b/src/cmd/compile/internal/noder/object.go
@@ -22,20 +22,35 @@ func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) {
return g.obj(obj), obj
}
-// use returns the Name node associated with the use of name. The returned node
-// will have the correct type and be marked as typechecked.
-func (g *irgen) use(name *syntax.Name) *ir.Name {
+// use returns the Name or InstExpr node associated with the use of name,
+// possibly instantiated by type arguments. The returned node will have
+// the correct type and be marked as typechecked.
+func (g *irgen) use(name *syntax.Name) ir.Node {
obj2, ok := g.info.Uses[name]
if !ok {
base.FatalfAt(g.pos(name), "unknown name %v", name)
}
- obj := ir.CaptureName(g.pos(obj2), ir.CurFunc, g.obj(obj2))
+ obj := ir.CaptureName(g.pos(name), ir.CurFunc, g.obj(obj2))
if obj.Defn != nil && obj.Defn.Op() == ir.ONAME {
// If CaptureName created a closure variable, then transfer the
// type of the captured name to the new closure variable.
obj.SetTypecheck(1)
obj.SetType(obj.Defn.Type())
}
+
+ if obj.Class == ir.PFUNC {
+ if inst, ok := g.info.Instances[name]; ok {
+ // This is the case where inferring types required the
+ // types of the function arguments.
+ targs := make([]ir.Node, inst.TypeArgs.Len())
+ for i := range targs {
+ targs[i] = ir.TypeNode(g.typ(inst.TypeArgs.At(i)))
+ }
+ typ := g.substType(obj.Type(), obj.Type().TParams(), targs)
+ return typed(typ, ir.NewInstExpr(g.pos(name), ir.OFUNCINST, obj, targs))
+ }
+ }
+
return obj
}
@@ -49,6 +64,11 @@ func (g *irgen) obj(obj types2.Object) *ir.Name {
// For imported objects, we use iimport directly instead of mapping
// the types2 representation.
if obj.Pkg() != g.self {
+ if sig, ok := obj.Type().(*types2.Signature); ok && sig.Recv() != nil {
+ // We can't import a method by name - must import the type
+ // and access the method from it.
+ base.FatalfAt(g.pos(obj), "tried to import a method directly")
+ }
sym := g.sym(obj)
if sym.Def != nil {
return sym.Def.(*ir.Name)
@@ -101,25 +121,28 @@ func (g *irgen) obj(obj types2.Object) *ir.Name {
case *types2.TypeName:
if obj.IsAlias() {
name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type()))
+ name.SetAlias(true)
} else {
name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj))
g.objFinish(name, class, types.NewNamed(name))
}
case *types2.Var:
- var sym *types.Sym
- if class == ir.PPARAMOUT {
+ sym := g.sym(obj)
+ if class == ir.PPARAMOUT && (sym == nil || sym.IsBlank()) {
// Backend needs names for result parameters,
// even if they're anonymous or blank.
- switch obj.Name() {
- case "":
- sym = typecheck.LookupNum("~r", len(ir.CurFunc.Dcl)) // 'r' for "result"
- case "_":
- sym = typecheck.LookupNum("~b", len(ir.CurFunc.Dcl)) // 'b' for "blank"
+ nresults := 0
+ for _, n := range ir.CurFunc.Dcl {
+ if n.Class == ir.PPARAMOUT {
+ nresults++
+ }
+ }
+ if sym == nil {
+ sym = typecheck.LookupNum("~r", nresults) // 'r' for "result"
+ } else {
+ sym = typecheck.LookupNum("~b", nresults) // 'b' for "blank"
}
- }
- if sym == nil {
- sym = g.sym(obj)
}
name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type()))
@@ -164,9 +187,8 @@ func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) {
break // methods are exported with their receiver type
}
if types.IsExported(sym.Name) {
- if name.Class == ir.PFUNC && name.Type().NumTParams() > 0 {
- base.FatalfAt(name.Pos(), "Cannot export a generic function (yet): %v", name)
- }
+ // Generic functions can be marked for export here, even
+ // though they will not be compiled until instantiated.
typecheck.Export(name)
}
if base.Flag.AsmHdr != "" && !name.Sym().Asm() {
diff --git a/src/cmd/compile/internal/noder/posmap.go b/src/cmd/compile/internal/noder/posmap.go
index a6d3e2d7ef4d9ac0aa241eede47c23b2c6595e85..f22628f845f7225c3862d00ada9066eaf7c0054e 100644
--- a/src/cmd/compile/internal/noder/posmap.go
+++ b/src/cmd/compile/internal/noder/posmap.go
@@ -45,8 +45,10 @@ func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
b1, ok := m.bases[b0]
if !ok {
fn := b0.Filename()
+ absfn := trimFilename(b0)
+
if b0.IsFileBase() {
- b1 = src.NewFileBase(fn, absFilename(fn))
+ b1 = src.NewFileBase(fn, absfn)
} else {
// line directive base
p0 := b0.Pos()
@@ -55,7 +57,7 @@ func (m *posMap) makeSrcPosBase(b0 *syntax.PosBase) *src.PosBase {
panic("infinite recursion in makeSrcPosBase")
}
p1 := src.MakePos(m.makeSrcPosBase(p0b), p0.Line(), p0.Col())
- b1 = src.NewLinePragmaBase(p1, fn, fileh(fn), b0.Line(), b0.Col())
+ b1 = src.NewLinePragmaBase(p1, fn, absfn, b0.Line(), b0.Col())
}
if m.bases == nil {
m.bases = make(map[*syntax.PosBase]*src.PosBase)
diff --git a/src/cmd/compile/internal/noder/quirks.go b/src/cmd/compile/internal/noder/quirks.go
new file mode 100644
index 0000000000000000000000000000000000000000..914c5d2bd7115663dd34df2833e12dd86fa7bdb0
--- /dev/null
+++ b/src/cmd/compile/internal/noder/quirks.go
@@ -0,0 +1,450 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "fmt"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+// This file defines helper functions useful for satisfying toolstash
+// -cmp when compared against the legacy frontend behavior, but can be
+// removed after that's no longer a concern.
+
+// quirksMode controls whether behavior specific to satisfying
+// toolstash -cmp is used.
+func quirksMode() bool {
+ return base.Debug.UnifiedQuirks != 0
+}
+
+// posBasesOf returns all of the position bases in the source files,
+// as seen in a straightforward traversal.
+//
+// This is necessary to ensure position bases (and thus file names)
+// get registered in the same order as noder would visit them.
+func posBasesOf(noders []*noder) []*syntax.PosBase {
+ seen := make(map[*syntax.PosBase]bool)
+ var bases []*syntax.PosBase
+
+ for _, p := range noders {
+ syntax.Crawl(p.file, func(n syntax.Node) bool {
+ if b := n.Pos().Base(); !seen[b] {
+ bases = append(bases, b)
+ seen[b] = true
+ }
+ return false
+ })
+ }
+
+ return bases
+}
+
+// importedObjsOf returns the imported objects (i.e., referenced
+// objects not declared by curpkg) from the parsed source files, in
+// the order that typecheck used to load their definitions.
+//
+// This is needed because loading the definitions for imported objects
+// can also add file names.
+func importedObjsOf(curpkg *types2.Package, info *types2.Info, noders []*noder) []types2.Object {
+ // This code is complex because it matches the precise order that
+ // typecheck recursively and repeatedly traverses the IR. It's meant
+ // to be thrown away eventually anyway.
+
+ seen := make(map[types2.Object]bool)
+ var objs []types2.Object
+
+ var phase int
+
+ decls := make(map[types2.Object]syntax.Decl)
+ assoc := func(decl syntax.Decl, names ...*syntax.Name) {
+ for _, name := range names {
+ obj, ok := info.Defs[name]
+ assert(ok)
+ decls[obj] = decl
+ }
+ }
+
+ for _, p := range noders {
+ syntax.Crawl(p.file, func(n syntax.Node) bool {
+ switch n := n.(type) {
+ case *syntax.ConstDecl:
+ assoc(n, n.NameList...)
+ case *syntax.FuncDecl:
+ assoc(n, n.Name)
+ case *syntax.TypeDecl:
+ assoc(n, n.Name)
+ case *syntax.VarDecl:
+ assoc(n, n.NameList...)
+ case *syntax.BlockStmt:
+ return true
+ }
+ return false
+ })
+ }
+
+ var visited map[syntax.Decl]bool
+
+ var resolveDecl func(n syntax.Decl)
+ var resolveNode func(n syntax.Node, top bool)
+
+ resolveDecl = func(n syntax.Decl) {
+ if visited[n] {
+ return
+ }
+ visited[n] = true
+
+ switch n := n.(type) {
+ case *syntax.ConstDecl:
+ resolveNode(n.Type, true)
+ resolveNode(n.Values, true)
+
+ case *syntax.FuncDecl:
+ if n.Recv != nil {
+ resolveNode(n.Recv, true)
+ }
+ resolveNode(n.Type, true)
+
+ case *syntax.TypeDecl:
+ resolveNode(n.Type, true)
+
+ case *syntax.VarDecl:
+ if n.Type != nil {
+ resolveNode(n.Type, true)
+ } else {
+ resolveNode(n.Values, true)
+ }
+ }
+ }
+
+ resolveObj := func(pos syntax.Pos, obj types2.Object) {
+ switch obj.Pkg() {
+ case nil:
+ // builtin; nothing to do
+
+ case curpkg:
+ if decl, ok := decls[obj]; ok {
+ resolveDecl(decl)
+ }
+
+ default:
+ if obj.Parent() == obj.Pkg().Scope() && !seen[obj] {
+ seen[obj] = true
+ objs = append(objs, obj)
+ }
+ }
+ }
+
+ checkdefat := func(pos syntax.Pos, n *syntax.Name) {
+ if n.Value == "_" {
+ return
+ }
+ obj, ok := info.Uses[n]
+ if !ok {
+ obj, ok = info.Defs[n]
+ if !ok {
+ return
+ }
+ }
+ if obj == nil {
+ return
+ }
+ resolveObj(pos, obj)
+ }
+ checkdef := func(n *syntax.Name) { checkdefat(n.Pos(), n) }
+
+ var later []syntax.Node
+
+ resolveNode = func(n syntax.Node, top bool) {
+ if n == nil {
+ return
+ }
+ syntax.Crawl(n, func(n syntax.Node) bool {
+ switch n := n.(type) {
+ case *syntax.Name:
+ checkdef(n)
+
+ case *syntax.SelectorExpr:
+ if name, ok := n.X.(*syntax.Name); ok {
+ if _, isPkg := info.Uses[name].(*types2.PkgName); isPkg {
+ checkdefat(n.X.Pos(), n.Sel)
+ return true
+ }
+ }
+
+ case *syntax.AssignStmt:
+ resolveNode(n.Rhs, top)
+ resolveNode(n.Lhs, top)
+ return true
+
+ case *syntax.VarDecl:
+ resolveNode(n.Values, top)
+
+ case *syntax.FuncLit:
+ if top {
+ resolveNode(n.Type, top)
+ later = append(later, n.Body)
+ return true
+ }
+
+ case *syntax.BlockStmt:
+ if phase >= 3 {
+ for _, stmt := range n.List {
+ resolveNode(stmt, false)
+ }
+ }
+ return true
+ }
+
+ return false
+ })
+ }
+
+ for phase = 1; phase <= 5; phase++ {
+ visited = map[syntax.Decl]bool{}
+
+ for _, p := range noders {
+ for _, decl := range p.file.DeclList {
+ switch decl := decl.(type) {
+ case *syntax.ConstDecl:
+ resolveDecl(decl)
+
+ case *syntax.FuncDecl:
+ resolveDecl(decl)
+ if phase >= 3 && decl.Body != nil {
+ resolveNode(decl.Body, true)
+ }
+
+ case *syntax.TypeDecl:
+ if !decl.Alias || phase >= 2 {
+ resolveDecl(decl)
+ }
+
+ case *syntax.VarDecl:
+ if phase >= 2 {
+ resolveNode(decl.Values, true)
+ resolveDecl(decl)
+ }
+ }
+ }
+
+ if phase >= 5 {
+ syntax.Crawl(p.file, func(n syntax.Node) bool {
+ if name, ok := n.(*syntax.Name); ok {
+ if obj, ok := info.Uses[name]; ok {
+ resolveObj(name.Pos(), obj)
+ }
+ }
+ return false
+ })
+ }
+ }
+
+ for i := 0; i < len(later); i++ {
+ resolveNode(later[i], true)
+ }
+ later = nil
+ }
+
+ return objs
+}
+
+// typeExprEndPos returns the position that noder would leave base.Pos
+// after parsing the given type expression.
+func typeExprEndPos(expr0 syntax.Expr) syntax.Pos {
+ for {
+ switch expr := expr0.(type) {
+ case *syntax.Name:
+ return expr.Pos()
+ case *syntax.SelectorExpr:
+ return expr.X.Pos()
+
+ case *syntax.ParenExpr:
+ expr0 = expr.X
+
+ case *syntax.Operation:
+ assert(expr.Op == syntax.Mul)
+ assert(expr.Y == nil)
+ expr0 = expr.X
+
+ case *syntax.ArrayType:
+ expr0 = expr.Elem
+ case *syntax.ChanType:
+ expr0 = expr.Elem
+ case *syntax.DotsType:
+ expr0 = expr.Elem
+ case *syntax.MapType:
+ expr0 = expr.Value
+ case *syntax.SliceType:
+ expr0 = expr.Elem
+
+ case *syntax.StructType:
+ return expr.Pos()
+
+ case *syntax.InterfaceType:
+ expr0 = lastFieldType(expr.MethodList)
+ if expr0 == nil {
+ return expr.Pos()
+ }
+
+ case *syntax.FuncType:
+ expr0 = lastFieldType(expr.ResultList)
+ if expr0 == nil {
+ expr0 = lastFieldType(expr.ParamList)
+ if expr0 == nil {
+ return expr.Pos()
+ }
+ }
+
+ case *syntax.IndexExpr: // explicit type instantiation
+ targs := unpackListExpr(expr.Index)
+ expr0 = targs[len(targs)-1]
+
+ default:
+ panic(fmt.Sprintf("%s: unexpected type expression %v", expr.Pos(), syntax.String(expr)))
+ }
+ }
+}
+
+func lastFieldType(fields []*syntax.Field) syntax.Expr {
+ if len(fields) == 0 {
+ return nil
+ }
+ return fields[len(fields)-1].Type
+}
+
+// sumPos returns the position that noder.sum would produce for
+// constant expression x.
+func sumPos(x syntax.Expr) syntax.Pos {
+ orig := x
+ for {
+ switch x1 := x.(type) {
+ case *syntax.BasicLit:
+ assert(x1.Kind == syntax.StringLit)
+ return x1.Pos()
+ case *syntax.Operation:
+ assert(x1.Op == syntax.Add && x1.Y != nil)
+ if r, ok := x1.Y.(*syntax.BasicLit); ok {
+ assert(r.Kind == syntax.StringLit)
+ x = x1.X
+ continue
+ }
+ }
+ return orig.Pos()
+ }
+}
+
+// funcParamsEndPos returns the value of base.Pos left by noder after
+// processing a function signature.
+func funcParamsEndPos(fn *ir.Func) src.XPos {
+ sig := fn.Nname.Type()
+
+ fields := sig.Results().FieldSlice()
+ if len(fields) == 0 {
+ fields = sig.Params().FieldSlice()
+ if len(fields) == 0 {
+ fields = sig.Recvs().FieldSlice()
+ if len(fields) == 0 {
+ if fn.OClosure != nil {
+ return fn.Nname.Ntype.Pos()
+ }
+ return fn.Pos()
+ }
+ }
+ }
+
+ return fields[len(fields)-1].Pos
+}
+
+type dupTypes struct {
+ origs map[types2.Type]types2.Type
+}
+
+func (d *dupTypes) orig(t types2.Type) types2.Type {
+ if orig, ok := d.origs[t]; ok {
+ return orig
+ }
+ return t
+}
+
+func (d *dupTypes) add(t, orig types2.Type) {
+ if t == orig {
+ return
+ }
+
+ if d.origs == nil {
+ d.origs = make(map[types2.Type]types2.Type)
+ }
+ assert(d.origs[t] == nil)
+ d.origs[t] = orig
+
+ switch t := t.(type) {
+ case *types2.Pointer:
+ orig := orig.(*types2.Pointer)
+ d.add(t.Elem(), orig.Elem())
+
+ case *types2.Slice:
+ orig := orig.(*types2.Slice)
+ d.add(t.Elem(), orig.Elem())
+
+ case *types2.Map:
+ orig := orig.(*types2.Map)
+ d.add(t.Key(), orig.Key())
+ d.add(t.Elem(), orig.Elem())
+
+ case *types2.Array:
+ orig := orig.(*types2.Array)
+ assert(t.Len() == orig.Len())
+ d.add(t.Elem(), orig.Elem())
+
+ case *types2.Chan:
+ orig := orig.(*types2.Chan)
+ assert(t.Dir() == orig.Dir())
+ d.add(t.Elem(), orig.Elem())
+
+ case *types2.Struct:
+ orig := orig.(*types2.Struct)
+ assert(t.NumFields() == orig.NumFields())
+ for i := 0; i < t.NumFields(); i++ {
+ d.add(t.Field(i).Type(), orig.Field(i).Type())
+ }
+
+ case *types2.Interface:
+ orig := orig.(*types2.Interface)
+ assert(t.NumExplicitMethods() == orig.NumExplicitMethods())
+ assert(t.NumEmbeddeds() == orig.NumEmbeddeds())
+ for i := 0; i < t.NumExplicitMethods(); i++ {
+ d.add(t.ExplicitMethod(i).Type(), orig.ExplicitMethod(i).Type())
+ }
+ for i := 0; i < t.NumEmbeddeds(); i++ {
+ d.add(t.EmbeddedType(i), orig.EmbeddedType(i))
+ }
+
+ case *types2.Signature:
+ orig := orig.(*types2.Signature)
+ assert((t.Recv() == nil) == (orig.Recv() == nil))
+ if t.Recv() != nil {
+ d.add(t.Recv().Type(), orig.Recv().Type())
+ }
+ d.add(t.Params(), orig.Params())
+ d.add(t.Results(), orig.Results())
+
+ case *types2.Tuple:
+ orig := orig.(*types2.Tuple)
+ assert(t.Len() == orig.Len())
+ for i := 0; i < t.Len(); i++ {
+ d.add(t.At(i).Type(), orig.At(i).Type())
+ }
+
+ default:
+ assert(types2.Identical(t, orig))
+ }
+}
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go
new file mode 100644
index 0000000000000000000000000000000000000000..0bc9135999e0f050935e0c1558516d5a2ed19190
--- /dev/null
+++ b/src/cmd/compile/internal/noder/reader.go
@@ -0,0 +1,2460 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "bytes"
+ "fmt"
+ "go/constant"
+ "internal/buildcfg"
+ "strings"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/deadcode"
+ "cmd/compile/internal/dwarfgen"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "cmd/internal/src"
+)
+
+// TODO(mdempsky): Suppress duplicate type/const errors that can arise
+// during typecheck due to naive type substitution (e.g., see #42758).
+// I anticipate these will be handled as a consequence of adding
+// dictionaries support, so it's probably not important to focus on
+// this until after that's done.
+
+type pkgReader struct {
+ pkgDecoder
+
+ posBases []*src.PosBase
+ pkgs []*types.Pkg
+ typs []*types.Type
+
+ // offset for rewriting the given index into the output,
+ // but bitwise inverted so we can detect if we're missing the entry or not.
+ newindex []int
+}
+
+func newPkgReader(pr pkgDecoder) *pkgReader {
+ return &pkgReader{
+ pkgDecoder: pr,
+
+ posBases: make([]*src.PosBase, pr.numElems(relocPosBase)),
+ pkgs: make([]*types.Pkg, pr.numElems(relocPkg)),
+ typs: make([]*types.Type, pr.numElems(relocType)),
+
+ newindex: make([]int, pr.totalElems()),
+ }
+}
+
+type pkgReaderIndex struct {
+ pr *pkgReader
+ idx int
+ dict *readerDict
+}
+
+func (pri pkgReaderIndex) asReader(k reloc, marker syncMarker) *reader {
+ r := pri.pr.newReader(k, pri.idx, marker)
+ r.dict = pri.dict
+ return r
+}
+
+func (pr *pkgReader) newReader(k reloc, idx int, marker syncMarker) *reader {
+ return &reader{
+ decoder: pr.newDecoder(k, idx, marker),
+ p: pr,
+ }
+}
+
+type reader struct {
+ decoder
+
+ p *pkgReader
+
+ dict *readerDict
+
+ // TODO(mdempsky): The state below is all specific to reading
+ // function bodies. It probably makes sense to split it out
+ // separately so that it doesn't take up space in every reader
+ // instance.
+
+ curfn *ir.Func
+ locals []*ir.Name
+ closureVars []*ir.Name
+
+ funarghack bool
+
+ // scopeVars is a stack tracking the number of variables declared in
+ // the current function at the moment each open scope was opened.
+ scopeVars []int
+ marker dwarfgen.ScopeMarker
+ lastCloseScopePos src.XPos
+
+ // === details for handling inline body expansion ===
+
+ // If we're reading in a function body because of inlining, this is
+ // the call that we're inlining for.
+ inlCaller *ir.Func
+ inlCall *ir.CallExpr
+ inlFunc *ir.Func
+ inlTreeIndex int
+ inlPosBases map[*src.PosBase]*src.PosBase
+
+ delayResults bool
+
+ // Label to return to.
+ retlabel *types.Sym
+
+ inlvars, retvars ir.Nodes
+}
+
+type readerDict struct {
+ // targs holds the implicit and explicit type arguments in use for
+ // reading the current object. For example:
+ //
+ // func F[T any]() {
+ // type X[U any] struct { t T; u U }
+ // var _ X[string]
+ // }
+ //
+ // var _ = F[int]
+ //
+ // While instantiating F[int], we need to in turn instantiate
+ // X[string]. [int] and [string] are explicit type arguments for F
+ // and X, respectively; but [int] is also the implicit type
+ // arguments for X.
+ //
+ // (As an analogy to function literals, explicits are the function
+ // literal's formal parameters, while implicits are variables
+ // captured by the function literal.)
+ targs []*types.Type
+
+ // implicits counts how many of types within targs are implicit type
+ // arguments; the rest are explicit.
+ implicits int
+
+ derived []derivedInfo // reloc index of the derived type's descriptor
+ derivedTypes []*types.Type // slice of previously computed derived types
+
+ funcs []objInfo
+ funcsObj []ir.Node
+}
+
+func setType(n ir.Node, typ *types.Type) {
+ n.SetType(typ)
+ n.SetTypecheck(1)
+
+ if name, ok := n.(*ir.Name); ok {
+ name.SetWalkdef(1)
+ name.Ntype = ir.TypeNode(name.Type())
+ }
+}
+
+func setValue(name *ir.Name, val constant.Value) {
+ name.SetVal(val)
+ name.Defn = nil
+}
+
+// @@@ Positions
+
+func (r *reader) pos() src.XPos {
+ return base.Ctxt.PosTable.XPos(r.pos0())
+}
+
+func (r *reader) pos0() src.Pos {
+ r.sync(syncPos)
+ if !r.bool() {
+ return src.NoPos
+ }
+
+ posBase := r.posBase()
+ line := r.uint()
+ col := r.uint()
+ return src.MakePos(posBase, line, col)
+}
+
+func (r *reader) posBase() *src.PosBase {
+ return r.inlPosBase(r.p.posBaseIdx(r.reloc(relocPosBase)))
+}
+
+func (pr *pkgReader) posBaseIdx(idx int) *src.PosBase {
+ if b := pr.posBases[idx]; b != nil {
+ return b
+ }
+
+ r := pr.newReader(relocPosBase, idx, syncPosBase)
+ var b *src.PosBase
+
+ absFilename := r.string()
+ filename := absFilename
+
+ // For build artifact stability, the export data format only
+ // contains the "absolute" filename as returned by objabi.AbsFile.
+ // However, some tests (e.g., test/run.go's asmcheck tests) expect
+ // to see the full, original filename printed out. Re-expanding
+ // "$GOROOT" to buildcfg.GOROOT is a close-enough approximation to
+ // satisfy this.
+ //
+ // TODO(mdempsky): De-duplicate this logic with similar logic in
+ // cmd/link/internal/ld's expandGoroot. However, this will probably
+ // require being more consistent about when we use native vs UNIX
+ // file paths.
+ const dollarGOROOT = "$GOROOT"
+ if strings.HasPrefix(filename, dollarGOROOT) {
+ filename = buildcfg.GOROOT + filename[len(dollarGOROOT):]
+ }
+
+ if r.bool() {
+ b = src.NewFileBase(filename, absFilename)
+ } else {
+ pos := r.pos0()
+ line := r.uint()
+ col := r.uint()
+ b = src.NewLinePragmaBase(pos, filename, absFilename, line, col)
+ }
+
+ pr.posBases[idx] = b
+ return b
+}
+
+func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase {
+ if r.inlCall == nil {
+ return oldBase
+ }
+
+ if newBase, ok := r.inlPosBases[oldBase]; ok {
+ return newBase
+ }
+
+ newBase := src.NewInliningBase(oldBase, r.inlTreeIndex)
+ r.inlPosBases[oldBase] = newBase
+ return newBase
+}
+
+func (r *reader) updatePos(xpos src.XPos) src.XPos {
+ pos := base.Ctxt.PosTable.Pos(xpos)
+ pos.SetBase(r.inlPosBase(pos.Base()))
+ return base.Ctxt.PosTable.XPos(pos)
+}
+
+func (r *reader) origPos(xpos src.XPos) src.XPos {
+ if r.inlCall == nil {
+ return xpos
+ }
+
+ pos := base.Ctxt.PosTable.Pos(xpos)
+ for old, new := range r.inlPosBases {
+ if pos.Base() == new {
+ pos.SetBase(old)
+ return base.Ctxt.PosTable.XPos(pos)
+ }
+ }
+
+ base.FatalfAt(xpos, "pos base missing from inlPosBases")
+ panic("unreachable")
+}
+
+// @@@ Packages
+
+func (r *reader) pkg() *types.Pkg {
+ r.sync(syncPkg)
+ return r.p.pkgIdx(r.reloc(relocPkg))
+}
+
+func (pr *pkgReader) pkgIdx(idx int) *types.Pkg {
+ if pkg := pr.pkgs[idx]; pkg != nil {
+ return pkg
+ }
+
+ pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg()
+ pr.pkgs[idx] = pkg
+ return pkg
+}
+
+func (r *reader) doPkg() *types.Pkg {
+ path := r.string()
+ if path == "builtin" {
+ return types.BuiltinPkg
+ }
+ if path == "" {
+ path = r.p.pkgPath
+ }
+
+ name := r.string()
+ height := r.len()
+
+ pkg := types.NewPkg(path, "")
+
+ if pkg.Name == "" {
+ pkg.Name = name
+ } else {
+ assert(pkg.Name == name)
+ }
+
+ if pkg.Height == 0 {
+ pkg.Height = height
+ } else {
+ assert(pkg.Height == height)
+ }
+
+ return pkg
+}
+
+// @@@ Types
+
+func (r *reader) typ() *types.Type {
+ return r.typWrapped(true)
+}
+
+// typWrapped is like typ, but allows suppressing generation of
+// unnecessary wrappers as a compile-time optimization.
+func (r *reader) typWrapped(wrapped bool) *types.Type {
+ return r.p.typIdx(r.typInfo(), r.dict, wrapped)
+}
+
+func (r *reader) typInfo() typeInfo {
+ r.sync(syncType)
+ if r.bool() {
+ return typeInfo{idx: r.len(), derived: true}
+ }
+ return typeInfo{idx: r.reloc(relocType), derived: false}
+}
+
+func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *types.Type {
+ idx := info.idx
+ var where **types.Type
+ if info.derived {
+ where = &dict.derivedTypes[idx]
+ idx = dict.derived[idx].idx
+ } else {
+ where = &pr.typs[idx]
+ }
+
+ if typ := *where; typ != nil {
+ return typ
+ }
+
+ r := pr.newReader(relocType, idx, syncTypeIdx)
+ r.dict = dict
+
+ typ := r.doTyp()
+ assert(typ != nil)
+
+ // For recursive type declarations involving interfaces and aliases,
+ // above r.doTyp() call may have already set pr.typs[idx], so just
+ // double check and return the type.
+ //
+ // Example:
+ //
+ // type F = func(I)
+ //
+ // type I interface {
+ // m(F)
+ // }
+ //
+ // The writer writes data types in following index order:
+ //
+ // 0: func(I)
+ // 1: I
+ // 2: interface{m(func(I))}
+ //
+ // The reader resolves it in following index order:
+ //
+ // 0 -> 1 -> 2 -> 0 -> 1
+ //
+ // and can divide in logically 2 steps:
+ //
+ // - 0 -> 1 : first time the reader reach type I,
+ // it creates new named type with symbol I.
+ //
+ // - 2 -> 0 -> 1: the reader ends up reaching symbol I again,
+ // now the symbol I was setup in above step, so
+ // the reader just return the named type.
+ //
+ // Now, the functions called return, the pr.typs looks like below:
+ //
+ // - 0 -> 1 -> 2 -> 0 : [ I ]
+ // - 0 -> 1 -> 2 : [func(I) I ]
+ // - 0 -> 1 : [func(I) I interface { "".m(func("".I)) }]
+ //
+ // The idx 1, corresponding with type I was resolved successfully
+ // after r.doTyp() call.
+
+ if prev := *where; prev != nil {
+ return prev
+ }
+
+ if wrapped {
+ // Only cache if we're adding wrappers, so that other callers that
+ // find a cached type know it was wrapped.
+ *where = typ
+
+ r.needWrapper(typ)
+ }
+
+ if !typ.IsUntyped() {
+ types.CheckSize(typ)
+ }
+
+ return typ
+}
+
+func (r *reader) doTyp() *types.Type {
+ switch tag := codeType(r.code(syncType)); tag {
+ default:
+ panic(fmt.Sprintf("unexpected type: %v", tag))
+
+ case typeBasic:
+ return *basics[r.len()]
+
+ case typeNamed:
+ obj := r.obj()
+ assert(obj.Op() == ir.OTYPE)
+ return obj.Type()
+
+ case typeTypeParam:
+ return r.dict.targs[r.len()]
+
+ case typeArray:
+ len := int64(r.uint64())
+ return types.NewArray(r.typ(), len)
+ case typeChan:
+ dir := dirs[r.len()]
+ return types.NewChan(r.typ(), dir)
+ case typeMap:
+ return types.NewMap(r.typ(), r.typ())
+ case typePointer:
+ return types.NewPtr(r.typ())
+ case typeSignature:
+ return r.signature(types.LocalPkg, nil)
+ case typeSlice:
+ return types.NewSlice(r.typ())
+ case typeStruct:
+ return r.structType()
+ case typeInterface:
+ return r.interfaceType()
+ }
+}
+
+func (r *reader) interfaceType() *types.Type {
+ tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
+
+ nmethods, nembeddeds := r.len(), r.len()
+
+ fields := make([]*types.Field, nmethods+nembeddeds)
+ methods, embeddeds := fields[:nmethods], fields[nmethods:]
+
+ for i := range methods {
+ pos := r.pos()
+ pkg, sym := r.selector()
+ tpkg = pkg
+ mtyp := r.signature(pkg, types.FakeRecv())
+ methods[i] = types.NewField(pos, sym, mtyp)
+ }
+ for i := range embeddeds {
+ embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ())
+ }
+
+ if len(fields) == 0 {
+ return types.Types[types.TINTER] // empty interface
+ }
+ return types.NewInterface(tpkg, fields, false)
+}
+
+func (r *reader) structType() *types.Type {
+ tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone.
+ fields := make([]*types.Field, r.len())
+ for i := range fields {
+ pos := r.pos()
+ pkg, sym := r.selector()
+ tpkg = pkg
+ ftyp := r.typ()
+ tag := r.string()
+ embedded := r.bool()
+
+ f := types.NewField(pos, sym, ftyp)
+ f.Note = tag
+ if embedded {
+ f.Embedded = 1
+ }
+ fields[i] = f
+ }
+ return types.NewStruct(tpkg, fields)
+}
+
+func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
+ r.sync(syncSignature)
+
+ params := r.params(&tpkg)
+ results := r.params(&tpkg)
+ if r.bool() { // variadic
+ params[len(params)-1].SetIsDDD(true)
+ }
+
+ return types.NewSignature(tpkg, recv, nil, params, results)
+}
+
+func (r *reader) params(tpkg **types.Pkg) []*types.Field {
+ r.sync(syncParams)
+ fields := make([]*types.Field, r.len())
+ for i := range fields {
+ *tpkg, fields[i] = r.param()
+ }
+ return fields
+}
+
+func (r *reader) param() (*types.Pkg, *types.Field) {
+ r.sync(syncParam)
+
+ pos := r.pos()
+ pkg, sym := r.localIdent()
+ typ := r.typ()
+
+ return pkg, types.NewField(pos, sym, typ)
+}
+
+// @@@ Objects
+
+var objReader = map[*types.Sym]pkgReaderIndex{}
+
+func (r *reader) obj() ir.Node {
+ r.sync(syncObject)
+
+ if r.bool() {
+ idx := r.len()
+ obj := r.dict.funcsObj[idx]
+ if obj == nil {
+ fn := r.dict.funcs[idx]
+ targs := make([]*types.Type, len(fn.explicits))
+ for i, targ := range fn.explicits {
+ targs[i] = r.p.typIdx(targ, r.dict, true)
+ }
+
+ obj = r.p.objIdx(fn.idx, nil, targs)
+ assert(r.dict.funcsObj[idx] == nil)
+ r.dict.funcsObj[idx] = obj
+ }
+ return obj
+ }
+
+ idx := r.reloc(relocObj)
+
+ explicits := make([]*types.Type, r.len())
+ for i := range explicits {
+ explicits[i] = r.typ()
+ }
+
+ var implicits []*types.Type
+ if r.dict != nil {
+ implicits = r.dict.targs
+ }
+
+ return r.p.objIdx(idx, implicits, explicits)
+}
+
+func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node {
+ rname := pr.newReader(relocName, idx, syncObject1)
+ _, sym := rname.qualifiedIdent()
+ tag := codeObj(rname.code(syncCodeObj))
+
+ if tag == objStub {
+ assert(!sym.IsBlank())
+ switch sym.Pkg {
+ case types.BuiltinPkg, types.UnsafePkg:
+ return sym.Def.(ir.Node)
+ }
+ if pri, ok := objReader[sym]; ok {
+ return pri.pr.objIdx(pri.idx, nil, explicits)
+ }
+ if haveLegacyImports {
+ assert(len(explicits) == 0)
+ return typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
+ }
+ base.Fatalf("unresolved stub: %v", sym)
+ }
+
+ dict := pr.objDictIdx(sym, idx, implicits, explicits)
+
+ r := pr.newReader(relocObj, idx, syncObject1)
+ rext := pr.newReader(relocObjExt, idx, syncObject1)
+
+ r.dict = dict
+ rext.dict = dict
+
+ sym = r.mangle(sym)
+ if !sym.IsBlank() && sym.Def != nil {
+ return sym.Def.(*ir.Name)
+ }
+
+ do := func(op ir.Op, hasTParams bool) *ir.Name {
+ pos := r.pos()
+ if hasTParams {
+ r.typeParamNames()
+ }
+
+ name := ir.NewDeclNameAt(pos, op, sym)
+ name.Class = ir.PEXTERN // may be overridden later
+ if !sym.IsBlank() {
+ if sym.Def != nil {
+ base.FatalfAt(name.Pos(), "already have a definition for %v", name)
+ }
+ assert(sym.Def == nil)
+ sym.Def = name
+ }
+ return name
+ }
+
+ switch tag {
+ default:
+ panic("unexpected object")
+
+ case objAlias:
+ name := do(ir.OTYPE, false)
+ setType(name, r.typ())
+ name.SetAlias(true)
+ return name
+
+ case objConst:
+ name := do(ir.OLITERAL, false)
+ typ := r.typ()
+ val := FixValue(typ, r.value())
+ setType(name, typ)
+ setValue(name, val)
+ return name
+
+ case objFunc:
+ if sym.Name == "init" {
+ sym = renameinit()
+ }
+ name := do(ir.ONAME, true)
+ setType(name, r.signature(sym.Pkg, nil))
+
+ name.Func = ir.NewFunc(r.pos())
+ name.Func.Nname = name
+
+ rext.funcExt(name)
+ return name
+
+ case objType:
+ name := do(ir.OTYPE, true)
+ typ := types.NewNamed(name)
+ setType(name, typ)
+
+ // Important: We need to do this before SetUnderlying.
+ rext.typeExt(name)
+
+ // We need to defer CheckSize until we've called SetUnderlying to
+ // handle recursive types.
+ types.DeferCheckSize()
+ typ.SetUnderlying(r.typWrapped(false))
+ types.ResumeCheckSize()
+
+ methods := make([]*types.Field, r.len())
+ for i := range methods {
+ methods[i] = r.method(rext)
+ }
+ if len(methods) != 0 {
+ typ.Methods().Set(methods)
+ }
+
+ r.needWrapper(typ)
+
+ return name
+
+ case objVar:
+ name := do(ir.ONAME, false)
+ setType(name, r.typ())
+ rext.varExt(name)
+ return name
+ }
+}
+
+func (r *reader) mangle(sym *types.Sym) *types.Sym {
+ if !r.hasTypeParams() {
+ return sym
+ }
+
+ var buf bytes.Buffer
+ buf.WriteString(sym.Name)
+ buf.WriteByte('[')
+ for i, targ := range r.dict.targs {
+ if i > 0 {
+ if i == r.dict.implicits {
+ buf.WriteByte(';')
+ } else {
+ buf.WriteByte(',')
+ }
+ }
+ buf.WriteString(targ.LinkString())
+ }
+ buf.WriteByte(']')
+ return sym.Pkg.Lookup(buf.String())
+}
+
+func (pr *pkgReader) objDictIdx(sym *types.Sym, idx int, implicits, explicits []*types.Type) *readerDict {
+ r := pr.newReader(relocObjDict, idx, syncObject1)
+
+ var dict readerDict
+
+ nimplicits := r.len()
+ nexplicits := r.len()
+
+ if nimplicits > len(implicits) || nexplicits != len(explicits) {
+ base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits))
+ }
+
+ dict.targs = append(implicits[:nimplicits:nimplicits], explicits...)
+ dict.implicits = nimplicits
+
+ // For stenciling, we can just skip over the type parameters.
+ for range dict.targs[dict.implicits:] {
+ // Skip past bounds without actually evaluating them.
+ r.sync(syncType)
+ if r.bool() {
+ r.len()
+ } else {
+ r.reloc(relocType)
+ }
+ }
+
+ dict.derived = make([]derivedInfo, r.len())
+ dict.derivedTypes = make([]*types.Type, len(dict.derived))
+ for i := range dict.derived {
+ dict.derived[i] = derivedInfo{r.reloc(relocType), r.bool()}
+ }
+
+ dict.funcs = make([]objInfo, r.len())
+ dict.funcsObj = make([]ir.Node, len(dict.funcs))
+ for i := range dict.funcs {
+ objIdx := r.reloc(relocObj)
+ targs := make([]typeInfo, r.len())
+ for j := range targs {
+ targs[j] = r.typInfo()
+ }
+ dict.funcs[i] = objInfo{idx: objIdx, explicits: targs}
+ }
+
+ return &dict
+}
+
+func (r *reader) typeParamNames() {
+ r.sync(syncTypeParamNames)
+
+ for range r.dict.targs[r.dict.implicits:] {
+ r.pos()
+ r.localIdent()
+ }
+}
+
+func (r *reader) method(rext *reader) *types.Field {
+ r.sync(syncMethod)
+ pos := r.pos()
+ pkg, sym := r.selector()
+ r.typeParamNames()
+ _, recv := r.param()
+ typ := r.signature(pkg, recv)
+
+ fnsym := sym
+ fnsym = ir.MethodSym(recv.Type, fnsym)
+ name := ir.NewNameAt(pos, fnsym)
+ setType(name, typ)
+
+ name.Func = ir.NewFunc(r.pos())
+ name.Func.Nname = name
+
+ rext.funcExt(name)
+
+ meth := types.NewField(name.Func.Pos(), sym, typ)
+ meth.Nname = name
+ meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0)
+
+ return meth
+}
+
+func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) {
+ r.sync(syncSym)
+ pkg = r.pkg()
+ if name := r.string(); name != "" {
+ sym = pkg.Lookup(name)
+ }
+ return
+}
+
+func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) {
+ r.sync(syncLocalIdent)
+ pkg = r.pkg()
+ if name := r.string(); name != "" {
+ sym = pkg.Lookup(name)
+ }
+ return
+}
+
+func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) {
+ r.sync(syncSelector)
+ origPkg = r.pkg()
+ name := r.string()
+ pkg := origPkg
+ if types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ sym = pkg.Lookup(name)
+ return
+}
+
+func (r *reader) hasTypeParams() bool {
+ return r.dict.hasTypeParams()
+}
+
+func (dict *readerDict) hasTypeParams() bool {
+ return dict != nil && len(dict.targs) != 0
+}
+
+// @@@ Compiler extensions
+
+func (r *reader) funcExt(name *ir.Name) {
+ r.sync(syncFuncExt)
+
+ name.Class = 0 // so MarkFunc doesn't complain
+ ir.MarkFunc(name)
+
+ fn := name.Func
+
+ // XXX: Workaround because linker doesn't know how to copy Pos.
+ if !fn.Pos().IsKnown() {
+ fn.SetPos(name.Pos())
+ }
+
+ // Normally, we only compile local functions, which saves redundant compilation work.
+ // n.Defn is not nil for local functions, and is nil for imported function. But for
+ // generic functions, we might have an instantiation that no other package has seen before.
+ // So we need to be conservative and compile it again.
+ //
+ // That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function.
+ // TODO(mdempsky,cuonglm): find a cleaner way to handle this.
+ if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() {
+ name.Defn = fn
+ }
+
+ fn.Pragma = r.pragmaFlag()
+ r.linkname(name)
+
+ typecheck.Func(fn)
+
+ if r.bool() {
+ fn.ABI = obj.ABI(r.uint64())
+
+ // Escape analysis.
+ for _, fs := range &types.RecvsParams {
+ for _, f := range fs(name.Type()).FieldSlice() {
+ f.Note = r.string()
+ }
+ }
+
+ if r.bool() {
+ fn.Inl = &ir.Inline{
+ Cost: int32(r.len()),
+ CanDelayResults: r.bool(),
+ }
+ r.addBody(name.Func)
+ }
+ } else {
+ r.addBody(name.Func)
+ }
+ r.sync(syncEOF)
+}
+
+func (r *reader) typeExt(name *ir.Name) {
+ r.sync(syncTypeExt)
+
+ typ := name.Type()
+
+ if r.hasTypeParams() {
+ // Set "RParams" (really type arguments here, not parameters) so
+ // this type is treated as "fully instantiated". This ensures the
+ // type descriptor is written out as DUPOK and method wrappers are
+ // generated even for imported types.
+ var targs []*types.Type
+ targs = append(targs, r.dict.targs...)
+ typ.SetRParams(targs)
+ }
+
+ name.SetPragma(r.pragmaFlag())
+ if name.Pragma()&ir.NotInHeap != 0 {
+ typ.SetNotInHeap(true)
+ }
+
+ typecheck.SetBaseTypeIndex(typ, r.int64(), r.int64())
+}
+
+func (r *reader) varExt(name *ir.Name) {
+ r.sync(syncVarExt)
+ r.linkname(name)
+}
+
+func (r *reader) linkname(name *ir.Name) {
+ assert(name.Op() == ir.ONAME)
+ r.sync(syncLinkname)
+
+ if idx := r.int64(); idx >= 0 {
+ lsym := name.Linksym()
+ lsym.SymIdx = int32(idx)
+ lsym.Set(obj.AttrIndexed, true)
+ } else {
+ name.Sym().Linkname = r.string()
+ }
+}
+
+func (r *reader) pragmaFlag() ir.PragmaFlag {
+ r.sync(syncPragma)
+ return ir.PragmaFlag(r.int())
+}
+
+// @@@ Function bodies
+
+// bodyReader tracks where the serialized IR for a function's body can
+// be found.
+var bodyReader = map[*ir.Func]pkgReaderIndex{}
+
+// todoBodies holds the list of function bodies that still need to be
+// constructed.
+var todoBodies []*ir.Func
+
+// todoBodiesDone signals that we constructed all function in todoBodies.
+// This is necessary to prevent reader.addBody adds thing to todoBodies
+// when nested inlining happens.
+var todoBodiesDone = false
+
+func (r *reader) addBody(fn *ir.Func) {
+ pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict}
+ bodyReader[fn] = pri
+
+ if fn.Nname.Defn == nil {
+ // Don't read in function body for imported functions.
+ // See comment in funcExt.
+ return
+ }
+
+ if r.curfn == nil && !todoBodiesDone {
+ todoBodies = append(todoBodies, fn)
+ return
+ }
+
+ pri.funcBody(fn)
+}
+
+func (pri pkgReaderIndex) funcBody(fn *ir.Func) {
+ r := pri.asReader(relocBody, syncFuncBody)
+ r.funcBody(fn)
+}
+
+func (r *reader) funcBody(fn *ir.Func) {
+ r.curfn = fn
+ r.closureVars = fn.ClosureVars
+
+ ir.WithFunc(fn, func() {
+ r.funcargs(fn)
+
+ if !r.bool() {
+ return
+ }
+
+ body := r.stmts()
+ if body == nil {
+ pos := src.NoXPos
+ if quirksMode() {
+ pos = funcParamsEndPos(fn)
+ }
+ body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(pos, nil))}
+ }
+ fn.Body = body
+ fn.Endlineno = r.pos()
+ })
+
+ r.marker.WriteTo(fn)
+}
+
+func (r *reader) funcargs(fn *ir.Func) {
+ sig := fn.Nname.Type()
+
+ if recv := sig.Recv(); recv != nil {
+ r.funcarg(recv, recv.Sym, ir.PPARAM)
+ }
+ for _, param := range sig.Params().FieldSlice() {
+ r.funcarg(param, param.Sym, ir.PPARAM)
+ }
+
+ for i, param := range sig.Results().FieldSlice() {
+ sym := types.OrigSym(param.Sym)
+
+ if sym == nil || sym.IsBlank() {
+ prefix := "~r"
+ if r.inlCall != nil {
+ prefix = "~R"
+ } else if sym != nil {
+ prefix = "~b"
+ }
+ sym = typecheck.LookupNum(prefix, i)
+ }
+
+ r.funcarg(param, sym, ir.PPARAMOUT)
+ }
+}
+
+func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) {
+ if sym == nil {
+ assert(ctxt == ir.PPARAM)
+ if r.inlCall != nil {
+ r.inlvars.Append(ir.BlankNode)
+ }
+ return
+ }
+
+ name := ir.NewNameAt(r.updatePos(param.Pos), sym)
+ setType(name, param.Type)
+ r.addLocal(name, ctxt)
+
+ if r.inlCall == nil {
+ if !r.funarghack {
+ param.Sym = sym
+ param.Nname = name
+ }
+ } else {
+ if ctxt == ir.PPARAMOUT {
+ r.retvars.Append(name)
+ } else {
+ r.inlvars.Append(name)
+ }
+ }
+}
+
+func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) {
+ assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT)
+
+ r.sync(syncAddLocal)
+ if enableSync {
+ want := r.int()
+ if have := len(r.locals); have != want {
+ base.FatalfAt(name.Pos(), "locals table has desynced")
+ }
+ }
+
+ name.SetUsed(true)
+ r.locals = append(r.locals, name)
+
+ // TODO(mdempsky): Move earlier.
+ if ir.IsBlank(name) {
+ return
+ }
+
+ if r.inlCall != nil {
+ if ctxt == ir.PAUTO {
+ name.SetInlLocal(true)
+ } else {
+ name.SetInlFormal(true)
+ ctxt = ir.PAUTO
+ }
+
+ // TODO(mdempsky): Rethink this hack.
+ if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 {
+ name.SetPos(r.inlCall.Pos())
+ name.SetInlFormal(false)
+ name.SetInlLocal(false)
+ }
+ }
+
+ name.Class = ctxt
+ name.Curfn = r.curfn
+
+ r.curfn.Dcl = append(r.curfn.Dcl, name)
+
+ if ctxt == ir.PAUTO {
+ name.SetFrameOffset(0)
+ }
+}
+
+func (r *reader) useLocal() *ir.Name {
+ r.sync(syncUseObjLocal)
+ if r.bool() {
+ return r.locals[r.len()]
+ }
+ return r.closureVars[r.len()]
+}
+
+func (r *reader) openScope() {
+ r.sync(syncOpenScope)
+ pos := r.pos()
+
+ if base.Flag.Dwarf {
+ r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl))
+ r.marker.Push(pos)
+ }
+}
+
+func (r *reader) closeScope() {
+ r.sync(syncCloseScope)
+ r.lastCloseScopePos = r.pos()
+
+ r.closeAnotherScope()
+}
+
+// closeAnotherScope is like closeScope, but it reuses the same mark
+// position as the last closeScope call. This is useful for "for" and
+// "if" statements, as their implicit blocks always end at the same
+// position as an explicit block.
+func (r *reader) closeAnotherScope() {
+ r.sync(syncCloseAnotherScope)
+
+ if base.Flag.Dwarf {
+ scopeVars := r.scopeVars[len(r.scopeVars)-1]
+ r.scopeVars = r.scopeVars[:len(r.scopeVars)-1]
+
+ // Quirkish: noder decides which scopes to keep before
+ // typechecking, whereas incremental typechecking during IR
+ // construction can result in new autotemps being allocated. To
+ // produce identical output, we ignore autotemps here for the
+ // purpose of deciding whether to retract the scope.
+ //
+ // This is important for net/http/fcgi, because it contains:
+ //
+ // var body io.ReadCloser
+ // if len(content) > 0 {
+ // body, req.pw = io.Pipe()
+ // } else { … }
+ //
+ // Notably, io.Pipe is inlinable, and inlining it introduces a ~R0
+ // variable at the call site.
+ //
+ // Noder does not preserve the scope where the io.Pipe() call
+ // resides, because it doesn't contain any declared variables in
+ // source. So the ~R0 variable ends up being assigned to the
+ // enclosing scope instead.
+ //
+ // However, typechecking this assignment also introduces
+ // autotemps, because io.Pipe's results need conversion before
+ // they can be assigned to their respective destination variables.
+ //
+ // TODO(mdempsky): We should probably just keep all scopes, and
+ // let dwarfgen take care of pruning them instead.
+ retract := true
+ for _, n := range r.curfn.Dcl[scopeVars:] {
+ if !n.AutoTemp() {
+ retract = false
+ break
+ }
+ }
+
+ if retract {
+ // no variables were declared in this scope, so we can retract it.
+ r.marker.Unpush()
+ } else {
+ r.marker.Pop(r.lastCloseScopePos)
+ }
+ }
+}
+
+// @@@ Statements
+
+func (r *reader) stmt() ir.Node {
+ switch stmts := r.stmts(); len(stmts) {
+ case 0:
+ return nil
+ case 1:
+ return stmts[0]
+ default:
+ return ir.NewBlockStmt(stmts[0].Pos(), stmts)
+ }
+}
+
+func (r *reader) stmts() []ir.Node {
+ assert(ir.CurFunc == r.curfn)
+ var res ir.Nodes
+
+ r.sync(syncStmts)
+ for {
+ tag := codeStmt(r.code(syncStmt1))
+ if tag == stmtEnd {
+ r.sync(syncStmtsEnd)
+ return res
+ }
+
+ if n := r.stmt1(tag, &res); n != nil {
+ res.Append(typecheck.Stmt(n))
+ }
+ }
+}
+
+func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node {
+ var label *types.Sym
+ if n := len(*out); n > 0 {
+ if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok {
+ label = ls.Label
+ }
+ }
+
+ switch tag {
+ default:
+ panic("unexpected statement")
+
+ case stmtAssign:
+ pos := r.pos()
+
+ // TODO(mdempsky): After quirks mode is gone, swap these
+ // statements so we visit LHS before RHS again.
+ rhs := r.exprList()
+ names, lhs := r.assignList()
+
+ if len(rhs) == 0 {
+ for _, name := range names {
+ as := ir.NewAssignStmt(pos, name, nil)
+ as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name))
+ out.Append(typecheck.Stmt(as))
+ }
+ return nil
+ }
+
+ if len(lhs) == 1 && len(rhs) == 1 {
+ n := ir.NewAssignStmt(pos, lhs[0], rhs[0])
+ n.Def = r.initDefn(n, names)
+ return n
+ }
+
+ n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs)
+ n.Def = r.initDefn(n, names)
+ return n
+
+ case stmtAssignOp:
+ op := r.op()
+ lhs := r.expr()
+ pos := r.pos()
+ rhs := r.expr()
+ return ir.NewAssignOpStmt(pos, op, lhs, rhs)
+
+ case stmtIncDec:
+ op := r.op()
+ lhs := r.expr()
+ pos := r.pos()
+ n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one))
+ n.IncDec = true
+ return n
+
+ case stmtBlock:
+ out.Append(r.blockStmt()...)
+ return nil
+
+ case stmtBranch:
+ pos := r.pos()
+ op := r.op()
+ sym := r.optLabel()
+ return ir.NewBranchStmt(pos, op, sym)
+
+ case stmtCall:
+ pos := r.pos()
+ op := r.op()
+ call := r.expr()
+ return ir.NewGoDeferStmt(pos, op, call)
+
+ case stmtExpr:
+ return r.expr()
+
+ case stmtFor:
+ return r.forStmt(label)
+
+ case stmtIf:
+ return r.ifStmt()
+
+ case stmtLabel:
+ pos := r.pos()
+ sym := r.label()
+ return ir.NewLabelStmt(pos, sym)
+
+ case stmtReturn:
+ pos := r.pos()
+ results := r.exprList()
+ return ir.NewReturnStmt(pos, results)
+
+ case stmtSelect:
+ return r.selectStmt(label)
+
+ case stmtSend:
+ pos := r.pos()
+ ch := r.expr()
+ value := r.expr()
+ return ir.NewSendStmt(pos, ch, value)
+
+ case stmtSwitch:
+ return r.switchStmt(label)
+
+ case stmtTypeDeclHack:
+ // fake "type _ = int" declaration to prevent inlining in quirks mode.
+ assert(quirksMode())
+
+ name := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.BlankNode.Sym())
+ name.SetAlias(true)
+ setType(name, types.Types[types.TINT])
+
+ n := ir.NewDecl(src.NoXPos, ir.ODCLTYPE, name)
+ n.SetTypecheck(1)
+ return n
+ }
+}
+
+func (r *reader) assignList() ([]*ir.Name, []ir.Node) {
+ lhs := make([]ir.Node, r.len())
+ var names []*ir.Name
+
+ for i := range lhs {
+ if r.bool() {
+ pos := r.pos()
+ _, sym := r.localIdent()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, sym)
+ lhs[i] = name
+ names = append(names, name)
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ continue
+ }
+
+ lhs[i] = r.expr()
+ }
+
+ return names, lhs
+}
+
+func (r *reader) blockStmt() []ir.Node {
+ r.sync(syncBlockStmt)
+ r.openScope()
+ stmts := r.stmts()
+ r.closeScope()
+ return stmts
+}
+
+func (r *reader) forStmt(label *types.Sym) ir.Node {
+ r.sync(syncForStmt)
+
+ r.openScope()
+
+ if r.bool() {
+ pos := r.pos()
+
+ // TODO(mdempsky): After quirks mode is gone, swap these
+ // statements so we read LHS before X again.
+ x := r.expr()
+ names, lhs := r.assignList()
+
+ body := r.blockStmt()
+ r.closeAnotherScope()
+
+ rang := ir.NewRangeStmt(pos, nil, nil, x, body)
+ if len(lhs) >= 1 {
+ rang.Key = lhs[0]
+ if len(lhs) >= 2 {
+ rang.Value = lhs[1]
+ }
+ }
+ rang.Def = r.initDefn(rang, names)
+ rang.Label = label
+ return rang
+ }
+
+ pos := r.pos()
+ init := r.stmt()
+ cond := r.expr()
+ post := r.stmt()
+ body := r.blockStmt()
+ r.closeAnotherScope()
+
+ stmt := ir.NewForStmt(pos, init, cond, post, body)
+ stmt.Label = label
+ return stmt
+}
+
+func (r *reader) ifStmt() ir.Node {
+ r.sync(syncIfStmt)
+ r.openScope()
+ pos := r.pos()
+ init := r.stmts()
+ cond := r.expr()
+ then := r.blockStmt()
+ els := r.stmts()
+ n := ir.NewIfStmt(pos, cond, then, els)
+ n.SetInit(init)
+ r.closeAnotherScope()
+ return n
+}
+
+func (r *reader) selectStmt(label *types.Sym) ir.Node {
+ r.sync(syncSelectStmt)
+
+ pos := r.pos()
+ clauses := make([]*ir.CommClause, r.len())
+ for i := range clauses {
+ if i > 0 {
+ r.closeScope()
+ }
+ r.openScope()
+
+ pos := r.pos()
+ comm := r.stmt()
+ body := r.stmts()
+
+ clauses[i] = ir.NewCommStmt(pos, comm, body)
+ }
+ if len(clauses) > 0 {
+ r.closeScope()
+ }
+ n := ir.NewSelectStmt(pos, clauses)
+ n.Label = label
+ return n
+}
+
+func (r *reader) switchStmt(label *types.Sym) ir.Node {
+ r.sync(syncSwitchStmt)
+
+ r.openScope()
+ pos := r.pos()
+ init := r.stmt()
+
+ var tag ir.Node
+ if r.bool() {
+ pos := r.pos()
+ var ident *ir.Ident
+ if r.bool() {
+ pos := r.pos()
+ sym := typecheck.Lookup(r.string())
+ ident = ir.NewIdent(pos, sym)
+ }
+ x := r.expr()
+ tag = ir.NewTypeSwitchGuard(pos, ident, x)
+ } else {
+ tag = r.expr()
+ }
+
+ tswitch, ok := tag.(*ir.TypeSwitchGuard)
+ if ok && tswitch.Tag == nil {
+ tswitch = nil
+ }
+
+ clauses := make([]*ir.CaseClause, r.len())
+ for i := range clauses {
+ if i > 0 {
+ r.closeScope()
+ }
+ r.openScope()
+
+ pos := r.pos()
+ cases := r.exprList()
+
+ clause := ir.NewCaseStmt(pos, cases, nil)
+ if tswitch != nil {
+ pos := r.pos()
+ typ := r.typ()
+
+ name := ir.NewNameAt(pos, tswitch.Tag.Sym())
+ setType(name, typ)
+ r.addLocal(name, ir.PAUTO)
+ clause.Var = name
+ name.Defn = tswitch
+ }
+
+ clause.Body = r.stmts()
+ clauses[i] = clause
+ }
+ if len(clauses) > 0 {
+ r.closeScope()
+ }
+ r.closeScope()
+
+ n := ir.NewSwitchStmt(pos, tag, clauses)
+ n.Label = label
+ if init != nil {
+ n.SetInit([]ir.Node{init})
+ }
+ return n
+}
+
+func (r *reader) label() *types.Sym {
+ r.sync(syncLabel)
+ name := r.string()
+ if r.inlCall != nil {
+ name = fmt.Sprintf("~%s·%d", name, inlgen)
+ }
+ return typecheck.Lookup(name)
+}
+
+func (r *reader) optLabel() *types.Sym {
+ r.sync(syncOptLabel)
+ if r.bool() {
+ return r.label()
+ }
+ return nil
+}
+
+// initDefn marks the given names as declared by defn and populates
+// its Init field with ODCL nodes. It then reports whether any names
+// were so declared, which can be used to initialize defn.Def.
+func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool {
+ if len(names) == 0 {
+ return false
+ }
+
+ init := make([]ir.Node, len(names))
+ for i, name := range names {
+ name.Defn = defn
+ init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
+ }
+ defn.SetInit(init)
+ return true
+}
+
+// @@@ Expressions
+
+// expr reads and returns a typechecked expression.
+func (r *reader) expr() (res ir.Node) {
+ defer func() {
+ if res != nil && res.Typecheck() == 0 {
+ base.FatalfAt(res.Pos(), "%v missed typecheck", res)
+ }
+ }()
+
+ switch tag := codeExpr(r.code(syncExpr)); tag {
+ default:
+ panic("unhandled expression")
+
+ case exprNone:
+ return nil
+
+ case exprBlank:
+ // blank only allowed in LHS of assignments
+ // TODO(mdempsky): Handle directly in assignList instead?
+ return typecheck.AssignExpr(ir.BlankNode)
+
+ case exprLocal:
+ return typecheck.Expr(r.useLocal())
+
+ case exprName:
+ // Callee instead of Expr allows builtins
+ // TODO(mdempsky): Handle builtins directly in exprCall, like method calls?
+ return typecheck.Callee(r.obj())
+
+ case exprType:
+ // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node.
+ n := ir.TypeNode(r.typ())
+ n.SetTypecheck(1)
+ return n
+
+ case exprConst:
+ pos := r.pos()
+ typ := r.typ()
+ val := FixValue(typ, r.value())
+ op := r.op()
+ orig := r.string()
+ return typecheck.Expr(OrigConst(pos, typ, val, op, orig))
+
+ case exprCompLit:
+ return r.compLit()
+
+ case exprFuncLit:
+ return r.funcLit()
+
+ case exprSelector:
+ x := r.expr()
+ pos := r.pos()
+ _, sym := r.selector()
+ n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
+ if n.Op() == ir.OMETHVALUE {
+ wrapper := methodValueWrapper{
+ rcvr: n.X.Type(),
+ method: n.Selection,
+ }
+ if r.importedDef() {
+ haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
+ } else {
+ needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
+ }
+ }
+ return n
+
+ case exprIndex:
+ x := r.expr()
+ pos := r.pos()
+ index := r.expr()
+ return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
+
+ case exprSlice:
+ x := r.expr()
+ pos := r.pos()
+ var index [3]ir.Node
+ for i := range index {
+ index[i] = r.expr()
+ }
+ op := ir.OSLICE
+ if index[2] != nil {
+ op = ir.OSLICE3
+ }
+ return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2]))
+
+ case exprAssert:
+ x := r.expr()
+ pos := r.pos()
+ typ := r.expr().(ir.Ntype)
+ return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ))
+
+ case exprUnaryOp:
+ op := r.op()
+ pos := r.pos()
+ x := r.expr()
+
+ switch op {
+ case ir.OADDR:
+ return typecheck.Expr(typecheck.NodAddrAt(pos, x))
+ case ir.ODEREF:
+ return typecheck.Expr(ir.NewStarExpr(pos, x))
+ }
+ return typecheck.Expr(ir.NewUnaryExpr(pos, op, x))
+
+ case exprBinaryOp:
+ op := r.op()
+ x := r.expr()
+ pos := r.pos()
+ y := r.expr()
+
+ switch op {
+ case ir.OANDAND, ir.OOROR:
+ return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y))
+ }
+ return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
+
+ case exprCall:
+ fun := r.expr()
+ if r.bool() { // method call
+ pos := r.pos()
+ _, sym := r.selector()
+ fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym))
+ }
+ pos := r.pos()
+ args := r.exprs()
+ dots := r.bool()
+ return typecheck.Call(pos, fun, args, dots)
+
+ case exprConvert:
+ typ := r.typ()
+ pos := r.pos()
+ x := r.expr()
+ return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x))
+ }
+}
+
+func (r *reader) compLit() ir.Node {
+ r.sync(syncCompLit)
+ pos := r.pos()
+ typ0 := r.typ()
+
+ typ := typ0
+ if typ.IsPtr() {
+ typ = typ.Elem()
+ }
+ if typ.Kind() == types.TFORW {
+ base.FatalfAt(pos, "unresolved composite literal type: %v", typ)
+ }
+ isStruct := typ.Kind() == types.TSTRUCT
+
+ elems := make([]ir.Node, r.len())
+ for i := range elems {
+ elemp := &elems[i]
+
+ if isStruct {
+ sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.len()), nil)
+ *elemp, elemp = sk, &sk.Value
+ } else if r.bool() {
+ kv := ir.NewKeyExpr(r.pos(), r.expr(), nil)
+ *elemp, elemp = kv, &kv.Value
+ }
+
+ *elemp = wrapName(r.pos(), r.expr())
+ }
+
+ lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), elems))
+ if typ0.IsPtr() {
+ lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit))
+ lit.SetType(typ0)
+ }
+ return lit
+}
+
+func wrapName(pos src.XPos, x ir.Node) ir.Node {
+ // These nodes do not carry line numbers.
+ // Introduce a wrapper node to give them the correct line.
+ switch ir.Orig(x).Op() {
+ case ir.OTYPE, ir.OLITERAL:
+ if x.Sym() == nil {
+ break
+ }
+ fallthrough
+ case ir.ONAME, ir.ONONAME, ir.OPACK, ir.ONIL:
+ p := ir.NewParenExpr(pos, x)
+ p.SetImplicit(true)
+ return p
+ }
+ return x
+}
+
+func (r *reader) funcLit() ir.Node {
+ r.sync(syncFuncLit)
+
+ pos := r.pos()
+ typPos := r.pos()
+ xtype2 := r.signature(types.LocalPkg, nil)
+
+ opos := pos
+ if quirksMode() {
+ opos = r.origPos(pos)
+ }
+
+ fn := ir.NewClosureFunc(opos, r.curfn != nil)
+ clo := fn.OClosure
+ ir.NameClosure(clo, r.curfn)
+
+ setType(fn.Nname, xtype2)
+ if quirksMode() {
+ fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2)
+ }
+ typecheck.Func(fn)
+ setType(clo, fn.Type())
+
+ fn.ClosureVars = make([]*ir.Name, 0, r.len())
+ for len(fn.ClosureVars) < cap(fn.ClosureVars) {
+ ir.NewClosureVar(r.pos(), fn, r.useLocal())
+ }
+
+ r.addBody(fn)
+
+ // TODO(mdempsky): Remove hard-coding of typecheck.Target.
+ return ir.UseClosure(clo, typecheck.Target)
+}
+
+func (r *reader) exprList() []ir.Node {
+ r.sync(syncExprList)
+ return r.exprs()
+}
+
+func (r *reader) exprs() []ir.Node {
+ r.sync(syncExprs)
+ nodes := make([]ir.Node, r.len())
+ if len(nodes) == 0 {
+ return nil // TODO(mdempsky): Unclear if this matters.
+ }
+ for i := range nodes {
+ nodes[i] = r.expr()
+ }
+ return nodes
+}
+
+func (r *reader) op() ir.Op {
+ r.sync(syncOp)
+ return ir.Op(r.len())
+}
+
+// @@@ Package initialization
+
+func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
+ if quirksMode() {
+ for i, n := 0, r.len(); i < n; i++ {
+ // Eagerly register position bases, so their filenames are
+ // assigned stable indices.
+ posBase := r.posBase()
+ _ = base.Ctxt.PosTable.XPos(src.MakePos(posBase, 0, 0))
+ }
+
+ for i, n := 0, r.len(); i < n; i++ {
+ // Eagerly resolve imported objects, so any filenames registered
+ // in the process are assigned stable indices too.
+ _, sym := r.qualifiedIdent()
+ typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
+ assert(sym.Def != nil)
+ }
+ }
+
+ cgoPragmas := make([][]string, r.len())
+ for i := range cgoPragmas {
+ cgoPragmas[i] = r.strings()
+ }
+ target.CgoPragmas = cgoPragmas
+
+ r.pkgDecls(target)
+
+ r.sync(syncEOF)
+}
+
+func (r *reader) pkgDecls(target *ir.Package) {
+ r.sync(syncDecls)
+ for {
+ switch code := codeDecl(r.code(syncDecl)); code {
+ default:
+ panic(fmt.Sprintf("unhandled decl: %v", code))
+
+ case declEnd:
+ return
+
+ case declFunc:
+ names := r.pkgObjs(target)
+ assert(len(names) == 1)
+ target.Decls = append(target.Decls, names[0].Func)
+
+ case declMethod:
+ typ := r.typ()
+ _, sym := r.selector()
+
+ method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0)
+ target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func)
+
+ case declVar:
+ pos := r.pos()
+ names := r.pkgObjs(target)
+ values := r.exprList()
+
+ if len(names) > 1 && len(values) == 1 {
+ as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values)
+ for _, name := range names {
+ as.Lhs.Append(name)
+ name.Defn = as
+ }
+ target.Decls = append(target.Decls, as)
+ } else {
+ for i, name := range names {
+ as := ir.NewAssignStmt(pos, name, nil)
+ if i < len(values) {
+ as.Y = values[i]
+ }
+ name.Defn = as
+ target.Decls = append(target.Decls, as)
+ }
+ }
+
+ if n := r.len(); n > 0 {
+ assert(len(names) == 1)
+ embeds := make([]ir.Embed, n)
+ for i := range embeds {
+ embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.strings()}
+ }
+ names[0].Embed = &embeds
+ target.Embeds = append(target.Embeds, names[0])
+ }
+
+ case declOther:
+ r.pkgObjs(target)
+ }
+ }
+}
+
+func (r *reader) pkgObjs(target *ir.Package) []*ir.Name {
+ r.sync(syncDeclNames)
+ nodes := make([]*ir.Name, r.len())
+ for i := range nodes {
+ r.sync(syncDeclName)
+
+ name := r.obj().(*ir.Name)
+ nodes[i] = name
+
+ sym := name.Sym()
+ if sym.IsBlank() {
+ continue
+ }
+
+ switch name.Class {
+ default:
+ base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class)
+
+ case ir.PEXTERN:
+ target.Externs = append(target.Externs, name)
+
+ case ir.PFUNC:
+ assert(name.Type().Recv() == nil)
+
+ // TODO(mdempsky): Cleaner way to recognize init?
+ if strings.HasPrefix(sym.Name, "init.") {
+ target.Inits = append(target.Inits, name.Func)
+ }
+ }
+
+ if types.IsExported(sym.Name) {
+ assert(!sym.OnExportList())
+ target.Exports = append(target.Exports, name)
+ sym.SetOnExportList(true)
+ }
+
+ if base.Flag.AsmHdr != "" {
+ assert(!sym.Asm())
+ target.Asms = append(target.Asms, name)
+ sym.SetAsm(true)
+ }
+ }
+
+ return nodes
+}
+
+// @@@ Inlining
+
+var inlgen = 0
+
+func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
+ // TODO(mdempsky): Turn callerfn into an explicit parameter.
+ callerfn := ir.CurFunc
+
+ pri, ok := bodyReader[fn]
+ if !ok {
+ // Assume it's an imported function or something that we don't
+ // have access to in quirks mode.
+ if haveLegacyImports {
+ return nil
+ }
+
+ base.FatalfAt(call.Pos(), "missing function body for call to %v", fn)
+ }
+
+ if fn.Inl.Body == nil {
+ expandInline(fn, pri)
+ }
+
+ r := pri.asReader(relocBody, syncFuncBody)
+
+ // TODO(mdempsky): This still feels clumsy. Can we do better?
+ tmpfn := ir.NewFunc(fn.Pos())
+ tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym())
+ tmpfn.Closgen = callerfn.Closgen
+ defer func() { callerfn.Closgen = tmpfn.Closgen }()
+
+ setType(tmpfn.Nname, fn.Type())
+ r.curfn = tmpfn
+
+ r.inlCaller = callerfn
+ r.inlCall = call
+ r.inlFunc = fn
+ r.inlTreeIndex = inlIndex
+ r.inlPosBases = make(map[*src.PosBase]*src.PosBase)
+
+ r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars))
+ for i, cv := range r.inlFunc.ClosureVars {
+ r.closureVars[i] = cv.Outer
+ }
+
+ r.funcargs(fn)
+
+ assert(r.bool()) // have body
+ r.delayResults = fn.Inl.CanDelayResults
+
+ r.retlabel = typecheck.AutoLabel(".i")
+ inlgen++
+
+ init := ir.TakeInit(call)
+
+ // For normal function calls, the function callee expression
+ // may contain side effects. Make sure to preserve these,
+ // if necessary (#42703).
+ if call.Op() == ir.OCALLFUNC {
+ inline.CalleeEffects(&init, call.X)
+ }
+
+ var args ir.Nodes
+ if call.Op() == ir.OCALLMETH {
+ base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
+ }
+ args.Append(call.Args...)
+
+ // Create assignment to declare and initialize inlvars.
+ as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args)
+ as2.Def = true
+ var as2init ir.Nodes
+ for _, name := range r.inlvars {
+ if ir.IsBlank(name) {
+ continue
+ }
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
+ name.Defn = as2
+ }
+ as2.SetInit(as2init)
+ init.Append(typecheck.Stmt(as2))
+
+ if !r.delayResults {
+ // If not delaying retvars, declare and zero initialize the
+ // result variables now.
+ for _, name := range r.retvars {
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
+ ras := ir.NewAssignStmt(call.Pos(), name, nil)
+ init.Append(typecheck.Stmt(ras))
+ }
+ }
+
+ // Add an inline mark just before the inlined body.
+ // This mark is inline in the code so that it's a reasonable spot
+ // to put a breakpoint. Not sure if that's really necessary or not
+ // (in which case it could go at the end of the function instead).
+ // Note issue 28603.
+ init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex)))
+
+ nparams := len(r.curfn.Dcl)
+
+ ir.WithFunc(r.curfn, func() {
+ r.curfn.Body = r.stmts()
+ r.curfn.Endlineno = r.pos()
+
+ deadcode.Func(r.curfn)
+
+ // Replace any "return" statements within the function body.
+ var edit func(ir.Node) ir.Node
+ edit = func(n ir.Node) ir.Node {
+ if ret, ok := n.(*ir.ReturnStmt); ok {
+ n = typecheck.Stmt(r.inlReturn(ret))
+ }
+ ir.EditChildren(n, edit)
+ return n
+ }
+ edit(r.curfn)
+ })
+
+ body := ir.Nodes(r.curfn.Body)
+
+ // Quirk: If deadcode elimination turned a non-empty function into
+ // an empty one, we need to set the position for the empty block
+ // left behind to the the inlined position for src.NoXPos, so that
+ // an empty string gets added into the DWARF file name listing at
+ // the appropriate index.
+ if quirksMode() && len(body) == 1 {
+ if block, ok := body[0].(*ir.BlockStmt); ok && len(block.List) == 0 {
+ block.SetPos(r.updatePos(src.NoXPos))
+ }
+ }
+
+ // Quirkish: We need to eagerly prune variables added during
+ // inlining, but removed by deadcode.FuncBody above. Unused
+ // variables will get removed during stack frame layout anyway, but
+ // len(fn.Dcl) ends up influencing things like autotmp naming.
+
+ used := usedLocals(body)
+
+ for i, name := range r.curfn.Dcl {
+ if i < nparams || used.Has(name) {
+ name.Curfn = callerfn
+ callerfn.Dcl = append(callerfn.Dcl, name)
+
+ // Quirkish. TODO(mdempsky): Document why.
+ if name.AutoTemp() {
+ name.SetEsc(ir.EscUnknown)
+
+ if base.Flag.GenDwarfInl != 0 {
+ name.SetInlLocal(true)
+ } else {
+ name.SetPos(r.inlCall.Pos())
+ }
+ }
+ }
+ }
+
+ body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
+
+ res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...))
+ res.SetInit(init)
+ res.SetType(call.Type())
+ res.SetTypecheck(1)
+
+ // Inlining shouldn't add any functions to todoBodies.
+ assert(len(todoBodies) == 0)
+
+ return res
+}
+
+// inlReturn returns a statement that can substitute for the given
+// return statement when inlining.
+func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt {
+ pos := r.inlCall.Pos()
+
+ block := ir.TakeInit(ret)
+
+ if results := ret.Results; len(results) != 0 {
+ assert(len(r.retvars) == len(results))
+
+ as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results)
+
+ if r.delayResults {
+ for _, name := range r.retvars {
+ // TODO(mdempsky): Use inlined position of name.Pos() instead?
+ name := name.(*ir.Name)
+ block.Append(ir.NewDecl(pos, ir.ODCL, name))
+ name.Defn = as2
+ }
+ }
+
+ block.Append(as2)
+ }
+
+ block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel))
+ return ir.NewBlockStmt(pos, block)
+}
+
+// expandInline reads in an extra copy of IR to populate
+// fn.Inl.{Dcl,Body}.
+func expandInline(fn *ir.Func, pri pkgReaderIndex) {
+ // TODO(mdempsky): Remove this function. It's currently needed by
+ // dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to
+ // create abstract function DIEs. But we should be able to provide it
+ // with the same information some other way.
+
+ fndcls := len(fn.Dcl)
+ topdcls := len(typecheck.Target.Decls)
+
+ tmpfn := ir.NewFunc(fn.Pos())
+ tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym())
+ tmpfn.ClosureVars = fn.ClosureVars
+
+ {
+ r := pri.asReader(relocBody, syncFuncBody)
+ setType(tmpfn.Nname, fn.Type())
+
+ // Don't change parameter's Sym/Nname fields.
+ r.funarghack = true
+
+ r.funcBody(tmpfn)
+
+ ir.WithFunc(tmpfn, func() {
+ deadcode.Func(tmpfn)
+ })
+ }
+
+ used := usedLocals(tmpfn.Body)
+
+ for _, name := range tmpfn.Dcl {
+ if name.Class != ir.PAUTO || used.Has(name) {
+ name.Curfn = fn
+ fn.Inl.Dcl = append(fn.Inl.Dcl, name)
+ }
+ }
+ fn.Inl.Body = tmpfn.Body
+
+ // Double check that we didn't change fn.Dcl by accident.
+ assert(fndcls == len(fn.Dcl))
+
+ // typecheck.Stmts may have added function literals to
+ // typecheck.Target.Decls. Remove them again so we don't risk trying
+ // to compile them multiple times.
+ typecheck.Target.Decls = typecheck.Target.Decls[:topdcls]
+}
+
+// usedLocals returns a set of local variables that are used within body.
+func usedLocals(body []ir.Node) ir.NameSet {
+ var used ir.NameSet
+ ir.VisitList(body, func(n ir.Node) {
+ if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
+ used.Add(n)
+ }
+ })
+ return used
+}
+
+// @@@ Method wrappers
+
+// needWrapperTypes lists types for which we may need to generate
+// method wrappers.
+var needWrapperTypes []*types.Type
+
+// haveWrapperTypes lists types for which we know we already have
+// method wrappers, because we found the type in an imported package.
+var haveWrapperTypes []*types.Type
+
+// needMethodValueWrappers lists methods for which we may need to
+// generate method value wrappers.
+var needMethodValueWrappers []methodValueWrapper
+
+// haveMethodValueWrappers lists methods for which we know we already
+// have method value wrappers, because we found it in an imported
+// package.
+var haveMethodValueWrappers []methodValueWrapper
+
+type methodValueWrapper struct {
+ rcvr *types.Type
+ method *types.Field
+}
+
+func (r *reader) needWrapper(typ *types.Type) {
+ if typ.IsPtr() {
+ return
+ }
+
+ // If a type was found in an imported package, then we can assume
+ // that package (or one of its transitive dependencies) already
+ // generated method wrappers for it.
+ if r.importedDef() {
+ haveWrapperTypes = append(haveWrapperTypes, typ)
+ } else {
+ needWrapperTypes = append(needWrapperTypes, typ)
+ }
+}
+
+func (r *reader) importedDef() bool {
+ // If a type was found in an imported package, then we can assume
+ // that package (or one of its transitive dependencies) already
+ // generated method wrappers for it.
+ //
+ // Exception: If we're instantiating an imported generic type or
+ // function, we might be instantiating it with type arguments not
+ // previously seen before.
+ //
+ // TODO(mdempsky): Distinguish when a generic function or type was
+ // instantiated in an imported package so that we can add types to
+ // haveWrapperTypes instead.
+ return r.p != localPkgReader && !r.hasTypeParams()
+}
+
+func MakeWrappers(target *ir.Package) {
+ // Only unified IR in non-quirks mode emits its own wrappers.
+ if base.Debug.Unified == 0 || quirksMode() {
+ return
+ }
+
+ // always generate a wrapper for error.Error (#29304)
+ needWrapperTypes = append(needWrapperTypes, types.ErrorType)
+
+ seen := make(map[string]*types.Type)
+
+ for _, typ := range haveWrapperTypes {
+ wrapType(typ, target, seen, false)
+ }
+ haveWrapperTypes = nil
+
+ for _, typ := range needWrapperTypes {
+ wrapType(typ, target, seen, true)
+ }
+ needWrapperTypes = nil
+
+ for _, wrapper := range haveMethodValueWrappers {
+ wrapMethodValue(wrapper.rcvr, wrapper.method, target, false)
+ }
+ haveMethodValueWrappers = nil
+
+ for _, wrapper := range needMethodValueWrappers {
+ wrapMethodValue(wrapper.rcvr, wrapper.method, target, true)
+ }
+ needMethodValueWrappers = nil
+}
+
+func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) {
+ key := typ.LinkString()
+ if prev := seen[key]; prev != nil {
+ if !types.Identical(typ, prev) {
+ base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key)
+ }
+ return
+ }
+ seen[key] = typ
+
+ if !needed {
+ // Only called to add to 'seen'.
+ return
+ }
+
+ if !typ.IsInterface() {
+ typecheck.CalcMethods(typ)
+ }
+ for _, meth := range typ.AllMethods().Slice() {
+ if meth.Sym.IsBlank() || !meth.IsMethod() {
+ base.FatalfAt(meth.Pos, "invalid method: %v", meth)
+ }
+
+ methodWrapper(0, typ, meth, target)
+
+ // For non-interface types, we also want *T wrappers.
+ if !typ.IsInterface() {
+ methodWrapper(1, typ, meth, target)
+
+ // For not-in-heap types, *T is a scalar, not pointer shaped,
+ // so the interface wrappers use **T.
+ if typ.NotInHeap() {
+ methodWrapper(2, typ, meth, target)
+ }
+ }
+ }
+}
+
+func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
+ wrapper := tbase
+ for i := 0; i < derefs; i++ {
+ wrapper = types.NewPtr(wrapper)
+ }
+
+ sym := ir.MethodSym(wrapper, method.Sym)
+ base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
+ sym.SetSiggen(true)
+
+ wrappee := method.Type.Recv().Type
+ if types.Identical(wrapper, wrappee) ||
+ !types.IsMethodApplicable(wrapper, method) ||
+ !reflectdata.NeedEmit(tbase) {
+ return
+ }
+
+ // TODO(mdempsky): Use method.Pos instead?
+ pos := base.AutogeneratedPos
+
+ fn := newWrapperFunc(pos, sym, wrapper, method)
+
+ var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
+
+ // For simple *T wrappers around T methods, panicwrap produces a
+ // nicer panic message.
+ if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
+ cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
+ then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
+ fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
+ }
+
+ // typecheck will add one implicit deref, if necessary,
+ // but not-in-heap types require more for their **T wrappers.
+ for i := 1; i < derefs; i++ {
+ recv = Implicit(ir.NewStarExpr(pos, recv))
+ }
+
+ addTailCall(pos, fn, recv, method)
+
+ finishWrapperFunc(fn, target)
+}
+
+func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
+ sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
+ if sym.Uniq() {
+ return
+ }
+ sym.SetUniq(true)
+
+ // TODO(mdempsky): Use method.Pos instead?
+ pos := base.AutogeneratedPos
+
+ fn := newWrapperFunc(pos, sym, nil, method)
+ sym.Def = fn.Nname
+
+ // Declare and initialize variable holding receiver.
+ recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
+
+ if !needed {
+ typecheck.Func(fn)
+ return
+ }
+
+ addTailCall(pos, fn, recv, method)
+
+ finishWrapperFunc(fn, target)
+}
+
+func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
+ fn := ir.NewFunc(pos)
+ fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
+
+ name := ir.NewNameAt(pos, sym)
+ ir.MarkFunc(name)
+ name.Func = fn
+ name.Defn = fn
+ fn.Nname = name
+
+ sig := newWrapperType(wrapper, method)
+ setType(name, sig)
+
+ // TODO(mdempsky): De-duplicate with similar logic in funcargs.
+ defParams := func(class ir.Class, params *types.Type) {
+ for _, param := range params.FieldSlice() {
+ name := ir.NewNameAt(param.Pos, param.Sym)
+ name.Class = class
+ setType(name, param.Type)
+
+ name.Curfn = fn
+ fn.Dcl = append(fn.Dcl, name)
+
+ param.Nname = name
+ }
+ }
+
+ defParams(ir.PPARAM, sig.Recvs())
+ defParams(ir.PPARAM, sig.Params())
+ defParams(ir.PPARAMOUT, sig.Results())
+
+ return fn
+}
+
+func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
+ typecheck.Func(fn)
+
+ ir.WithFunc(fn, func() {
+ typecheck.Stmts(fn.Body)
+ })
+
+ // We generate wrappers after the global inlining pass,
+ // so we're responsible for applying inlining ourselves here.
+ inline.InlineCalls(fn)
+
+ target.Decls = append(target.Decls, fn)
+}
+
+// newWrapperType returns a copy of the given signature type, but with
+// the receiver parameter type substituted with recvType.
+// If recvType is nil, newWrapperType returns a signature
+// without a receiver parameter.
+func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
+ clone := func(params []*types.Field) []*types.Field {
+ res := make([]*types.Field, len(params))
+ for i, param := range params {
+ sym := param.Sym
+ if sym == nil || sym.Name == "_" {
+ sym = typecheck.LookupNum(".anon", i)
+ }
+ res[i] = types.NewField(param.Pos, sym, param.Type)
+ res[i].SetIsDDD(param.IsDDD())
+ }
+ return res
+ }
+
+ sig := method.Type
+
+ var recv *types.Field
+ if recvType != nil {
+ recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
+ }
+ params := clone(sig.Params().FieldSlice())
+ results := clone(sig.Results().FieldSlice())
+
+ return types.NewSignature(types.NoPkg, recv, nil, params, results)
+}
+
+func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
+ sig := fn.Nname.Type()
+ args := make([]ir.Node, sig.NumParams())
+ for i, param := range sig.Params().FieldSlice() {
+ args[i] = param.Nname.(*ir.Name)
+ }
+
+ // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
+ // Not urgent though, because tail calls are currently incompatible with regabi anyway.
+
+ fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
+
+ dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
+ call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
+
+ if method.Type.NumResults() == 0 {
+ fn.Body.Append(call)
+ return
+ }
+
+ ret := ir.NewReturnStmt(pos, nil)
+ ret.Results = []ir.Node{call}
+ fn.Body.Append(ret)
+}
diff --git a/src/cmd/compile/internal/noder/reader2.go b/src/cmd/compile/internal/noder/reader2.go
new file mode 100644
index 0000000000000000000000000000000000000000..9396c0c87c5e3501fd7ffbc4a025fc7ded331ef1
--- /dev/null
+++ b/src/cmd/compile/internal/noder/reader2.go
@@ -0,0 +1,508 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+type pkgReader2 struct {
+ pkgDecoder
+
+ ctxt *types2.Context
+ imports map[string]*types2.Package
+
+ posBases []*syntax.PosBase
+ pkgs []*types2.Package
+ typs []types2.Type
+}
+
+func readPackage2(ctxt *types2.Context, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
+ pr := pkgReader2{
+ pkgDecoder: input,
+
+ ctxt: ctxt,
+ imports: imports,
+
+ posBases: make([]*syntax.PosBase, input.numElems(relocPosBase)),
+ pkgs: make([]*types2.Package, input.numElems(relocPkg)),
+ typs: make([]types2.Type, input.numElems(relocType)),
+ }
+
+ r := pr.newReader(relocMeta, publicRootIdx, syncPublic)
+ pkg := r.pkg()
+ r.bool() // has init
+
+ for i, n := 0, r.len(); i < n; i++ {
+ // As if r.obj(), but avoiding the Scope.Lookup call,
+ // to avoid eager loading of imports.
+ r.sync(syncObject)
+ assert(!r.bool())
+ r.p.objIdx(r.reloc(relocObj))
+ assert(r.len() == 0)
+ }
+
+ r.sync(syncEOF)
+
+ pkg.MarkComplete()
+ return pkg
+}
+
+type reader2 struct {
+ decoder
+
+ p *pkgReader2
+
+ dict *reader2Dict
+}
+
+type reader2Dict struct {
+ bounds []typeInfo
+
+ tparams []*types2.TypeParam
+
+ derived []derivedInfo
+ derivedTypes []types2.Type
+}
+
+type reader2TypeBound struct {
+ derived bool
+ boundIdx int
+}
+
+func (pr *pkgReader2) newReader(k reloc, idx int, marker syncMarker) *reader2 {
+ return &reader2{
+ decoder: pr.newDecoder(k, idx, marker),
+ p: pr,
+ }
+}
+
+// @@@ Positions
+
+func (r *reader2) pos() syntax.Pos {
+ r.sync(syncPos)
+ if !r.bool() {
+ return syntax.Pos{}
+ }
+
+ // TODO(mdempsky): Delta encoding.
+ posBase := r.posBase()
+ line := r.uint()
+ col := r.uint()
+ return syntax.MakePos(posBase, line, col)
+}
+
+func (r *reader2) posBase() *syntax.PosBase {
+ return r.p.posBaseIdx(r.reloc(relocPosBase))
+}
+
+func (pr *pkgReader2) posBaseIdx(idx int) *syntax.PosBase {
+ if b := pr.posBases[idx]; b != nil {
+ return b
+ }
+
+ r := pr.newReader(relocPosBase, idx, syncPosBase)
+ var b *syntax.PosBase
+
+ filename := r.string()
+
+ if r.bool() {
+ b = syntax.NewTrimmedFileBase(filename, true)
+ } else {
+ pos := r.pos()
+ line := r.uint()
+ col := r.uint()
+ b = syntax.NewLineBase(pos, filename, true, line, col)
+ }
+
+ pr.posBases[idx] = b
+ return b
+}
+
+// @@@ Packages
+
+func (r *reader2) pkg() *types2.Package {
+ r.sync(syncPkg)
+ return r.p.pkgIdx(r.reloc(relocPkg))
+}
+
+func (pr *pkgReader2) pkgIdx(idx int) *types2.Package {
+ // TODO(mdempsky): Consider using some non-nil pointer to indicate
+ // the universe scope, so we don't need to keep re-reading it.
+ if pkg := pr.pkgs[idx]; pkg != nil {
+ return pkg
+ }
+
+ pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg()
+ pr.pkgs[idx] = pkg
+ return pkg
+}
+
+func (r *reader2) doPkg() *types2.Package {
+ path := r.string()
+ if path == "builtin" {
+ return nil // universe
+ }
+ if path == "" {
+ path = r.p.pkgPath
+ }
+
+ if pkg := r.p.imports[path]; pkg != nil {
+ return pkg
+ }
+
+ name := r.string()
+ height := r.len()
+
+ pkg := types2.NewPackageHeight(path, name, height)
+ r.p.imports[path] = pkg
+
+ // TODO(mdempsky): The list of imported packages is important for
+ // go/types, but we could probably skip populating it for types2.
+ imports := make([]*types2.Package, r.len())
+ for i := range imports {
+ imports[i] = r.pkg()
+ }
+ pkg.SetImports(imports)
+
+ return pkg
+}
+
+// @@@ Types
+
+func (r *reader2) typ() types2.Type {
+ return r.p.typIdx(r.typInfo(), r.dict)
+}
+
+func (r *reader2) typInfo() typeInfo {
+ r.sync(syncType)
+ if r.bool() {
+ return typeInfo{idx: r.len(), derived: true}
+ }
+ return typeInfo{idx: r.reloc(relocType), derived: false}
+}
+
+func (pr *pkgReader2) typIdx(info typeInfo, dict *reader2Dict) types2.Type {
+ idx := info.idx
+ var where *types2.Type
+ if info.derived {
+ where = &dict.derivedTypes[idx]
+ idx = dict.derived[idx].idx
+ } else {
+ where = &pr.typs[idx]
+ }
+
+ if typ := *where; typ != nil {
+ return typ
+ }
+
+ r := pr.newReader(relocType, idx, syncTypeIdx)
+ r.dict = dict
+
+ typ := r.doTyp()
+ assert(typ != nil)
+
+ // See comment in pkgReader.typIdx explaining how this happens.
+ if prev := *where; prev != nil {
+ return prev
+ }
+
+ *where = typ
+ return typ
+}
+
+func (r *reader2) doTyp() (res types2.Type) {
+ switch tag := codeType(r.code(syncType)); tag {
+ default:
+ base.FatalfAt(src.NoXPos, "unhandled type tag: %v", tag)
+ panic("unreachable")
+
+ case typeBasic:
+ return types2.Typ[r.len()]
+
+ case typeNamed:
+ obj, targs := r.obj()
+ name := obj.(*types2.TypeName)
+ if len(targs) != 0 {
+ t, _ := types2.Instantiate(r.p.ctxt, name.Type(), targs, false)
+ return t
+ }
+ return name.Type()
+
+ case typeTypeParam:
+ return r.dict.tparams[r.len()]
+
+ case typeArray:
+ len := int64(r.uint64())
+ return types2.NewArray(r.typ(), len)
+ case typeChan:
+ dir := types2.ChanDir(r.len())
+ return types2.NewChan(dir, r.typ())
+ case typeMap:
+ return types2.NewMap(r.typ(), r.typ())
+ case typePointer:
+ return types2.NewPointer(r.typ())
+ case typeSignature:
+ return r.signature(nil)
+ case typeSlice:
+ return types2.NewSlice(r.typ())
+ case typeStruct:
+ return r.structType()
+ case typeInterface:
+ return r.interfaceType()
+ case typeUnion:
+ return r.unionType()
+ }
+}
+
+func (r *reader2) structType() *types2.Struct {
+ fields := make([]*types2.Var, r.len())
+ var tags []string
+ for i := range fields {
+ pos := r.pos()
+ pkg, name := r.selector()
+ ftyp := r.typ()
+ tag := r.string()
+ embedded := r.bool()
+
+ fields[i] = types2.NewField(pos, pkg, name, ftyp, embedded)
+ if tag != "" {
+ for len(tags) < i {
+ tags = append(tags, "")
+ }
+ tags = append(tags, tag)
+ }
+ }
+ return types2.NewStruct(fields, tags)
+}
+
+func (r *reader2) unionType() *types2.Union {
+ terms := make([]*types2.Term, r.len())
+ for i := range terms {
+ terms[i] = types2.NewTerm(r.bool(), r.typ())
+ }
+ return types2.NewUnion(terms)
+}
+
+func (r *reader2) interfaceType() *types2.Interface {
+ methods := make([]*types2.Func, r.len())
+ embeddeds := make([]types2.Type, r.len())
+
+ for i := range methods {
+ pos := r.pos()
+ pkg, name := r.selector()
+ mtyp := r.signature(nil)
+ methods[i] = types2.NewFunc(pos, pkg, name, mtyp)
+ }
+
+ for i := range embeddeds {
+ embeddeds[i] = r.typ()
+ }
+
+ return types2.NewInterfaceType(methods, embeddeds)
+}
+
+func (r *reader2) signature(recv *types2.Var) *types2.Signature {
+ r.sync(syncSignature)
+
+ params := r.params()
+ results := r.params()
+ variadic := r.bool()
+
+ return types2.NewSignatureType(recv, nil, nil, params, results, variadic)
+}
+
+func (r *reader2) params() *types2.Tuple {
+ r.sync(syncParams)
+ params := make([]*types2.Var, r.len())
+ for i := range params {
+ params[i] = r.param()
+ }
+ return types2.NewTuple(params...)
+}
+
+func (r *reader2) param() *types2.Var {
+ r.sync(syncParam)
+
+ pos := r.pos()
+ pkg, name := r.localIdent()
+ typ := r.typ()
+
+ return types2.NewParam(pos, pkg, name, typ)
+}
+
+// @@@ Objects
+
+func (r *reader2) obj() (types2.Object, []types2.Type) {
+ r.sync(syncObject)
+
+ assert(!r.bool())
+
+ pkg, name := r.p.objIdx(r.reloc(relocObj))
+ obj := pkg.Scope().Lookup(name)
+
+ targs := make([]types2.Type, r.len())
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+
+ return obj, targs
+}
+
+func (pr *pkgReader2) objIdx(idx int) (*types2.Package, string) {
+ rname := pr.newReader(relocName, idx, syncObject1)
+
+ objPkg, objName := rname.qualifiedIdent()
+ assert(objName != "")
+
+ tag := codeObj(rname.code(syncCodeObj))
+
+ if tag == objStub {
+ assert(objPkg == nil || objPkg == types2.Unsafe)
+ return objPkg, objName
+ }
+
+ dict := pr.objDictIdx(idx)
+
+ r := pr.newReader(relocObj, idx, syncObject1)
+ r.dict = dict
+
+ objPkg.Scope().InsertLazy(objName, func() types2.Object {
+ switch tag {
+ default:
+ panic("weird")
+
+ case objAlias:
+ pos := r.pos()
+ typ := r.typ()
+ return types2.NewTypeName(pos, objPkg, objName, typ)
+
+ case objConst:
+ pos := r.pos()
+ typ := r.typ()
+ val := r.value()
+ return types2.NewConst(pos, objPkg, objName, typ, val)
+
+ case objFunc:
+ pos := r.pos()
+ tparams := r.typeParamNames()
+ sig := r.signature(nil)
+ sig.SetTypeParams(tparams)
+ return types2.NewFunc(pos, objPkg, objName, sig)
+
+ case objType:
+ pos := r.pos()
+
+ return types2.NewTypeNameLazy(pos, objPkg, objName, func(named *types2.Named) (tparams []*types2.TypeParam, underlying types2.Type, methods []*types2.Func) {
+ tparams = r.typeParamNames()
+
+ // TODO(mdempsky): Rewrite receiver types to underlying is an
+ // Interface? The go/types importer does this (I think because
+ // unit tests expected that), but cmd/compile doesn't care
+ // about it, so maybe we can avoid worrying about that here.
+ underlying = r.typ().Underlying()
+
+ methods = make([]*types2.Func, r.len())
+ for i := range methods {
+ methods[i] = r.method()
+ }
+
+ return
+ })
+
+ case objVar:
+ pos := r.pos()
+ typ := r.typ()
+ return types2.NewVar(pos, objPkg, objName, typ)
+ }
+ })
+
+ return objPkg, objName
+}
+
+func (pr *pkgReader2) objDictIdx(idx int) *reader2Dict {
+ r := pr.newReader(relocObjDict, idx, syncObject1)
+
+ var dict reader2Dict
+
+ if implicits := r.len(); implicits != 0 {
+ base.Fatalf("unexpected object with %v implicit type parameter(s)", implicits)
+ }
+
+ dict.bounds = make([]typeInfo, r.len())
+ for i := range dict.bounds {
+ dict.bounds[i] = r.typInfo()
+ }
+
+ dict.derived = make([]derivedInfo, r.len())
+ dict.derivedTypes = make([]types2.Type, len(dict.derived))
+ for i := range dict.derived {
+ dict.derived[i] = derivedInfo{r.reloc(relocType), r.bool()}
+ }
+
+ // function references follow, but reader2 doesn't need those
+
+ return &dict
+}
+
+func (r *reader2) typeParamNames() []*types2.TypeParam {
+ r.sync(syncTypeParamNames)
+
+ // Note: This code assumes it only processes objects without
+ // implement type parameters. This is currently fine, because
+ // reader2 is only used to read in exported declarations, which are
+ // always package scoped.
+
+ if len(r.dict.bounds) == 0 {
+ return nil
+ }
+
+ // Careful: Type parameter lists may have cycles. To allow for this,
+ // we construct the type parameter list in two passes: first we
+ // create all the TypeNames and TypeParams, then we construct and
+ // set the bound type.
+
+ r.dict.tparams = make([]*types2.TypeParam, len(r.dict.bounds))
+ for i := range r.dict.bounds {
+ pos := r.pos()
+ pkg, name := r.localIdent()
+
+ tname := types2.NewTypeName(pos, pkg, name, nil)
+ r.dict.tparams[i] = types2.NewTypeParam(tname, nil)
+ }
+
+ for i, bound := range r.dict.bounds {
+ r.dict.tparams[i].SetConstraint(r.p.typIdx(bound, r.dict))
+ }
+
+ return r.dict.tparams
+}
+
+func (r *reader2) method() *types2.Func {
+ r.sync(syncMethod)
+ pos := r.pos()
+ pkg, name := r.selector()
+
+ rparams := r.typeParamNames()
+ sig := r.signature(r.param())
+ sig.SetRecvTypeParams(rparams)
+
+ _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
+ return types2.NewFunc(pos, pkg, name, sig)
+}
+
+func (r *reader2) qualifiedIdent() (*types2.Package, string) { return r.ident(syncSym) }
+func (r *reader2) localIdent() (*types2.Package, string) { return r.ident(syncLocalIdent) }
+func (r *reader2) selector() (*types2.Package, string) { return r.ident(syncSelector) }
+
+func (r *reader2) ident(marker syncMarker) (*types2.Package, string) {
+ r.sync(marker)
+ return r.pkg(), r.string()
+}
diff --git a/src/cmd/compile/internal/noder/reloc.go b/src/cmd/compile/internal/noder/reloc.go
new file mode 100644
index 0000000000000000000000000000000000000000..669a6182e62eb250dd2f0ac817e4b5712d06ece7
--- /dev/null
+++ b/src/cmd/compile/internal/noder/reloc.go
@@ -0,0 +1,42 @@
+// UNREVIEWED
+
+// 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 noder
+
+// A reloc indicates a particular section within a unified IR export.
+//
+// TODO(mdempsky): Rename to "section" or something similar?
+type reloc int
+
+// A relocEnt (relocation entry) is an entry in an atom's local
+// reference table.
+//
+// TODO(mdempsky): Rename this too.
+type relocEnt struct {
+ kind reloc
+ idx int
+}
+
+// Reserved indices within the meta relocation section.
+const (
+ publicRootIdx = 0
+ privateRootIdx = 1
+)
+
+const (
+ relocString reloc = iota
+ relocMeta
+ relocPosBase
+ relocPkg
+ relocName
+ relocType
+ relocObj
+ relocObjExt
+ relocObjDict
+ relocBody
+
+ numRelocs = iota
+)
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index 3ebc8dff6d84322d2d5c2379e9497a87ebb1d73b..62c306b89e2e6862a725076a5b26e5cb9a89f68d 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -8,267 +8,750 @@
package noder
import (
- "bytes"
"cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/objw"
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/internal/obj"
"cmd/internal/src"
"fmt"
- "strings"
+ "go/constant"
)
-// For catching problems as we add more features
-// TODO(danscales): remove assertions or replace with base.FatalfAt()
+// Enable extra consistency checks.
+const doubleCheck = false
+
func assert(p bool) {
- if !p {
- panic("assertion failed")
+ base.Assert(p)
+}
+
+// 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{}) {
+ if infoPrintMode {
+ fmt.Printf(format, a...)
}
}
-// 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.target.Stencils = make(map[*types.Sym]*ir.Func)
+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.
+ if preinliningMainScan {
+ n := len(typecheck.Target.Decls)
+ for i := 0; i < n; i++ {
+ g.scanForGenCalls(typecheck.Target.Decls[i])
+ }
+ }
- default:
- // The other possible ops at the top level are ODCLCONST
- // and ODCLTYPE, which don't have any function
- // instantiations.
- continue
- }
-
- // 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
- foundFuncInst := false
- ir.Visit(decl, func(n ir.Node) {
- if n.Op() == ir.OFUNCINST {
- // We found a function instantiation that is not
- // immediately called.
- foundFuncInst = true
- }
- if n.Op() != ir.OCALL || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST {
- return
- }
+ // 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 new instantiation and transform the various operations that need to make
+ // use of their dictionary.
+ 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 {
+ return
+ }
+ c := n.(*ir.ConvExpr)
+ if c.X.Type().HasShape() && !c.X.Type().IsInterface() {
+ ir.Dump("BAD FUNCTION", info.fun)
+ ir.Dump("BAD CONVERSION", c)
+ base.Fatalf("converting shape type to interface")
+ }
+ })
+ }
+ if base.Flag.W > 1 {
+ ir.Dump(fmt.Sprintf("\ndictpass %v", info.fun), info.fun)
+ }
+ }
+ 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))
+ // New instantiations created during inlining should run
+ // ComputeAddrTaken directly, since we are past the main pass
+ // that did ComputeAddrTaken(). We could instead do this
+ // incrementally during stenciling (for all instantiations,
+ // including main ones before inlining), since we have the
+ // type information.
+ typecheck.ComputeAddrtaken(fun.(*ir.Func).Body)
+ }
+ }
+ 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)
- st := g.getInstantiationForNode(inst)
+ 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.OCALLPART {
+ 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.
- withRecv := make([]ir.Node, len(call.Args)+1)
- dot := inst.X.(*ir.SelectorExpr)
- withRecv[0] = dot.X
- copy(withRecv[1:], call.Args)
- call.Args = withRecv
+ 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 an OFUNCINST without a corresponding call in the
- // above decl, then traverse the nodes of decl again (with
- // EditChildren rather than Visit), where we actually change the
- // OFUNCINST node to an ONAME for the instantiated function.
- // EditChildren is more expensive than Visit, so we only do this
- // in the infrequent case of an OFUNCINSt without a corresponding
- // call.
- if foundFuncInst {
- var edit func(ir.Node) ir.Node
- edit = func(x ir.Node) ir.Node {
- if x.Op() == ir.OFUNCINST {
- st := g.getInstantiationForNode(x.(*ir.InstExpr))
- return st.Nname
+ }
+ })
+
+ // 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)
- return x
}
- edit(decl)
+ 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
}
- if base.Flag.W > 1 && modified {
- ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
+ 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 *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
+ var rcvrValue ir.Node // receiver, if a method value
+ typ := x.Type() // type of the closure
+ var outerInfo *instInfo
+ if outer != nil {
+ outerInfo = g.instInfoMap[outer.Sym()]
+ }
+ usingSubdict := false
+ valueMethod := false
+ if x.Op() == ir.OFUNCINST {
+ inst := x.(*ir.InstExpr)
+
+ // Type arguments we're instantiating with.
+ targs := typecheck.TypesOf(inst.Targs)
+
+ // Find the generic function/method.
+ var gf *ir.Name
+ if inst.X.Op() == ir.ONAME {
+ // Instantiating a generic function call.
+ gf = inst.X.(*ir.Name)
+ } else if inst.X.Op() == ir.OMETHVALUE {
+ // Instantiating a method value x.M.
+ se := inst.X.(*ir.SelectorExpr)
+ rcvrValue = se.X
+ gf = se.Selection.Nname.(*ir.Name)
+ } else {
+ panic("unhandled")
+ }
+
+ // target is the instantiated function we're trying to call.
+ // For functions, the target expects a dictionary as its first argument.
+ // For method values, the target expects a dictionary and the receiver
+ // as its first two arguments.
+ // dictValue is the value to use for the dictionary argument.
+ target = g.getInstantiation(gf, targs, rcvrValue != nil).fun
+ dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
+ if infoPrintMode {
+ dictkind := "Main dictionary"
+ if usingSubdict {
+ dictkind = "Sub-dictionary"
+ }
+ if rcvrValue == nil {
+ fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
+ } else {
+ fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
+ }
+ }
+ } else { // ir.OMETHEXPR or ir.METHVALUE
+ // Method expression T.M where T is a generic type.
+ se := x.(*ir.SelectorExpr)
+ targs := deref(se.X.Type()).RParams()
+ if len(targs) == 0 {
+ panic("bad")
+ }
+ if x.Op() == ir.OMETHVALUE {
+ rcvrValue = se.X
+ }
+
+ // se.X.Type() is the top-level type of the method expression. To
+ // correctly handle method expressions involving embedded fields,
+ // look up the generic method below using the type of the receiver
+ // of se.Selection, since that will be the type that actually has
+ // the method.
+ recv := deref(se.Selection.Type.Recv().Type)
+ if len(recv.RParams()) == 0 {
+ // The embedded type that actually has the method is not
+ // actually generic, so no need to build a closure.
+ return x
+ }
+ baseType := recv.OrigSym().Def.Type()
+ var gf *ir.Name
+ for _, m := range baseType.Methods().Slice() {
+ if se.Sel == m.Sym {
+ gf = m.Nname.(*ir.Name)
+ break
+ }
+ }
+ if !gf.Type().Recv().Type.IsPtr() {
+ // Remember if value method, so we can detect (*T).M case.
+ valueMethod = true
+ }
+ target = g.getInstantiation(gf, targs, true).fun
+ dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
+ if infoPrintMode {
+ dictkind := "Main dictionary"
+ if usingSubdict {
+ dictkind = "Sub-dictionary"
+ }
+ fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
}
- 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()
}
-}
+ // Build a closure to implement a function instantiation.
+ //
+ // func f[T any] (int, int) (int, int) { ...whatever... }
+ //
+ // Then any reference to f[int] not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // func(a0, a1 int) (r0, r1 int) {
+ // return .inst.f[int](.dictN, a0, a1)
+ // }
+ //
+ // Similarly for method expressions,
+ //
+ // type g[T any] ....
+ // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... }
+ //
+ // Any reference to g[int].f not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // func(rcvr g[int], a0, a1 int) (r0, r1 int) {
+ // return .inst.g[int].f(.dictN, rcvr, a0, a1)
+ // }
+ //
+ // Also method values
+ //
+ // var x g[int]
+ //
+ // Any reference to x.f not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // x2 := x
+ // func(a0, a1 int) (r0, r1 int) {
+ // return .inst.g[int].f(.dictN, x2, a0, a1)
+ // }
+
+ // Make a new internal function.
+ fn, formalParams, formalResults := startClosure(pos, outer, typ)
+
+ // This is the dictionary we want to use.
+ // It may be a constant, or it may be a dictionary acquired from the outer function's dictionary.
+ // For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary
+ // read from the outer function's dictionary.
+ var dictVar *ir.Name
+ var dictAssign *ir.AssignStmt
+ if outer != nil {
+ dictVar = ir.NewNameAt(pos, typecheck.LookupNum(typecheck.LocalDictName, g.dnum))
+ g.dnum++
+ dictVar.Class = ir.PAUTO
+ typed(types.Types[types.TUINTPTR], dictVar)
+ dictVar.Curfn = outer
+ dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue)
+ dictAssign.SetTypecheck(1)
+ dictVar.Defn = dictAssign
+ outer.Dcl = append(outer.Dcl, dictVar)
+ }
+ // assign the receiver to a temporary.
+ var rcvrVar *ir.Name
+ var rcvrAssign ir.Node
+ if rcvrValue != nil {
+ rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
+ g.dnum++
+ typed(rcvrValue.Type(), rcvrVar)
+ rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
+ rcvrAssign.SetTypecheck(1)
+ rcvrVar.Defn = rcvrAssign
+ if outer == nil {
+ rcvrVar.Class = ir.PEXTERN
+ typecheck.Target.Decls = append(typecheck.Target.Decls, rcvrAssign)
+ typecheck.Target.Externs = append(typecheck.Target.Externs, rcvrVar)
+ } else {
+ rcvrVar.Class = ir.PAUTO
+ rcvrVar.Curfn = outer
+ outer.Dcl = append(outer.Dcl, rcvrVar)
+ }
+ }
+
+ // Build body of closure. This involves just calling the wrapped function directly
+ // with the additional dictionary argument.
+
+ // First, figure out the dictionary argument.
+ var dict2Var ir.Node
+ if usingSubdict {
+ // Capture sub-dictionary calculated in the outer function
+ dict2Var = ir.CaptureName(pos, fn, dictVar)
+ typed(types.Types[types.TUINTPTR], dict2Var)
+ } else {
+ // Static dictionary, so can be used directly in the closure
+ dict2Var = dictValue
+ }
+ // Also capture the receiver variable.
+ var rcvr2Var *ir.Name
+ if rcvrValue != nil {
+ rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
+ }
+
+ // Build arguments to call inside the closure.
+ var args []ir.Node
-// instantiateMethods instantiates all the methods of all fully-instantiated
-// generic types that have been added to g.instTypeList.
-func (g *irgen) instantiateMethods() {
- for i := 0; i < len(g.instTypeList); i++ {
- typ := g.instTypeList[i]
- // Get the base generic type by looking up the symbol of the
- // generic (uninstantiated) name.
- baseSym := typ.Sym().Pkg.Lookup(genericTypeName(typ.Sym()))
- baseType := baseSym.Def.(*ir.Name).Type()
- for j, m := range typ.Methods().Slice() {
- name := m.Nname.(*ir.Name)
- targs := make([]ir.Node, len(typ.RParams()))
- for k, targ := range typ.RParams() {
- targs[k] = ir.TypeNode(targ)
+ // First the dictionary argument.
+ args = append(args, dict2Var)
+ // Then the receiver.
+ if rcvrValue != nil {
+ args = append(args, rcvr2Var)
+ }
+ // Then all the other arguments (including receiver for method expressions).
+ for i := 0; i < typ.NumParams(); i++ {
+ if x.Op() == ir.OMETHEXPR && i == 0 {
+ // If we are doing a method expression, we need to
+ // explicitly traverse any embedded fields in the receiver
+ // argument in order to call the method instantiation.
+ arg0 := formalParams[0].Nname.(ir.Node)
+ arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(x.Pos(), ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
+ if valueMethod && arg0.Type().IsPtr() {
+ // For handling the (*T).M case: if we have a pointer
+ // receiver after following all the embedded fields,
+ // but it's a value method, add a star operator.
+ arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
}
- baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
- name.Func = g.getInstantiation(baseNname, targs, true)
+ args = append(args, arg0)
+ } else {
+ args = append(args, formalParams[i].Nname.(*ir.Name))
}
}
- g.instTypeList = nil
+ // Build call itself.
+ var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
+ innerCall.(*ir.CallExpr).IsDDD = typ.IsVariadic()
+ if len(formalResults) > 0 {
+ innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
+ }
+ // Finish building body of closure.
+ ir.CurFunc = fn
+ // TODO: set types directly here instead of using typecheck.Stmt
+ typecheck.Stmt(innerCall)
+ ir.CurFunc = nil
+ fn.Body = []ir.Node{innerCall}
+
+ // We're all done with the captured dictionary (and receiver, for method values).
+ ir.FinishCaptureNames(pos, outer, fn)
+
+ // Make a closure referencing our new internal function.
+ c := ir.UseClosure(fn.OClosure, typecheck.Target)
+ var init []ir.Node
+ if outer != nil {
+ init = append(init, dictAssign)
+ }
+ if rcvrValue != nil {
+ init = append(init, rcvrAssign)
+ }
+ return ir.InitExpr(init, c)
}
-// genericSym returns the name of the base generic type for the type named by
-// sym. It simply returns the name obtained by removing everything after the
-// first bracket ("[").
-func genericTypeName(sym *types.Sym) string {
- return sym.Name[0:strings.Index(sym.Name, "[")]
+// 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 *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
+ // compiler puts out the needed DWARF symbols, when this
+ // instantiated type has a different package from the local
+ // package.
+ typecheck.NeedRuntimeType(typ)
+ // Lookup the method on the base generic type, since methods may
+ // not be set on imported instantiated types.
+ baseSym := typ.OrigSym()
+ baseType := baseSym.Def.(*ir.Name).Type()
+ for j, _ := range typ.Methods().Slice() {
+ if baseType.Methods().Slice()[j].Nointerface() {
+ typ.Methods().Slice()[j].SetNointerface(true)
+ }
+ baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
+ // Eagerly generate the instantiations and dictionaries that implement these methods.
+ // We don't use the instantiations here, just generate them (and any
+ // further instantiations those generate, etc.).
+ // Note that we don't set the Func for any methods on instantiated
+ // types. Their signatures don't match so that would be confusing.
+ // Direct method calls go directly to the instantiations, implemented above.
+ // Indirect method calls use wrappers generated in reflectcall. Those wrappers
+ // will use these instantiations if they are needed (for interface tables or reflection).
+ _ = g.getInstantiation(baseNname, typ.RParams(), true)
+ _ = g.getDictionarySym(baseNname, typ.RParams(), true)
+ }
+ }
+ }
}
-// getInstantiationForNode returns the function/method instantiation for a
-// InstExpr node inst.
-func (g *irgen) getInstantiationForNode(inst *ir.InstExpr) *ir.Func {
+// 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 *genInst) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
- return g.getInstantiation(meth.Selection.Nname.(*ir.Name), inst.Targs, true)
+ return meth.Selection.Nname.(*ir.Name), true
} else {
- return g.getInstantiation(inst.X.(*ir.Name), inst.Targs, false)
+ return inst.X.(*ir.Name), false
}
}
-// getInstantiation gets the instantiantion of the function or method nameNode
-// with the type arguments targs. If the instantiated function is not already
-// cached, then it calls genericSubst to create the new instantiation.
-func (g *irgen) getInstantiation(nameNode *ir.Name, targs []ir.Node, isMeth bool) *ir.Func {
- sym := makeInstName(nameNode.Sym(), targs, isMeth)
- st := g.target.Stencils[sym]
- if st == nil {
- // If instantiation doesn't exist yet, create it and add
- // to the list of decls.
- st = g.genericSubst(sym, nameNode, targs, isMeth)
- g.target.Stencils[sym] = st
- g.target.Decls = append(g.target.Decls, st)
- if base.Flag.W > 1 {
- ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
+// getDictOrSubdict returns, for a method/function call or reference (node n) in an
+// instantiation (described by instInfo), a node which is accessing a sub-dictionary
+// 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 *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 {
+ entry := -1
+ for i, de := range declInfo.dictInfo.subDictCalls {
+ if n == de {
+ entry = declInfo.dictInfo.startSubDict + i
+ break
+ }
+ }
+ // If the entry is not found, it may be that this node did not have
+ // any type args that depend on type params, so we need a main
+ // dictionary, not a sub-dictionary.
+ if entry >= 0 {
+ dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictInfo.dictLen)
+ usingSubdict = true
}
}
- return st
+ if !usingSubdict {
+ dict = g.getDictionaryValue(n.Pos(), nameNode, targs, isMeth)
+ }
+ return dict, usingSubdict
}
-// makeInstName makes the unique name for a stenciled generic function or method,
-// based on the name of the function fy=nsym and the targs. It replaces any
-// existing bracket type list in the name. makeInstName asserts that fnsym has
-// brackets in its name if and only if hasBrackets is true.
-// TODO(danscales): remove the assertions and the hasBrackets argument later.
-//
-// Names of declared generic functions have no brackets originally, so hasBrackets
-// should be false. Names of generic methods already have brackets, since the new
-// type parameter is specified in the generic type of the receiver (e.g. func
-// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set.
-//
-// The standard naming is something like: 'genFn[int,bool]' for functions and
-// '(*genType[int,bool]).methodName' for methods
-func makeInstName(fnsym *types.Sym, targs []ir.Node, hasBrackets bool) *types.Sym {
- b := bytes.NewBufferString("")
- name := fnsym.Name
- i := strings.Index(name, "[")
- assert(hasBrackets == (i >= 0))
- if i >= 0 {
- b.WriteString(name[0:i])
+// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded
+// 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
+ // import the whole generic body.
+ assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
+ typecheck.ImportBody(nameNode.Func)
+ assert(nameNode.Func.Inl.Body != nil)
+ nameNode.Func.Body = nameNode.Func.Inl.Body
+ nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
+ }
+}
+
+// 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 *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 {
- b.WriteString(name)
+ checkFetchBody(nameNode)
}
- b.WriteString("[")
- for i, targ := range targs {
- if i > 0 {
- b.WriteString(",")
+
+ // 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
+ // of shape and non-shape arguments, because of inferred or explicitly
+ // specified concrete type args.
+ s1 := make([]*types.Type, len(shapes))
+ for i, t := range shapes {
+ if !t.IsShape() {
+ s1[i] = typecheck.Shapify(t, i)
+ } else {
+ // Already a shape, but make sure it has the correct index.
+ s1[i] = typecheck.Shapify(shapes[i].Underlying(), i)
}
- b.WriteString(targ.Type().String())
}
- b.WriteString("]")
- if i >= 0 {
- i2 := strings.Index(name[i:], "]")
- assert(i2 >= 0)
- b.WriteString(name[i+i2+1:])
+ shapes = s1
+
+ sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, false, isMeth)
+ info := g.instInfoMap[sym]
+ if info == nil {
+ // If instantiation doesn't exist yet, create it and add
+ // to the list of decls.
+ info = &instInfo{
+ dictInfo: &dictInfo{},
+ }
+ info.dictInfo.shapeToBound = make(map[*types.Type]*types.Type)
+
+ if sym.Def != nil {
+ // This instantiation must have been imported from another
+ // package (because it was needed for inlining), so we should
+ // not re-generate it and have conflicting definitions for the
+ // symbol (issue #50121). It will have already gone through the
+ // dictionary transformations of dictPass, so we don't actually
+ // need the info.dictParam and info.shapeToBound info filled in
+ // below. We just set the imported instantiation as info.fun.
+ assert(sym.Pkg != types.LocalPkg)
+ info.fun = sym.Def.(*ir.Name).Func
+ assert(info.fun != nil)
+ g.instInfoMap[sym] = info
+ return info
+ }
+
+ // genericSubst fills in info.dictParam and info.shapeToBound.
+ st := g.genericSubst(sym, nameNode, shapes, isMeth, info)
+ info.fun = st
+ g.instInfoMap[sym] = info
+
+ // getInstInfo fills in info.dictInfo.
+ g.getInstInfo(st, shapes, info)
+ if base.Flag.W > 1 {
+ ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
+ }
+
+ // This ensures that the linker drops duplicates of this instantiation.
+ // All just works!
+ st.SetDupok(true)
+ typecheck.Target.Decls = append(typecheck.Target.Decls, st)
+ g.newInsts = append(g.newInsts, st)
}
- return typecheck.Lookup(b.String())
+ return info
}
// 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
- tparams []*types.Field
- targs []ir.Node
- // The substitution map from name nodes in the generic function to the
- // name nodes in the new stenciled function.
- vars map[*ir.Name]*ir.Name
+ ts typecheck.Tsubster
+ info *instInfo // Place to put extra info in the instantiation
+
+ // Map from non-nil, non-ONAME node n to slice of all m, where m.Defn = n
+ defnMap map[ir.Node][]**ir.Name
}
// genericSubst returns a new function with name newsym. The function is an
// instantiation of a generic function or method specified by namedNode with type
-// args targs. For a method with a generic receiver, it returns an instantiated
-// function type where the receiver becomes the first parameter. Otherwise the
-// instantiated method would still need to be transformed by later compiler
-// phases.
-func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []ir.Node, isMethod bool) *ir.Func {
- var tparams []*types.Field
+// args shapes. For a method with a generic receiver, it returns an instantiated
+// 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.shapeToBound.
+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
// over any pointer)
recvType := nameNode.Type().Recv().Type
recvType = deref(recvType)
- tparams = make([]*types.Field, len(recvType.RParams()))
- for i, rparam := range recvType.RParams() {
- tparams[i] = types.NewField(src.NoXPos, nil, rparam)
- }
+ tparams = recvType.RParams()
} else {
- tparams = nameNode.Type().TParams().Fields().Slice()
+ fields := nameNode.Type().TParams().Fields().Slice()
+ tparams = make([]*types.Type, len(fields))
+ for i, f := range fields {
+ tparams[i] = f.Type
+ }
}
gf := nameNode.Func
// Pos of the instantiated function is same as the generic function
@@ -283,145 +766,277 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []ir.No
// depend on ir.CurFunc being set.
ir.CurFunc = newf
- assert(len(tparams) == len(targs))
+ assert(len(tparams) == len(shapes))
subst := &subster{
g: g,
isMethod: isMethod,
newf: newf,
- tparams: tparams,
- targs: targs,
- vars: make(map[*ir.Name]*ir.Name),
+ info: info,
+ ts: typecheck.Tsubster{
+ Tparams: tparams,
+ Targs: shapes,
+ Vars: make(map[*ir.Name]*ir.Name),
+ },
+ defnMap: make(map[ir.Node][]**ir.Name),
}
- newf.Dcl = make([]*ir.Name, len(gf.Dcl))
- for i, n := range gf.Dcl {
- newf.Dcl[i] = subst.node(n).(*ir.Name)
+ newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
+
+ // Create the needed dictionary param
+ dictionarySym := newsym.Pkg.Lookup(typecheck.LocalDictName)
+ dictionaryType := types.Types[types.TUINTPTR]
+ dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
+ typed(dictionaryType, dictionaryName)
+ dictionaryName.Class = ir.PPARAM
+ dictionaryName.Curfn = newf
+ newf.Dcl = append(newf.Dcl, dictionaryName)
+ for _, n := range gf.Dcl {
+ if n.Sym().Name == typecheck.LocalDictName {
+ panic("already has dictionary")
+ }
+ newf.Dcl = append(newf.Dcl, subst.localvar(n))
}
+ dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
+ dictionaryArg.Nname = dictionaryName
+ info.dictParam = dictionaryName
- // Ugly: we have to insert the Name nodes of the parameters/results into
+ // We add the dictionary as the first parameter in the function signature.
+ // We also transform a method type to the corresponding function type
+ // (make the receiver be the next parameter after the dictionary).
+ oldt := nameNode.Type()
+ var args []*types.Field
+ args = append(args, dictionaryArg)
+ args = append(args, oldt.Recvs().FieldSlice()...)
+ args = append(args, oldt.Params().FieldSlice()...)
+
+ // Replace the types in the function signature via subst.fields.
+ // Ugly: also, we have to insert the Name nodes of the parameters/results into
// the function type. The current function type has no Nname fields set,
// because it came via conversion from the types2 type.
- oldt := nameNode.Type()
- // We also transform a generic method type to the corresponding
- // instantiated function type where the receiver is the first parameter.
newt := types.NewSignature(oldt.Pkg(), nil, nil,
- subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl),
+ subst.fields(ir.PPARAM, args, newf.Dcl),
subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
- newf.Nname.SetType(newt)
+ typed(newt, newf.Nname)
ir.MarkFunc(newf.Nname)
newf.SetTypecheck(1)
- newf.Nname.SetTypecheck(1)
// Make sure name/type of newf is set before substituting the body.
newf.Body = subst.list(gf.Body)
+ if len(newf.Body) == 0 {
+ // Ensure the body is nonempty, for issue 49524.
+ // TODO: have some other way to detect the difference between
+ // a function declared with no body, vs. one with an empty body?
+ newf.Body = append(newf.Body, ir.NewBlockStmt(gf.Pos(), nil))
+ }
+
+ if len(subst.defnMap) > 0 {
+ base.Fatalf("defnMap is not empty")
+ }
+
+ for i, tp := range tparams {
+ info.dictInfo.shapeToBound[shapes[i]] = subst.ts.Typ(tp.Bound())
+ }
+
ir.CurFunc = savef
- return newf
+ return subst.newf
}
-// node is like DeepCopy(), but creates distinct ONAME nodes, and also descends
-// into closures. It substitutes type arguments for type parameters in all the new
-// nodes.
+// localvar creates a new name node for the specified local variable and enters it
+// in subst.vars. It substitutes type arguments for type parameters in the type of
+// name as needed.
+func (subst *subster) localvar(name *ir.Name) *ir.Name {
+ m := ir.NewNameAt(name.Pos(), name.Sym())
+ if name.IsClosureVar() {
+ m.SetIsClosureVar(true)
+ }
+ m.SetType(subst.ts.Typ(name.Type()))
+ m.BuiltinOp = name.BuiltinOp
+ m.Curfn = subst.newf
+ m.Class = name.Class
+ assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
+ m.Func = name.Func
+ subst.ts.Vars[name] = m
+ m.SetTypecheck(1)
+ m.DictIndex = name.DictIndex
+ if name.Defn != nil {
+ if name.Defn.Op() == ir.ONAME {
+ // This is a closure variable, so its Defn is the outer
+ // captured variable, which has already been substituted.
+ m.Defn = subst.node(name.Defn)
+ } else {
+ // The other values of Defn are nodes in the body of the
+ // function, so just remember the mapping so we can set Defn
+ // properly in node() when we create the new body node. We
+ // always call localvar() on all the local variables before
+ // we substitute the body.
+ slice := subst.defnMap[name.Defn]
+ subst.defnMap[name.Defn] = append(slice, &m)
+ }
+ }
+ if name.Outer != nil {
+ m.Outer = subst.node(name.Outer).(*ir.Name)
+ }
+
+ return m
+}
+
+// getDictionaryEntry gets the i'th entry in the dictionary dict.
+func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
+ // Convert dictionary to *[N]uintptr
+ // All entries in the dictionary are pointers. They all point to static data, though, so we
+ // treat them as uintptrs so the GC doesn't need to keep track of them.
+ d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict)
+ d.SetTypecheck(1)
+ d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d)
+ d.SetTypecheck(1)
+ types.CheckSize(d.Type().Elem())
+
+ // Load entry i out of the dictionary.
+ deref := ir.NewStarExpr(pos, d)
+ typed(d.Type().Elem(), deref)
+ idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to?
+ typed(types.Types[types.TUINTPTR], idx)
+ r := ir.NewIndexExpr(pos, deref, idx)
+ typed(types.Types[types.TUINTPTR], r)
+ return r
+}
+
+// getDictionaryType returns a *runtime._type from the dictionary entry i (which
+// refers to a type param or a derived type that uses type params). It uses the
+// specified dictionary dictParam, rather than the one in info.dictParam.
+func getDictionaryType(info *instInfo, dictParam *ir.Name, pos src.XPos, i int) ir.Node {
+ if i < 0 || i >= info.dictInfo.startSubDict {
+ base.Fatalf(fmt.Sprintf("bad dict index %d", i))
+ }
+
+ r := getDictionaryEntry(pos, info.dictParam, i, info.dictInfo.startSubDict)
+ // change type of retrieved dictionary entry to *byte, which is the
+ // standard typing of a *runtime._type in the compiler
+ typed(types.Types[types.TUINT8].PtrTo(), r)
+ return r
+}
+
+// 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 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
edit = func(x ir.Node) ir.Node {
+ // Analogous to ir.SetPos() at beginning of typecheck.typecheck() -
+ // allows using base.Pos during the transform functions, just like
+ // the tc*() functions.
+ ir.SetPos(x)
switch x.Op() {
case ir.OTYPE:
- return ir.TypeNode(subst.typ(x.Type()))
+ return ir.TypeNode(subst.ts.Typ(x.Type()))
case ir.ONAME:
- name := x.(*ir.Name)
- if v := subst.vars[name]; v != nil {
+ if v := subst.ts.Vars[x.(*ir.Name)]; v != nil {
return v
}
- m := ir.NewNameAt(name.Pos(), name.Sym())
- if name.IsClosureVar() {
- m.SetIsClosureVar(true)
+ if ir.IsBlank(x) {
+ // Special case, because a blank local variable is
+ // not in the fn.Dcl list.
+ m := ir.NewNameAt(x.Pos(), ir.BlankNode.Sym())
+ return typed(subst.ts.Typ(x.Type()), m)
}
- t := x.Type()
- if t == nil {
- assert(name.BuiltinOp != 0)
- } else {
- newt := subst.typ(t)
- m.SetType(newt)
- }
- m.BuiltinOp = name.BuiltinOp
- m.Curfn = subst.newf
- m.Class = name.Class
- m.Func = name.Func
- subst.vars[name] = m
- m.SetTypecheck(1)
- return m
+ return x
+ case ir.ONONAME:
+ // This handles the identifier in a type switch guard
+ fallthrough
case ir.OLITERAL, ir.ONIL:
if x.Sym() != nil {
return x
}
}
m := ir.Copy(x)
+
+ slice, ok := subst.defnMap[x]
+ if ok {
+ // We just copied a non-ONAME node which was the Defn value
+ // of a local variable. Set the Defn value of the copied
+ // local variable to this new Defn node.
+ for _, ptr := range slice {
+ (*ptr).Defn = m
+ }
+ delete(subst.defnMap, x)
+ }
+
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)
- if !isCallExpr && !isStructKeyExpr && x.Op() != ir.OPANIC &&
+ _, 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.typ(x.Type()))
+ m.SetType(subst.ts.Typ(x.Type()))
}
}
+
ir.EditChildren(m, edit)
- if x.Typecheck() == 3 {
- // These are nodes whose transforms were delayed until
- // their instantiated type was known.
- m.SetTypecheck(1)
- if typecheck.IsCmp(x.Op()) {
- transformCompare(m.(*ir.BinaryExpr))
- } else {
- switch x.Op() {
- case ir.OSLICE, ir.OSLICE3:
- transformSlice(m.(*ir.SliceExpr))
+ m.SetTypecheck(1)
- case ir.OADD:
- m = transformAdd(m.(*ir.BinaryExpr))
+ // 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.OINDEX:
- transformIndex(m.(*ir.IndexExpr))
+ case ir.OASOP:
+ as := m.(*ir.AssignOpStmt)
+ transformCheckAssign(as, as.X)
- case ir.OAS2:
- as2 := m.(*ir.AssignListStmt)
- transformAssign(as2, as2.Lhs, as2.Rhs)
+ case ir.ORETURN:
+ transformReturn(m.(*ir.ReturnStmt))
- case ir.OAS:
- as := m.(*ir.AssignStmt)
- lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
- transformAssign(as, lhs, rhs)
+ case ir.OSEND:
+ transformSend(m.(*ir.SendStmt))
- case ir.OASOP:
- as := m.(*ir.AssignOpStmt)
- transformCheckAssign(as, as.X)
+ case ir.OSELECT:
+ transformSelect(m.(*ir.SelectStmt))
- case ir.ORETURN:
- transformReturn(m.(*ir.ReturnStmt))
+ case ir.OCOMPLIT:
+ transformCompLit(m.(*ir.CompLitExpr))
- case ir.OSEND:
- transformSend(m.(*ir.SendStmt))
+ case ir.OADDR:
+ transformAddr(m.(*ir.AddrExpr))
- default:
- base.Fatalf("Unexpected node with Typecheck() == 3")
- }
- }
- }
-
- switch x.Op() {
case ir.OLITERAL:
t := m.Type()
if t != x.Type() {
@@ -439,18 +1054,17 @@ 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 OCALLPART 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.
- transformDot(m.(*ir.SelectorExpr), false)
- m.SetTypecheck(1)
+ // 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)
+ }
case ir.OCALL:
call := m.(*ir.CallExpr)
@@ -458,9 +1072,11 @@ func (subst *subster) node(n ir.Node) ir.Node {
case ir.OTYPE:
// Transform the conversion, now that we know the
// type argument.
- m = transformConvCall(m.(*ir.CallExpr))
+ m = transformConvCall(call)
+ // CONVIFACE transformation was already done in noder2
+ assert(m.Op() != ir.OCONVIFACE)
- case ir.OCALLPART:
+ case ir.OMETHVALUE, ir.OMETHEXPR:
// Redo the transformation of OXDOT, now that we
// know the method value is being called. Then
// transform the call.
@@ -479,7 +1095,7 @@ func (subst *subster) node(n ir.Node) ir.Node {
name := call.X.Name()
if name.BuiltinOp != ir.OXXX {
switch name.BuiltinOp {
- case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OLEN, ir.OCAP, ir.OAPPEND:
+ case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
// Transform these builtins now that we
// know the type of the args.
m = transformBuiltin(call)
@@ -493,54 +1109,97 @@ func (subst *subster) node(n ir.Node) ir.Node {
transformCall(call)
}
- case ir.OCLOSURE:
- transformCall(call)
-
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.
+ transformEarlyCall(call)
+
+ case ir.OXDOT:
+ // This is the case of a bound call on a typeparam,
+ // which will be handled in the dictPass.
+ // As with OFUNCINST, we must transform the arguments of the call now,
+ // so any needed CONVIFACE nodes are exposed.
+ transformEarlyCall(call)
+
+ 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:
+ // We're going to create a new closure from scratch, so clear m
+ // to avoid using the ir.Copy by accident until we reassign it.
+ m = nil
+
x := x.(*ir.ClosureExpr)
// Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
// x.Func.Body.
oldfn := x.Func
- newfn := ir.NewFunc(oldfn.Pos())
- if oldfn.ClosureCalled() {
- newfn.SetClosureCalled(true)
- }
- newfn.SetIsHiddenClosure(true)
- m.(*ir.ClosureExpr).Func = newfn
- // Closure name can already have brackets, if it derives
- // from a generic method
- newsym := makeInstName(oldfn.Nname.Sym(), subst.targs, subst.isMethod)
- newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), newsym)
- newfn.Nname.Func = newfn
- newfn.Nname.Defn = newfn
- ir.MarkFunc(newfn.Nname)
- newfn.OClosure = m.(*ir.ClosureExpr)
+ newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil)
+ ir.NameClosure(newfn.OClosure, subst.newf)
saveNewf := subst.newf
ir.CurFunc = newfn
subst.newf = newfn
newfn.Dcl = subst.namelist(oldfn.Dcl)
- newfn.ClosureVars = subst.namelist(oldfn.ClosureVars)
- typed(subst.typ(oldfn.Nname.Type()), newfn.Nname)
- typed(newfn.Nname.Type(), m)
+ // Make a closure variable for the dictionary of the
+ // containing function.
+ cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam)
+ typed(types.Types[types.TUINTPTR], cdict)
+ ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
+ newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)
+
+ // Copy that closure variable to a local one.
+ // Note: this allows the dictionary to be captured by child closures.
+ // See issue 47723.
+ ldict := ir.NewNameAt(x.Pos(), newfn.Sym().Pkg.Lookup(typecheck.LocalDictName))
+ typed(types.Types[types.TUINTPTR], ldict)
+ ldict.Class = ir.PAUTO
+ ldict.Curfn = newfn
+ newfn.Dcl = append(newfn.Dcl, ldict)
+ as := ir.NewAssignStmt(x.Pos(), ldict, cdict)
+ as.SetTypecheck(1)
+ newfn.Body.Append(as)
+
+ // Create inst info for the instantiated closure. The dict
+ // param is the closure variable for the dictionary of the
+ // outer function. Since the dictionary is shared, use the
+ // same dictInfo.
+ cinfo := &instInfo{
+ fun: newfn,
+ dictParam: ldict,
+ dictInfo: subst.info.dictInfo,
+ }
+ subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
+
+ typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
+ typed(newfn.Nname.Type(), newfn.OClosure)
newfn.SetTypecheck(1)
+ outerinfo := subst.info
+ subst.info = cinfo
// Make sure type of closure function is set before doing body.
- newfn.Body = subst.list(oldfn.Body)
+ newfn.Body.Append(subst.list(oldfn.Body)...)
+ subst.info = outerinfo
subst.newf = saveNewf
ir.CurFunc = saveNewf
- subst.g.target.Decls = append(subst.g.target.Decls, newfn)
+ 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()))
+
}
return m
}
@@ -548,16 +1207,252 @@ func (subst *subster) node(n ir.Node) ir.Node {
return edit(n)
}
+// dictPass takes a function instantiation and does the transformations on the
+// operations that need to make use of the dictionary param.
+func (g *genInst) dictPass(info *instInfo) {
+ savef := ir.CurFunc
+ ir.CurFunc = info.fun
+
+ var edit func(ir.Node) ir.Node
+ edit = func(m ir.Node) ir.Node {
+ ir.EditChildren(m, edit)
+
+ switch m.Op() {
+ case ir.OCLOSURE:
+ newf := m.(*ir.ClosureExpr).Func
+ ir.CurFunc = newf
+ outerinfo := info
+ info = g.instInfoMap[newf.Nname.Sym()]
+
+ body := newf.Body
+ for i, n := range body {
+ body[i] = edit(n)
+ }
+
+ info = outerinfo
+ ir.CurFunc = info.fun
+
+ case ir.OXDOT:
+ mse := m.(*ir.SelectorExpr)
+ src := mse.X.Type()
+ assert(src.IsShape())
+
+ // The only dot on a shape type value are methods.
+ if mse.X.Op() == ir.OTYPE {
+ // Method expression T.M
+ m = g.buildClosure2(info, m)
+ // No need for transformDot - buildClosure2 has already
+ // transformed to OCALLINTER/ODOTINTER.
+ } else {
+ // Implement x.M as a conversion-to-bound-interface
+ // 1) convert x to the bound interface
+ // 2) call M on that interface
+ dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
+ if src.IsInterface() {
+ // If type arg is an interface (unusual case),
+ // we do a type assert to the type bound.
+ mse.X = assertToBound(info, info.dictParam, m.Pos(), mse.X, dst)
+ } else {
+ mse.X = convertUsingDictionary(info, info.dictParam, m.Pos(), mse.X, m, dst)
+ }
+ transformDot(mse, false)
+ }
+ case ir.OCALL:
+ call := m.(*ir.CallExpr)
+ op := call.X.Op()
+ if op == ir.OMETHVALUE {
+ // Redo the transformation of OXDOT, now that we
+ // know the method value is being called.
+ call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
+ transformDot(call.X.(*ir.SelectorExpr), true)
+ }
+ transformCall(call)
+
+ case ir.OCONVIFACE:
+ if m.Type().IsEmptyInterface() && m.(*ir.ConvExpr).X.Type().IsEmptyInterface() {
+ // Was T->interface{}, after stenciling it is now interface{}->interface{}.
+ // No longer need the conversion. See issue 48276.
+ m.(*ir.ConvExpr).SetOp(ir.OCONVNOP)
+ break
+ }
+ mce := m.(*ir.ConvExpr)
+ // Note: x's argument is still typed as a type parameter.
+ // m's argument now has an instantiated type.
+ if mce.X.Type().HasShape() || (mce.X.Type().IsInterface() && m.Type().HasShape()) {
+ m = convertUsingDictionary(info, info.dictParam, m.Pos(), m.(*ir.ConvExpr).X, m, m.Type())
+ }
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ if !m.Type().HasShape() {
+ break
+ }
+ dt := m.(*ir.TypeAssertExpr)
+ var rt ir.Node
+ if dt.Type().IsInterface() || dt.X.Type().IsEmptyInterface() {
+ ix := findDictType(info, m.Type())
+ assert(ix >= 0)
+ rt = getDictionaryType(info, info.dictParam, dt.Pos(), ix)
+ } else {
+ // nonempty interface to noninterface. Need an itab.
+ ix := -1
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == m {
+ ix = info.dictInfo.startItabConv + i
+ break
+ }
+ }
+ assert(ix >= 0)
+ rt = getDictionaryEntry(dt.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+ }
+ op := ir.ODYNAMICDOTTYPE
+ if m.Op() == ir.ODOTTYPE2 {
+ op = ir.ODYNAMICDOTTYPE2
+ }
+ m = ir.NewDynamicTypeAssertExpr(dt.Pos(), op, dt.X, rt)
+ m.SetType(dt.Type())
+ m.SetTypecheck(1)
+ case ir.OCASE:
+ if _, ok := m.(*ir.CommClause); ok {
+ // This is not a type switch. TODO: Should we use an OSWITCH case here instead of OCASE?
+ break
+ }
+ m := m.(*ir.CaseClause)
+ for i, c := range m.List {
+ if c.Op() == ir.OTYPE && c.Type().HasShape() {
+ // Use a *runtime._type for the dynamic type.
+ ix := findDictType(info, m.List[i].Type())
+ assert(ix >= 0)
+ dt := ir.NewDynamicType(c.Pos(), getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen))
+
+ // For type switch from nonempty interfaces to non-interfaces, we need an itab as well.
+ if !m.List[i].Type().IsInterface() {
+ if _, ok := info.dictInfo.type2switchType[m.List[i]]; ok {
+ // Type switch from nonempty interface. We need a *runtime.itab
+ // for the dynamic type.
+ ix := -1
+ for j, ic := range info.dictInfo.itabConvs {
+ if ic == m.List[i] {
+ ix = info.dictInfo.startItabConv + j
+ break
+ }
+ }
+ assert(ix >= 0)
+ dt.ITab = getDictionaryEntry(c.Pos(), info.dictParam, ix, info.dictInfo.dictLen)
+ }
+ }
+ typed(m.List[i].Type(), dt)
+ m.List[i] = dt
+ }
+ }
+
+ }
+ return m
+ }
+ edit(info.fun)
+ ir.CurFunc = savef
+}
+
+// findDictType looks for type t in the typeparams or derived types in the generic
+// function info.gfInfo. This will indicate the dictionary entry with the
+// correct concrete type for the associated instantiated function.
+func findDictType(info *instInfo, t *types.Type) int {
+ for i, dt := range info.dictInfo.shapeParams {
+ if dt == t {
+ return i
+ }
+ }
+ for i, dt := range info.dictInfo.derivedTypes {
+ if types.IdenticalStrict(dt, t) {
+ return i + len(info.dictInfo.shapeParams)
+ }
+ }
+ return -1
+}
+
+// convertUsingDictionary converts instantiated value v (type v.Type()) to an interface
+// type dst, by returning a new set of nodes that make use of a dictionary entry. in is the
+// instantiated node of the CONVIFACE node or XDOT node (for a bound method call) that is causing the
+// conversion.
+func convertUsingDictionary(info *instInfo, dictParam *ir.Name, pos src.XPos, v ir.Node, in ir.Node, dst *types.Type) ir.Node {
+ assert(v.Type().HasShape() || v.Type().IsInterface() && in.Type().HasShape())
+ assert(dst.IsInterface())
+
+ if v.Type().IsInterface() {
+ // Converting from an interface. The shape-ness of the source doesn't really matter, as
+ // we'll be using the concrete type from the first interface word.
+ if dst.IsEmptyInterface() {
+ // Converting I2E. OCONVIFACE does that for us, and doesn't depend
+ // on what the empty interface was instantiated with. No dictionary entry needed.
+ v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
+ v.SetTypecheck(1)
+ return v
+ }
+ if !in.Type().HasShape() {
+ // Regular OCONVIFACE works if the destination isn't parameterized.
+ v = ir.NewConvExpr(pos, ir.OCONVIFACE, dst, v)
+ v.SetTypecheck(1)
+ return v
+ }
+
+ // We get the destination interface type from the dictionary and the concrete
+ // type from the argument's itab. Call runtime.convI2I to get the new itab.
+ tmp := typecheck.Temp(v.Type())
+ as := ir.NewAssignStmt(pos, tmp, v)
+ as.SetTypecheck(1)
+ itab := ir.NewUnaryExpr(pos, ir.OITAB, tmp)
+ typed(types.Types[types.TUINTPTR].PtrTo(), itab)
+ idata := ir.NewUnaryExpr(pos, ir.OIDATA, tmp)
+ typed(types.Types[types.TUNSAFEPTR], idata)
+
+ fn := typecheck.LookupRuntime("convI2I")
+ fn.SetTypecheck(1)
+ types.CalcSize(fn.Type())
+ call := ir.NewCallExpr(pos, ir.OCALLFUNC, fn, nil)
+ typed(types.Types[types.TUINT8].PtrTo(), call)
+ ix := findDictType(info, in.Type())
+ assert(ix >= 0)
+ inter := getDictionaryType(info, dictParam, pos, ix)
+ call.Args = []ir.Node{inter, itab}
+ i := ir.NewBinaryExpr(pos, ir.OEFACE, call, idata)
+ typed(dst, i)
+ i.PtrInit().Append(as)
+ return i
+ }
+
+ var rt ir.Node
+ if !dst.IsEmptyInterface() {
+ // We should have an itab entry in the dictionary. Using this itab
+ // will be more efficient than converting to an empty interface first
+ // and then type asserting to dst.
+ ix := -1
+ for i, ic := range info.dictInfo.itabConvs {
+ if ic == in {
+ ix = info.dictInfo.startItabConv + i
+ break
+ }
+ }
+ assert(ix >= 0)
+ rt = getDictionaryEntry(pos, dictParam, ix, info.dictInfo.dictLen)
+ } else {
+ ix := findDictType(info, v.Type())
+ assert(ix >= 0)
+ // Load the actual runtime._type of the type parameter from the dictionary.
+ rt = getDictionaryType(info, dictParam, pos, ix)
+ }
+
+ // Figure out what the data field of the interface will be.
+ data := ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
+ typed(types.Types[types.TUNSAFEPTR], data)
+
+ // Build an interface from the type and data parts.
+ var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
+ typed(dst, i)
+ return i
+}
+
func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
s := make([]*ir.Name, len(l))
for i, n := range l {
- s[i] = subst.node(n).(*ir.Name)
- if n.Defn != nil {
- s[i].Defn = subst.node(n.Defn)
- }
- if n.Outer != nil {
- s[i].Outer = subst.node(n.Outer).(*ir.Name)
- }
+ s[i] = subst.localvar(n)
}
return s
}
@@ -570,348 +1465,719 @@ func (subst *subster) list(l []ir.Node) []ir.Node {
return s
}
-// tstruct substitutes type params in types of the fields of a structure type. For
-// each field, if Nname is set, tstruct also translates the Nname using
-// subst.vars, if Nname is in subst.vars. To always force the creation of a new
-// (top-level) struct, regardless of whether anything changed with the types or
-// names of the struct's fields, set force to true.
-func (subst *subster) tstruct(t *types.Type, force bool) *types.Type {
- if t.NumFields() == 0 {
- if t.HasTParam() {
- // For an empty struct, we need to return a new type,
- // since it may now be fully instantiated (HasTParam
- // becomes false).
- return types.NewStruct(t.Pkg(), nil)
- }
- return t
- }
- var newfields []*types.Field
- if force {
- newfields = make([]*types.Field, t.NumFields())
- }
- for i, f := range t.Fields().Slice() {
- t2 := subst.typ(f.Type)
- if (t2 != f.Type || f.Nname != nil) && newfields == nil {
- newfields = make([]*types.Field, t.NumFields())
- for j := 0; j < i; j++ {
- newfields[j] = t.Field(j)
- }
- }
- 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)
- if f.Nname != nil {
- // f.Nname may not be in subst.vars[] if this is
- // a function name or a function instantiation type
- // that we are translating
- v := subst.vars[f.Nname.(*ir.Name)]
- // Be careful not to put a nil var into Nname,
- // since Nname is an interface, so it would be a
- // non-nil interface.
- if v != nil {
- newfields[i].Nname = v
- }
- }
+// fields sets the Nname field for the Field nodes inside a type signature, based
+// on the corresponding in/out parameters in dcl. It depends on the in and out
+// parameters being in order in dcl.
+func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
+ // Find the starting index in dcl of declarations of the class (either
+ // PPARAM or PPARAMOUT).
+ var i int
+ for i = range dcl {
+ if dcl[i].Class == class {
+ break
}
}
- if newfields != nil {
- return types.NewStruct(t.Pkg(), newfields)
+
+ // Create newfields nodes that are copies of the oldfields nodes, but
+ // with substitution for any type params, and with Nname set to be the node in
+ // Dcl for the corresponding PPARAM or PPARAMOUT.
+ newfields := make([]*types.Field, len(oldfields))
+ for j := range oldfields {
+ newfields[j] = oldfields[j].Copy()
+ newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
+ // A PPARAM field will be missing from dcl if its name is
+ // unspecified or specified as "_". So, we compare the dcl sym
+ // with the field sym (or sym of the field's Nname node). (Unnamed
+ // results still have a name like ~r2 in their Nname node.) If
+ // they don't match, this dcl (if there is one left) must apply to
+ // a later field.
+ if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
+ (oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
+ newfields[j].Nname = dcl[i]
+ i++
+ }
+ }
+ return newfields
+}
+
+// deref does a single deref of type t, if it is a pointer type.
+func deref(t *types.Type) *types.Type {
+ if t.IsPtr() {
+ return t.Elem()
}
return t
+}
+// markTypeUsed marks type t as used in order to help avoid dead-code elimination of
+// needed methods.
+func markTypeUsed(t *types.Type, lsym *obj.LSym) {
+ if t.IsInterface() {
+ 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)
}
-// tinter substitutes type params in types of the methods of an interface type.
-func (subst *subster) tinter(t *types.Type) *types.Type {
- if t.Methods().Len() == 0 {
- return t
+// getDictionarySym returns the dictionary for the named generic function gf, which
+// is instantiated with the type arguments targs.
+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)
+ }
+
+ // Enforce that only concrete types can make it to here.
+ for _, t := range targs {
+ if t.HasShape() {
+ panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
+ }
+ }
+
+ // Get a symbol representing the dictionary.
+ sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth)
+
+ // Initialize the dictionary, if we haven't yet already.
+ lsym := sym.Linksym()
+ if len(lsym.P) > 0 {
+ // We already started creating this dictionary and its lsym.
+ return sym
+ }
+
+ infoPrint("=== Creating dictionary %v\n", sym.Name)
+ off := 0
+ // Emit an entry for each targ (concrete type or gcshape).
+ for _, t := range targs {
+ infoPrint(" * %v\n", t)
+ s := reflectdata.TypeLinksym(t)
+ off = objw.SymPtr(lsym, off, s, 0)
+ markTypeUsed(t, lsym)
+ }
+
+ instInfo := g.getInstantiation(gf, targs, isMeth)
+ info := instInfo.dictInfo
+
+ subst := typecheck.Tsubster{
+ Tparams: info.shapeParams,
+ Targs: targs,
}
- var newfields []*types.Field
- for i, f := range t.Methods().Slice() {
- t2 := subst.typ(f.Type)
- if (t2 != f.Type || f.Nname != nil) && newfields == nil {
- newfields = make([]*types.Field, t.Methods().Len())
- for j := 0; j < i; j++ {
- newfields[j] = t.Methods().Index(j)
+ // Emit an entry for each derived type (after substituting targs)
+ for _, t := range info.derivedTypes {
+ ts := subst.Typ(t)
+ infoPrint(" - %v\n", ts)
+ s := reflectdata.TypeLinksym(ts)
+ off = objw.SymPtr(lsym, off, s, 0)
+ markTypeUsed(ts, lsym)
+ }
+ // Emit an entry for each subdictionary (after substituting targs)
+ for _, n := range info.subDictCalls {
+ var sym *types.Sym
+ switch n.Op() {
+ case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH:
+ call := n.(*ir.CallExpr)
+ if call.X.Op() == ir.OXDOT || call.X.Op() == ir.ODOTMETH {
+ var nameNode *ir.Name
+ se := call.X.(*ir.SelectorExpr)
+ if se.X.Type().IsShape() {
+ // This is a method call enabled by a type bound.
+
+ // We need this extra check for method expressions,
+ // which don't add in the implicit XDOTs.
+ tmpse := ir.NewSelectorExpr(src.NoXPos, ir.OXDOT, se.X, se.Sel)
+ tmpse = typecheck.AddImplicitDots(tmpse)
+ tparam := tmpse.X.Type()
+ if !tparam.IsShape() {
+ // The method expression is not
+ // really on a typeparam.
+ break
+ }
+ ix := -1
+ for i, shape := range info.shapeParams {
+ if shape == tparam {
+ ix = i
+ break
+ }
+ }
+ assert(ix >= 0)
+ recvType := targs[ix]
+ if recvType.IsInterface() || len(recvType.RParams()) == 0 {
+ // No sub-dictionary entry is
+ // actually needed, since the
+ // type arg is not an
+ // instantiated type that
+ // will have generic methods.
+ break
+ }
+ // This is a method call for an
+ // instantiated type, so we need a
+ // sub-dictionary.
+ targs := recvType.RParams()
+ genRecvType := recvType.OrigSym().Def.Type()
+ nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ sym = g.getDictionarySym(nameNode, targs, true)
+ } else {
+ // This is the case of a normal
+ // method call on a generic type.
+ recvType := deref(call.X.(*ir.SelectorExpr).X.Type())
+ genRecvType := recvType.OrigSym().Def.Type()
+ nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ subtargs := recvType.RParams()
+ s2targs := make([]*types.Type, len(subtargs))
+ for i, t := range subtargs {
+ s2targs[i] = subst.Typ(t)
+ }
+ sym = g.getDictionarySym(nameNode, s2targs, true)
+ }
+ } else {
+ inst := call.X.(*ir.InstExpr)
+ var nameNode *ir.Name
+ var meth *ir.SelectorExpr
+ var isMeth bool
+ if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth {
+ nameNode = meth.Selection.Nname.(*ir.Name)
+ } else {
+ nameNode = inst.X.(*ir.Name)
+ }
+ subtargs := typecheck.TypesOf(inst.Targs)
+ for i, t := range subtargs {
+ subtargs[i] = subst.Typ(t)
+ }
+ sym = g.getDictionarySym(nameNode, subtargs, isMeth)
+ }
+
+ case ir.OFUNCINST:
+ inst := n.(*ir.InstExpr)
+ nameNode := inst.X.(*ir.Name)
+ subtargs := typecheck.TypesOf(inst.Targs)
+ for i, t := range subtargs {
+ subtargs[i] = subst.Typ(t)
+ }
+ sym = g.getDictionarySym(nameNode, subtargs, false)
+
+ case ir.OXDOT, ir.OMETHEXPR, ir.OMETHVALUE:
+ selExpr := n.(*ir.SelectorExpr)
+ recvType := deref(selExpr.Selection.Type.Recv().Type)
+ genRecvType := recvType.OrigSym().Def.Type()
+ subtargs := recvType.RParams()
+ s2targs := make([]*types.Type, len(subtargs))
+ for i, t := range subtargs {
+ s2targs[i] = subst.Typ(t)
}
+ nameNode := typecheck.Lookdot1(selExpr, selExpr.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
+ sym = g.getDictionarySym(nameNode, s2targs, true)
+
+ default:
+ assert(false)
}
- if newfields != nil {
- newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+
+ if sym == nil {
+ // Unused sub-dictionary entry, just emit 0.
+ off = objw.Uintptr(lsym, off, 0)
+ infoPrint(" - Unused subdict entry\n")
+ } else {
+ off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
+ infoPrint(" - Subdict %v\n", sym.Name)
}
}
- if newfields != nil {
- return types.NewInterface(t.Pkg(), newfields)
+
+ g.instantiateMethods()
+ delay := &delayInfo{
+ gf: gf,
+ targs: targs,
+ sym: sym,
+ off: off,
+ isMeth: isMeth,
}
- return t
+ g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
+ return sym
}
-// instTypeName creates a name for an instantiated type, based on the name of the
-// generic type and the type args
-func instTypeName(name string, targs []*types.Type) string {
- b := bytes.NewBufferString(name)
- b.WriteByte('[')
- for i, targ := range targs {
- if i > 0 {
- b.WriteByte(',')
- }
- b.WriteString(targ.String())
- }
- b.WriteByte(']')
- return b.String()
-}
-
-// typ computes the type obtained by substituting any type parameter in t with the
-// corresponding type argument in subst. If t contains no type parameters, the
-// result is t; otherwise the result is a new type. It deals with recursive types
-// by using TFORW types and finding partially or fully created types via sym.Def.
-func (subst *subster) typ(t *types.Type) *types.Type {
- if !t.HasTParam() && t.Kind() != types.TFUNC {
- // Note: function types need to be copied regardless, as the
- // types of closures may contain declarations that need
- // to be copied. See #45738.
- return t
- }
-
- if t.Kind() == types.TTYPEPARAM {
- for i, tp := range subst.tparams {
- if tp.Type == t {
- return subst.targs[i].Type()
- }
- }
- // If t is a simple typeparam T, then t has the name/symbol 'T'
- // and t.Underlying() == t.
- //
- // However, consider the type definition: 'type P[T any] T'. We
- // might use this definition so we can have a variant of type T
- // that we can add new methods to. Suppose t is a reference to
- // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM,
- // because P[T] is defined as T. If we look at t.Underlying(), it
- // is different, because the name of t.Underlying() is 'T' rather
- // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM.
- // In this case, we do the needed recursive substitution in the
- // case statement below.
- if t.Underlying() == t {
- // t is a simple typeparam that didn't match anything in tparam
- return t
- }
- // t is a more complex typeparam (e.g. P[T], as above, whose
- // definition is just T).
- assert(t.Sym() != nil)
- }
-
- var newsym *types.Sym
- var neededTargs []*types.Type
- var forw *types.Type
-
- if t.Sym() != nil {
- // Translate the type params for this type according to
- // the tparam/targs mapping from subst.
- neededTargs = make([]*types.Type, len(t.RParams()))
- for i, rparam := range t.RParams() {
- neededTargs[i] = subst.typ(rparam)
- }
- // For a named (defined) type, we have to change the name of the
- // type as well. We do this first, so we can look up if we've
- // already seen this type during this substitution or other
- // definitions/substitutions.
- genName := genericTypeName(t.Sym())
- newsym = t.Sym().Pkg.Lookup(instTypeName(genName, neededTargs))
- if newsym.Def != nil {
- // We've already created this instantiated defined type.
- return newsym.Def.Type()
- }
-
- // In order to deal with recursive generic types, create a TFORW
- // type initially and set the Def field of its sym, so it can be
- // found if this type appears recursively within the type.
- forw = newIncompleteNamedType(t.Pos(), newsym)
- //println("Creating new type by sub", newsym.Name, forw.HasTParam())
- forw.SetRParams(neededTargs)
- }
-
- var newt *types.Type
+// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
+// any needed LSyms for itabs. The itab lsyms create wrappers which need various
+// 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 *genInst) finalizeSyms() {
+ for _, d := range g.dictSymsToFinalize {
+ infoPrint("=== Finalizing dictionary %s\n", d.sym.Name)
+
+ lsym := d.sym.Linksym()
+ instInfo := g.getInstantiation(d.gf, d.targs, d.isMeth)
+ info := instInfo.dictInfo
+
+ subst := typecheck.Tsubster{
+ Tparams: info.shapeParams,
+ Targs: d.targs,
+ }
- switch t.Kind() {
- case types.TTYPEPARAM:
- if t.Sym() == newsym {
- // The substitution did not change the type.
- return t
+ // Emit an entry for each itab
+ for _, n := range info.itabConvs {
+ var srctype, dsttype *types.Type
+ switch n.Op() {
+ case ir.OXDOT, ir.OMETHVALUE:
+ se := n.(*ir.SelectorExpr)
+ srctype = subst.Typ(se.X.Type())
+ dsttype = subst.Typ(info.shapeToBound[se.X.Type()])
+ case ir.ODOTTYPE, ir.ODOTTYPE2:
+ srctype = subst.Typ(n.(*ir.TypeAssertExpr).Type())
+ dsttype = subst.Typ(n.(*ir.TypeAssertExpr).X.Type())
+ case ir.OCONVIFACE:
+ srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
+ dsttype = subst.Typ(n.Type())
+ case ir.OTYPE:
+ srctype = subst.Typ(n.Type())
+ dsttype = subst.Typ(info.type2switchType[n])
+ default:
+ base.Fatalf("itab entry with unknown op %s", n.Op())
+ }
+ if srctype.IsInterface() || dsttype.IsEmptyInterface() {
+ // No itab is wanted if src type is an interface. We
+ // will use a type assert instead.
+ d.off = objw.Uintptr(lsym, d.off, 0)
+ infoPrint(" + Unused itab entry for %v\n", srctype)
+ } else {
+ // Make sure all new fully-instantiated types have
+ // their methods created before generating any itabs.
+ g.instantiateMethods()
+ itabLsym := reflectdata.ITabLsym(srctype, dsttype)
+ d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
+ infoPrint(" + Itab for (%v,%v)\n", srctype, dsttype)
+ }
}
- // Substitute the underlying typeparam (e.g. T in P[T], see
- // the example describing type P[T] above).
- newt = subst.typ(t.Underlying())
- assert(newt != t)
- case types.TARRAY:
- elem := t.Elem()
- newelem := subst.typ(elem)
- if newelem != elem {
- newt = types.NewArray(newelem, t.NumElem())
+ objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
+ infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
+ }
+ g.dictSymsToFinalize = nil
+}
+
+func (g *genInst) getDictionaryValue(pos src.XPos, 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.
+ var n *ir.Name
+ if sym.Def != nil {
+ n = sym.Def.(*ir.Name)
+ } else {
+ // We set the position of a static dictionary to be the position of
+ // one of its uses.
+ n = ir.NewNameAt(pos, sym)
+ n.Curfn = ir.CurFunc
+ n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
+ n.SetTypecheck(1)
+ n.Class = ir.PEXTERN
+ sym.Def = n
+ }
+
+ // Return the address of the dictionary. Addr node gets position that was passed in.
+ np := typecheck.NodAddrAt(pos, n)
+ // Note: treat dictionary pointers as uintptrs, so they aren't pointers
+ // with respect to GC. That saves on stack scanning work, write barriers, etc.
+ // We can get away with it because dictionaries are global variables.
+ // TODO: use a cast, or is typing directly ok?
+ np.SetType(types.Types[types.TUINTPTR])
+ np.SetTypecheck(1)
+ return np
+}
+
+// hasShapeNodes returns true if the type of any node in targs has a shape.
+func hasShapeNodes(targs []ir.Node) bool {
+ for _, n := range targs {
+ if n.Type().HasShape() {
+ return true
}
+ }
+ return false
+}
- case types.TPTR:
- elem := t.Elem()
- newelem := subst.typ(elem)
- if newelem != elem {
- newt = types.NewPtr(newelem)
+// hasShapeTypes returns true if any type in targs has a shape.
+func hasShapeTypes(targs []*types.Type) bool {
+ for _, t := range targs {
+ if t.HasShape() {
+ return true
}
+ }
+ return false
+}
- case types.TSLICE:
- elem := t.Elem()
- newelem := subst.typ(elem)
- if newelem != elem {
- newt = types.NewSlice(newelem)
+// getInstInfo get the dictionary format for a function instantiation- type params, derived
+// types, and needed subdictionaries and itabs.
+func (g *genInst) getInstInfo(st *ir.Func, shapes []*types.Type, instInfo *instInfo) {
+ info := instInfo.dictInfo
+ info.shapeParams = shapes
+
+ for _, t := range info.shapeParams {
+ b := info.shapeToBound[t]
+ if b.HasShape() {
+ // If a type bound is parameterized (unusual case), then we
+ // may need its derived type to do a type assert when doing a
+ // bound call for a type arg that is an interface.
+ addType(info, nil, b)
}
+ }
- case types.TSTRUCT:
- newt = subst.tstruct(t, false)
- if newt == t {
- newt = nil
+ for _, n := range st.Dcl {
+ addType(info, n, n.Type())
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+ }
+
+ if infoPrintMode {
+ fmt.Printf(">>> InstInfo for %v\n", st)
+ for _, t := range info.shapeParams {
+ fmt.Printf(" Typeparam %v\n", t)
}
+ }
- case types.TFUNC:
- newrecvs := subst.tstruct(t.Recvs(), false)
- newparams := subst.tstruct(t.Params(), false)
- newresults := subst.tstruct(t.Results(), false)
- if newrecvs != t.Recvs() || newparams != t.Params() || newresults != t.Results() {
- // If any types have changed, then the all the fields of
- // of recv, params, and results must be copied, because they have
- // offset fields that are dependent, and so must have an
- // independent copy for each new signature.
- var newrecv *types.Field
- if newrecvs.NumFields() > 0 {
- if newrecvs == t.Recvs() {
- newrecvs = subst.tstruct(t.Recvs(), true)
+ // 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) {
+ 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)
+ }
+ 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 {
+ infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
}
- newrecv = newrecvs.Field(0)
+ info.subDictCalls = append(info.subDictCalls, n)
}
- if newparams == t.Params() {
- newparams = subst.tstruct(t.Params(), true)
+ 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 newresults == t.Results() {
- newresults = subst.tstruct(t.Results(), true)
+ 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)
+ }
+ 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)
+ }
+ }
+ 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).
+ cfunc := n.(*ir.ClosureExpr).Func
+ for _, n1 := range cfunc.Body {
+ ir.Visit(n1, visitFunc)
+ }
+ for _, n := range cfunc.Dcl {
+ n.DictIndex = uint16(findDictType(instInfo, n.Type()) + 1)
+ }
+ 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()
+ }
+ }
+ }
}
- newt = types.NewSignature(t.Pkg(), newrecv, t.TParams().FieldSlice(), newparams.FieldSlice(), newresults.FieldSlice())
}
+ addType(info, n, n.Type())
+ }
- case types.TINTER:
- newt = subst.tinter(t)
- if newt == t {
- newt = nil
+ for _, stmt := range st.Body {
+ ir.Visit(stmt, visitFunc)
+ }
+ if infoPrintMode {
+ for _, t := range info.derivedTypes {
+ fmt.Printf(" Derived type %v\n", t)
}
+ fmt.Printf(">>> Done Instinfo\n")
+ }
+ info.startSubDict = len(info.shapeParams) + len(info.derivedTypes)
+ info.startItabConv = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls)
+ info.dictLen = len(info.shapeParams) + len(info.derivedTypes) + len(info.subDictCalls) + len(info.itabConvs)
+}
- case types.TMAP:
- newkey := subst.typ(t.Key())
- newval := subst.typ(t.Elem())
- if newkey != t.Key() || newval != t.Elem() {
- newt = types.NewMap(newkey, newval)
- }
-
- case types.TCHAN:
- elem := t.Elem()
- newelem := subst.typ(elem)
- if newelem != elem {
- newt = types.NewChan(newelem, t.ChanDir())
- if !newt.HasTParam() {
- // TODO(danscales): not sure why I have to do this
- // only for channels.....
- types.CheckSize(newt)
- }
- }
- }
- if newt == nil {
- // Even though there were typeparams in the type, there may be no
- // change if this is a function type for a function call (which will
- // have its own tparams/targs in the function instantiation).
- return t
- }
-
- if t.Sym() == nil {
- // Not a named type, so there was no forwarding type and there are
- // no methods to substitute.
- assert(t.Methods().Len() == 0)
- return newt
- }
-
- forw.SetUnderlying(newt)
- newt = forw
-
- if t.Kind() != types.TINTER && t.Methods().Len() > 0 {
- // Fill in the method info for the new type.
- var newfields []*types.Field
- newfields = make([]*types.Field, t.Methods().Len())
- for i, f := range t.Methods().Slice() {
- t2 := subst.typ(f.Type)
- oldsym := f.Nname.Sym()
- newsym := makeInstName(oldsym, subst.targs, true)
- var nname *ir.Name
- if newsym.Def != nil {
- nname = newsym.Def.(*ir.Name)
- } else {
- nname = ir.NewNameAt(f.Pos, newsym)
- nname.SetType(t2)
- newsym.Def = nname
- }
- newfields[i] = types.NewField(f.Pos, f.Sym, t2)
- newfields[i].Nname = nname
- }
- newt.Methods().Set(newfields)
- if !newt.HasTParam() {
- // Generate all the methods for a new fully-instantiated type.
- subst.g.instTypeList = append(subst.g.instTypeList, newt)
+// isShapeDeref returns true if t is either a shape or a pointer to a shape. (We
+// can't just use deref(t).IsShape(), since a shape type is a complex type and may
+// have a pointer as part of its shape.)
+func isShapeDeref(t *types.Type) bool {
+ return t.IsShape() || t.IsPtr() && t.Elem().IsShape()
+}
+
+// addType adds t to info.derivedTypes if it is parameterized type (which is not
+// just a simple shape) that is different from any existing type on
+// info.derivedTypes.
+func addType(info *dictInfo, n ir.Node, t *types.Type) {
+ if t == nil || !t.HasShape() {
+ return
+ }
+ if t.IsShape() {
+ return
+ }
+ if t.Kind() == types.TFUNC && n != nil &&
+ (t.Recv() != nil || n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
+ // Don't use the type of a named generic function or method,
+ // since that is parameterized by other typeparams.
+ // (They all come from arguments of a FUNCINST node.)
+ return
+ }
+ if doubleCheck && !parameterizedBy(t, info.shapeParams) {
+ base.Fatalf("adding type with invalid parameters %+v", t)
+ }
+ if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
+ // Multiple return values are not a relevant new type (?).
+ return
+ }
+ // Ignore a derived type we've already added.
+ for _, et := range info.derivedTypes {
+ if types.IdenticalStrict(t, et) {
+ return
}
}
- return newt
+ info.derivedTypes = append(info.derivedTypes, t)
}
-// fields sets the Nname field for the Field nodes inside a type signature, based
-// on the corresponding in/out parameters in dcl. It depends on the in and out
-// parameters being in order in dcl.
-func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
- // Find the starting index in dcl of declarations of the class (either
- // PPARAM or PPARAMOUT).
- var i int
- for i = range dcl {
- if dcl[i].Class == class {
- break
+// parameterizedBy returns true if t is parameterized by (at most) params.
+func parameterizedBy(t *types.Type, params []*types.Type) bool {
+ return parameterizedBy1(t, params, map[*types.Type]bool{})
+}
+func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
+ if visited[t] {
+ return true
+ }
+ visited[t] = true
+
+ if t.Sym() != nil && len(t.RParams()) > 0 {
+ // This defined type is instantiated. Check the instantiating types.
+ for _, r := range t.RParams() {
+ if !parameterizedBy1(r, params, visited) {
+ return false
+ }
}
+ return true
}
+ if t.IsShape() {
+ // Check if t is one of the allowed parameters in scope.
+ for _, p := range params {
+ if p == t {
+ return true
+ }
+ }
+ // Couldn't find t in the list of allowed parameters.
+ return false
- // Create newfields nodes that are copies of the oldfields nodes, but
- // with substitution for any type params, and with Nname set to be the node in
- // Dcl for the corresponding PPARAM or PPARAMOUT.
- newfields := make([]*types.Field, len(oldfields))
- for j := range oldfields {
- newfields[j] = oldfields[j].Copy()
- newfields[j].Type = subst.typ(oldfields[j].Type)
- // A param field will be missing from dcl if its name is
- // unspecified or specified as "_". So, we compare the dcl sym
- // with the field sym. If they don't match, this dcl (if there is
- // one left) must apply to a later field.
- if i < len(dcl) && dcl[i].Sym() == oldfields[j].Sym {
- newfields[j].Nname = dcl[i]
- i++
+ }
+ switch t.Kind() {
+ case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
+ return parameterizedBy1(t.Elem(), params, visited)
+
+ case types.TMAP:
+ return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)
+
+ case types.TFUNC:
+ return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)
+
+ case types.TSTRUCT:
+ for _, f := range t.Fields().Slice() {
+ if !parameterizedBy1(f.Type, params, visited) {
+ return false
+ }
+ }
+ return true
+
+ case types.TINTER:
+ for _, f := range t.Methods().Slice() {
+ if !parameterizedBy1(f.Type, params, visited) {
+ return false
+ }
+ }
+ return true
+
+ case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
+ types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
+ types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
+ return true
+
+ case types.TUNION:
+ for i := 0; i < t.NumTerms(); i++ {
+ tt, _ := t.Term(i)
+ if !parameterizedBy1(tt, params, visited) {
+ return false
+ }
}
+ return true
+
+ default:
+ base.Fatalf("bad type kind %+v", t)
+ return true
}
- return newfields
}
-// defer does a single defer of type t, if it is a pointer type.
-func deref(t *types.Type) *types.Type {
- if t.IsPtr() {
- return t.Elem()
+// startClosures starts creation of a closure that has the function type typ. It
+// creates all the formal params and results according to the type typ. On return,
+// the body and closure variables of the closure must still be filled in, and
+// ir.UseClosure() called.
+func startClosure(pos src.XPos, outer *ir.Func, typ *types.Type) (*ir.Func, []*types.Field, []*types.Field) {
+ // Make a new internal function.
+ fn := ir.NewClosureFunc(pos, outer != nil)
+ ir.NameClosure(fn.OClosure, outer)
+
+ // Build formal argument and return lists.
+ var formalParams []*types.Field // arguments of closure
+ var formalResults []*types.Field // returns of closure
+ for i := 0; i < typ.NumParams(); i++ {
+ t := typ.Params().Field(i).Type
+ arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
+ arg.Class = ir.PPARAM
+ typed(t, arg)
+ arg.Curfn = fn
+ fn.Dcl = append(fn.Dcl, arg)
+ f := types.NewField(pos, arg.Sym(), t)
+ f.Nname = arg
+ f.SetIsDDD(typ.Params().Field(i).IsDDD())
+ formalParams = append(formalParams, f)
}
- return t
+ for i := 0; i < typ.NumResults(); i++ {
+ t := typ.Results().Field(i).Type
+ result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed?
+ result.Class = ir.PPARAMOUT
+ typed(t, result)
+ result.Curfn = fn
+ fn.Dcl = append(fn.Dcl, result)
+ f := types.NewField(pos, result.Sym(), t)
+ f.Nname = result
+ formalResults = append(formalResults, f)
+ }
+
+ // Build an internal function with the right signature.
+ closureType := types.NewSignature(typ.Pkg(), nil, nil, formalParams, formalResults)
+ typed(closureType, fn.Nname)
+ typed(typ, fn.OClosure)
+ fn.SetTypecheck(1)
+ return fn, formalParams, formalResults
+
+}
+
+// assertToBound returns a new node that converts a node rcvr with interface type to
+// the 'dst' interface type.
+func assertToBound(info *instInfo, dictVar *ir.Name, pos src.XPos, rcvr ir.Node, dst *types.Type) ir.Node {
+ if dst.HasShape() {
+ ix := findDictType(info, dst)
+ assert(ix >= 0)
+ rt := getDictionaryType(info, dictVar, pos, ix)
+ rcvr = ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, rcvr, rt)
+ typed(dst, rcvr)
+ } else {
+ rcvr = ir.NewTypeAssertExpr(pos, rcvr, nil)
+ typed(dst, rcvr)
+ }
+ return rcvr
}
-// newIncompleteNamedType returns a TFORW type t with name specified by sym, such
-// that t.nod and sym.Def are set correctly.
-func newIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type {
- name := ir.NewDeclNameAt(pos, ir.OTYPE, sym)
- forw := types.NewNamed(name)
- name.SetType(forw)
- sym.Def = name
- return forw
+// buildClosure2 makes a closure to implement a method expression m (generic form x)
+// which has a shape type as receiver. If the receiver is exactly a shape (i.e. from
+// a typeparam), then the body of the closure converts m.X (the receiver) to the
+// interface bound type, and makes an interface call with the remaining arguments.
+//
+// The returned closure is fully substituted and has already had any needed
+// transformations done.
+func (g *genInst) buildClosure2(info *instInfo, m ir.Node) ir.Node {
+ outer := info.fun
+ pos := m.Pos()
+ typ := m.Type() // type of the closure
+
+ fn, formalParams, formalResults := startClosure(pos, outer, typ)
+
+ // Capture dictionary calculated in the outer function
+ dictVar := ir.CaptureName(pos, fn, info.dictParam)
+ typed(types.Types[types.TUINTPTR], dictVar)
+
+ // Build arguments to call inside the closure.
+ var args []ir.Node
+ for i := 0; i < typ.NumParams(); i++ {
+ args = append(args, formalParams[i].Nname.(*ir.Name))
+ }
+
+ // Build call itself. This involves converting the first argument to the
+ // bound type (an interface) using the dictionary, and then making an
+ // interface call with the remaining arguments.
+ var innerCall ir.Node
+ rcvr := args[0]
+ args = args[1:]
+ assert(m.(*ir.SelectorExpr).X.Type().IsShape())
+ dst := info.dictInfo.shapeToBound[m.(*ir.SelectorExpr).X.Type()]
+ if m.(*ir.SelectorExpr).X.Type().IsInterface() {
+ // If type arg is an interface (unusual case), we do a type assert to
+ // the type bound.
+ rcvr = assertToBound(info, dictVar, pos, rcvr, dst)
+ } else {
+ rcvr = convertUsingDictionary(info, dictVar, pos, rcvr, m, dst)
+ }
+ dot := ir.NewSelectorExpr(pos, ir.ODOTINTER, rcvr, m.(*ir.SelectorExpr).Sel)
+ dot.Selection = typecheck.Lookdot1(dot, dot.Sel, dot.X.Type(), dot.X.Type().AllMethods(), 1)
+
+ typed(dot.Selection.Type, dot)
+ innerCall = ir.NewCallExpr(pos, ir.OCALLINTER, dot, args)
+ t := m.Type()
+ if t.NumResults() == 0 {
+ innerCall.SetTypecheck(1)
+ } else if t.NumResults() == 1 {
+ typed(t.Results().Field(0).Type, innerCall)
+ } else {
+ typed(t.Results(), innerCall)
+ }
+ if len(formalResults) > 0 {
+ innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
+ innerCall.SetTypecheck(1)
+ }
+ fn.Body = []ir.Node{innerCall}
+
+ // We're all done with the captured dictionary
+ ir.FinishCaptureNames(pos, outer, fn)
+
+ // Do final checks on closure and return it.
+ return ir.UseClosure(fn.OClosure, typecheck.Target)
}
diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go
index 32a1483b4aabeb2fddeb646fc897ccf216cc52cb..1e996b95c48ffa506d61573f7bde134cc80be639 100644
--- a/src/cmd/compile/internal/noder/stmt.go
+++ b/src/cmd/compile/internal/noder/stmt.go
@@ -5,6 +5,7 @@
package noder
import (
+ "cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
@@ -27,6 +28,7 @@ func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
}
func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
+ base.Assert(g.exprStmtOK)
switch stmt := stmt.(type) {
case nil, *syntax.EmptyStmt:
return nil
@@ -35,24 +37,24 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
case *syntax.BlockStmt:
return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
case *syntax.ExprStmt:
- x := g.expr(stmt.X)
- if call, ok := x.(*ir.CallExpr); ok {
- call.Use = ir.CallUseStmt
- }
- return x
+ return wrapname(g.pos(stmt.X), g.expr(stmt.X))
case *syntax.SendStmt:
n := ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
- if n.Chan.Type().HasTParam() || n.Value.Type().HasTParam() {
- // Delay transforming the send if the channel or value
- // have a type param.
- n.SetTypecheck(3)
- return n
+ if !g.delayTransform() {
+ transformSend(n)
}
- transformSend(n)
n.SetTypecheck(1)
return n
case *syntax.DeclStmt:
- return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
+ if g.topFuncIsGeneric && len(stmt.DeclList) > 0 {
+ if _, ok := stmt.DeclList[0].(*syntax.TypeDecl); ok {
+ // TODO: remove this restriction. See issue 47631.
+ base.ErrorfAt(g.pos(stmt), "type declarations inside generic functions are not currently supported")
+ }
+ }
+ n := ir.NewBlockStmt(g.pos(stmt), nil)
+ g.decls(&n.List, stmt.DeclList)
+ return n
case *syntax.AssignStmt:
if stmt.Op != 0 && stmt.Op != syntax.Def {
@@ -61,60 +63,40 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
if stmt.Rhs == nil {
n = IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
} else {
- n = ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))
+ // Eval rhs before lhs, for compatibility with noder1
+ rhs := g.expr(stmt.Rhs)
+ lhs := g.expr(stmt.Lhs)
+ n = ir.NewAssignOpStmt(g.pos(stmt), op, lhs, rhs)
}
- if n.X.Typecheck() == 3 {
- n.SetTypecheck(3)
- return n
+ if !g.delayTransform() {
+ transformAsOp(n)
}
- transformAsOp(n)
n.SetTypecheck(1)
return n
}
- names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
+ // Eval rhs before lhs, for compatibility with noder1
rhs := g.exprList(stmt.Rhs)
-
- // We must delay transforming the assign statement if any of the
- // lhs or rhs nodes are also delayed, since transformAssign needs
- // to know the types of the left and right sides in various cases.
- delay := false
- for _, e := range lhs {
- if e.Typecheck() == 3 {
- delay = true
- break
- }
- }
- for _, e := range rhs {
- if e.Typecheck() == 3 {
- delay = true
- break
- }
- }
+ names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
if len(lhs) == 1 && len(rhs) == 1 {
n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
n.Def = initDefn(n, names)
- if delay {
- n.SetTypecheck(3)
- return n
+ if !g.delayTransform() {
+ lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
+ transformAssign(n, lhs, rhs)
+ n.X, n.Y = lhs[0], rhs[0]
}
-
- lhs, rhs := []ir.Node{n.X}, []ir.Node{n.Y}
- transformAssign(n, lhs, rhs)
- n.X, n.Y = lhs[0], rhs[0]
n.SetTypecheck(1)
return n
}
n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
n.Def = initDefn(n, names)
- if delay {
- n.SetTypecheck(3)
- return n
+ if !g.delayTransform() {
+ transformAssign(n, n.Lhs, n.Rhs)
}
- transformAssign(n, n.Lhs, n.Rhs)
n.SetTypecheck(1)
return n
@@ -124,15 +106,9 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
case *syntax.ReturnStmt:
n := ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
- for _, e := range n.Results {
- if e.Type().HasTParam() {
- // Delay transforming the return statement if any of the
- // return values have a type param.
- n.SetTypecheck(3)
- return n
- }
+ if !g.delayTransform() {
+ transformReturn(n)
}
- transformReturn(n)
n.SetTypecheck(1)
return n
case *syntax.IfStmt:
@@ -141,7 +117,10 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
return g.forStmt(stmt)
case *syntax.SelectStmt:
n := g.selectStmt(stmt)
- transformSelect(n.(*ir.SelectStmt))
+
+ if !g.delayTransform() {
+ transformSelect(n.(*ir.SelectStmt))
+ }
n.SetTypecheck(1)
return n
case *syntax.SwitchStmt:
@@ -266,6 +245,12 @@ func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node {
key, value := unpackTwo(lhs)
n := ir.NewRangeStmt(g.pos(r), key, value, g.expr(r.X), g.blockStmt(stmt.Body))
n.Def = initDefn(n, names)
+ if key != nil {
+ transformCheckAssign(n, key)
+ }
+ if value != nil {
+ transformCheckAssign(n, value)
+ }
return n
}
@@ -311,6 +296,8 @@ func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
if obj, ok := g.info.Implicits[clause]; ok {
cv = g.obj(obj)
cv.SetPos(g.makeXPos(clause.Colon))
+ assert(expr.Op() == ir.OTYPESW)
+ cv.Defn = expr
}
body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body))
body[i].Var = cv
diff --git a/src/cmd/compile/internal/noder/sync.go b/src/cmd/compile/internal/noder/sync.go
new file mode 100644
index 0000000000000000000000000000000000000000..7af558f8b207aee4f645b554a9e33981f8aaaad3
--- /dev/null
+++ b/src/cmd/compile/internal/noder/sync.go
@@ -0,0 +1,187 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "fmt"
+ "strings"
+)
+
+// enableSync controls whether sync markers are written into unified
+// IR's export data format and also whether they're expected when
+// reading them back in. They're inessential to the correct
+// functioning of unified IR, but are helpful during development to
+// detect mistakes.
+//
+// When sync is enabled, writer stack frames will also be included in
+// the export data. Currently, a fixed number of frames are included,
+// controlled by -d=syncframes (default 0).
+const enableSync = true
+
+// fmtFrames formats a backtrace for reporting reader/writer desyncs.
+func fmtFrames(pcs ...uintptr) []string {
+ res := make([]string, 0, len(pcs))
+ walkFrames(pcs, func(file string, line int, name string, offset uintptr) {
+ // Trim package from function name. It's just redundant noise.
+ name = strings.TrimPrefix(name, "cmd/compile/internal/noder.")
+
+ res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset))
+ })
+ return res
+}
+
+type frameVisitor func(file string, line int, name string, offset uintptr)
+
+// syncMarker is an enum type that represents markers that may be
+// written to export data to ensure the reader and writer stay
+// synchronized.
+type syncMarker int
+
+//go:generate stringer -type=syncMarker -trimprefix=sync
+
+// TODO(mdempsky): Cleanup unneeded sync markers.
+
+// TODO(mdempsky): Split these markers into public/stable markers, and
+// private ones. Also, trim unused ones.
+const (
+ _ syncMarker = iota
+ syncNode
+ syncBool
+ syncInt64
+ syncUint64
+ syncString
+ syncPos
+ syncPkg
+ syncSym
+ syncSelector
+ syncKind
+ syncType
+ syncTypePkg
+ syncSignature
+ syncParam
+ syncOp
+ syncObject
+ syncExpr
+ syncStmt
+ syncDecl
+ syncConstDecl
+ syncFuncDecl
+ syncTypeDecl
+ syncVarDecl
+ syncPragma
+ syncValue
+ syncEOF
+ syncMethod
+ syncFuncBody
+ syncUse
+ syncUseObj
+ syncObjectIdx
+ syncTypeIdx
+ syncBOF
+ syncEntry
+ syncOpenScope
+ syncCloseScope
+ syncGlobal
+ syncLocal
+ syncDefine
+ syncDefLocal
+ syncUseLocal
+ syncDefGlobal
+ syncUseGlobal
+ syncTypeParams
+ syncUseLabel
+ syncDefLabel
+ syncFuncLit
+ syncCommonFunc
+ syncBodyRef
+ syncLinksymExt
+ syncHack
+ syncSetlineno
+ syncName
+ syncImportDecl
+ syncDeclNames
+ syncDeclName
+ syncExprList
+ syncExprs
+ syncWrapname
+ syncTypeExpr
+ syncTypeExprOrNil
+ syncChanDir
+ syncParams
+ syncCloseAnotherScope
+ syncSum
+ syncUnOp
+ syncBinOp
+ syncStructType
+ syncInterfaceType
+ syncPackname
+ syncEmbedded
+ syncStmts
+ syncStmtsFall
+ syncStmtFall
+ syncBlockStmt
+ syncIfStmt
+ syncForStmt
+ syncSwitchStmt
+ syncRangeStmt
+ syncCaseClause
+ syncCommClause
+ syncSelectStmt
+ syncDecls
+ syncLabeledStmt
+ syncCompLit
+
+ sync1
+ sync2
+ sync3
+ sync4
+
+ syncN
+ syncDefImplicit
+ syncUseName
+ syncUseObjLocal
+ syncAddLocal
+ syncBothSignature
+ syncSetUnderlying
+ syncLinkname
+ syncStmt1
+ syncStmtsEnd
+ syncDeclare
+ syncTopDecls
+ syncTopConstDecl
+ syncTopFuncDecl
+ syncTopTypeDecl
+ syncTopVarDecl
+ syncObject1
+ syncAddBody
+ syncLabel
+ syncFuncExt
+ syncMethExt
+ syncOptLabel
+ syncScalar
+ syncStmtDecls
+ syncDeclLocal
+ syncObjLocal
+ syncObjLocal1
+ syncDeclareLocal
+ syncPublic
+ syncPrivate
+ syncRelocs
+ syncReloc
+ syncUseReloc
+ syncVarExt
+ syncPkgDef
+ syncTypeExt
+ syncVal
+ syncCodeObj
+ syncPosBase
+ syncLocalIdent
+ syncTypeParamNames
+ syncTypeParamBounds
+ syncImplicitTypes
+ syncObjectName
+)
diff --git a/src/cmd/compile/internal/noder/syncmarker_string.go b/src/cmd/compile/internal/noder/syncmarker_string.go
new file mode 100644
index 0000000000000000000000000000000000000000..655cafc9502d25332bdb9c69b50a77147b701c26
--- /dev/null
+++ b/src/cmd/compile/internal/noder/syncmarker_string.go
@@ -0,0 +1,156 @@
+// Code generated by "stringer -type=syncMarker -trimprefix=sync"; DO NOT EDIT.
+
+package noder
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[syncNode-1]
+ _ = x[syncBool-2]
+ _ = x[syncInt64-3]
+ _ = x[syncUint64-4]
+ _ = x[syncString-5]
+ _ = x[syncPos-6]
+ _ = x[syncPkg-7]
+ _ = x[syncSym-8]
+ _ = x[syncSelector-9]
+ _ = x[syncKind-10]
+ _ = x[syncType-11]
+ _ = x[syncTypePkg-12]
+ _ = x[syncSignature-13]
+ _ = x[syncParam-14]
+ _ = x[syncOp-15]
+ _ = x[syncObject-16]
+ _ = x[syncExpr-17]
+ _ = x[syncStmt-18]
+ _ = x[syncDecl-19]
+ _ = x[syncConstDecl-20]
+ _ = x[syncFuncDecl-21]
+ _ = x[syncTypeDecl-22]
+ _ = x[syncVarDecl-23]
+ _ = x[syncPragma-24]
+ _ = x[syncValue-25]
+ _ = x[syncEOF-26]
+ _ = x[syncMethod-27]
+ _ = x[syncFuncBody-28]
+ _ = x[syncUse-29]
+ _ = x[syncUseObj-30]
+ _ = x[syncObjectIdx-31]
+ _ = x[syncTypeIdx-32]
+ _ = x[syncBOF-33]
+ _ = x[syncEntry-34]
+ _ = x[syncOpenScope-35]
+ _ = x[syncCloseScope-36]
+ _ = x[syncGlobal-37]
+ _ = x[syncLocal-38]
+ _ = x[syncDefine-39]
+ _ = x[syncDefLocal-40]
+ _ = x[syncUseLocal-41]
+ _ = x[syncDefGlobal-42]
+ _ = x[syncUseGlobal-43]
+ _ = x[syncTypeParams-44]
+ _ = x[syncUseLabel-45]
+ _ = x[syncDefLabel-46]
+ _ = x[syncFuncLit-47]
+ _ = x[syncCommonFunc-48]
+ _ = x[syncBodyRef-49]
+ _ = x[syncLinksymExt-50]
+ _ = x[syncHack-51]
+ _ = x[syncSetlineno-52]
+ _ = x[syncName-53]
+ _ = x[syncImportDecl-54]
+ _ = x[syncDeclNames-55]
+ _ = x[syncDeclName-56]
+ _ = x[syncExprList-57]
+ _ = x[syncExprs-58]
+ _ = x[syncWrapname-59]
+ _ = x[syncTypeExpr-60]
+ _ = x[syncTypeExprOrNil-61]
+ _ = x[syncChanDir-62]
+ _ = x[syncParams-63]
+ _ = x[syncCloseAnotherScope-64]
+ _ = x[syncSum-65]
+ _ = x[syncUnOp-66]
+ _ = x[syncBinOp-67]
+ _ = x[syncStructType-68]
+ _ = x[syncInterfaceType-69]
+ _ = x[syncPackname-70]
+ _ = x[syncEmbedded-71]
+ _ = x[syncStmts-72]
+ _ = x[syncStmtsFall-73]
+ _ = x[syncStmtFall-74]
+ _ = x[syncBlockStmt-75]
+ _ = x[syncIfStmt-76]
+ _ = x[syncForStmt-77]
+ _ = x[syncSwitchStmt-78]
+ _ = x[syncRangeStmt-79]
+ _ = x[syncCaseClause-80]
+ _ = x[syncCommClause-81]
+ _ = x[syncSelectStmt-82]
+ _ = x[syncDecls-83]
+ _ = x[syncLabeledStmt-84]
+ _ = x[syncCompLit-85]
+ _ = x[sync1-86]
+ _ = x[sync2-87]
+ _ = x[sync3-88]
+ _ = x[sync4-89]
+ _ = x[syncN-90]
+ _ = x[syncDefImplicit-91]
+ _ = x[syncUseName-92]
+ _ = x[syncUseObjLocal-93]
+ _ = x[syncAddLocal-94]
+ _ = x[syncBothSignature-95]
+ _ = x[syncSetUnderlying-96]
+ _ = x[syncLinkname-97]
+ _ = x[syncStmt1-98]
+ _ = x[syncStmtsEnd-99]
+ _ = x[syncDeclare-100]
+ _ = x[syncTopDecls-101]
+ _ = x[syncTopConstDecl-102]
+ _ = x[syncTopFuncDecl-103]
+ _ = x[syncTopTypeDecl-104]
+ _ = x[syncTopVarDecl-105]
+ _ = x[syncObject1-106]
+ _ = x[syncAddBody-107]
+ _ = x[syncLabel-108]
+ _ = x[syncFuncExt-109]
+ _ = x[syncMethExt-110]
+ _ = x[syncOptLabel-111]
+ _ = x[syncScalar-112]
+ _ = x[syncStmtDecls-113]
+ _ = x[syncDeclLocal-114]
+ _ = x[syncObjLocal-115]
+ _ = x[syncObjLocal1-116]
+ _ = x[syncDeclareLocal-117]
+ _ = x[syncPublic-118]
+ _ = x[syncPrivate-119]
+ _ = x[syncRelocs-120]
+ _ = x[syncReloc-121]
+ _ = x[syncUseReloc-122]
+ _ = x[syncVarExt-123]
+ _ = x[syncPkgDef-124]
+ _ = x[syncTypeExt-125]
+ _ = x[syncVal-126]
+ _ = x[syncCodeObj-127]
+ _ = x[syncPosBase-128]
+ _ = x[syncLocalIdent-129]
+ _ = x[syncTypeParamNames-130]
+ _ = x[syncTypeParamBounds-131]
+ _ = x[syncImplicitTypes-132]
+ _ = x[syncObjectName-133]
+}
+
+const _syncMarker_name = "NodeBoolInt64Uint64StringPosPkgSymSelectorKindTypeTypePkgSignatureParamOpObjectExprStmtDeclConstDeclFuncDeclTypeDeclVarDeclPragmaValueEOFMethodFuncBodyUseUseObjObjectIdxTypeIdxBOFEntryOpenScopeCloseScopeGlobalLocalDefineDefLocalUseLocalDefGlobalUseGlobalTypeParamsUseLabelDefLabelFuncLitCommonFuncBodyRefLinksymExtHackSetlinenoNameImportDeclDeclNamesDeclNameExprListExprsWrapnameTypeExprTypeExprOrNilChanDirParamsCloseAnotherScopeSumUnOpBinOpStructTypeInterfaceTypePacknameEmbeddedStmtsStmtsFallStmtFallBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtCompLit1234NDefImplicitUseNameUseObjLocalAddLocalBothSignatureSetUnderlyingLinknameStmt1StmtsEndDeclareTopDeclsTopConstDeclTopFuncDeclTopTypeDeclTopVarDeclObject1AddBodyLabelFuncExtMethExtOptLabelScalarStmtDeclsDeclLocalObjLocalObjLocal1DeclareLocalPublicPrivateRelocsRelocUseRelocVarExtPkgDefTypeExtValCodeObjPosBaseLocalIdentTypeParamNamesTypeParamBoundsImplicitTypesObjectName"
+
+var _syncMarker_index = [...]uint16{0, 4, 8, 13, 19, 25, 28, 31, 34, 42, 46, 50, 57, 66, 71, 73, 79, 83, 87, 91, 100, 108, 116, 123, 129, 134, 137, 143, 151, 154, 160, 169, 176, 179, 184, 193, 203, 209, 214, 220, 228, 236, 245, 254, 264, 272, 280, 287, 297, 304, 314, 318, 327, 331, 341, 350, 358, 366, 371, 379, 387, 400, 407, 413, 430, 433, 437, 442, 452, 465, 473, 481, 486, 495, 503, 512, 518, 525, 535, 544, 554, 564, 574, 579, 590, 597, 598, 599, 600, 601, 602, 613, 620, 631, 639, 652, 665, 673, 678, 686, 693, 701, 713, 724, 735, 745, 752, 759, 764, 771, 778, 786, 792, 801, 810, 818, 827, 839, 845, 852, 858, 863, 871, 877, 883, 890, 893, 900, 907, 917, 931, 946, 959, 969}
+
+func (i syncMarker) String() string {
+ i -= 1
+ if i < 0 || i >= syncMarker(len(_syncMarker_index)-1) {
+ return "syncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")"
+ }
+ return _syncMarker_name[_syncMarker_index[i]:_syncMarker_index[i+1]]
+}
diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go
index 2859089e69b91386010db02268764a66aa2de7aa..a673484821b0c2161f2e5eb7e2a13136c5b8c12d 100644
--- a/src/cmd/compile/internal/noder/transform.go
+++ b/src/cmd/compile/internal/noder/transform.go
@@ -85,7 +85,15 @@ func stringtoruneslit(n *ir.ConvExpr) ir.Node {
// etc. Corresponds to typecheck.tcConv.
func transformConv(n *ir.ConvExpr) ir.Node {
t := n.X.Type()
- op, _ := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
+ op, why := typecheck.Convertop(n.X.Op() == ir.OLITERAL, t, n.Type())
+ if op == ir.OXXX {
+ // types2 currently ignores pragmas, so a 'notinheap' mismatch is the
+ // one type-related error that it does not catch. This error will be
+ // caught here by Convertop (see two checks near beginning of
+ // Convertop) and reported at the end of noding.
+ base.ErrorfAt(n.Pos(), "cannot convert %L to type %v%s", n.X, n.Type(), why)
+ return n
+ }
n.SetOp(op)
switch n.Op() {
case ir.OCONVNOP:
@@ -122,8 +130,12 @@ 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.
+// (non-conversion, non-builtin part) of typecheck.tcCall. This code should work even
+// in the case of OCALL/OFUNCINST.
func transformCall(n *ir.CallExpr) {
+ // Set base.Pos, since transformArgs below may need it, but transformCall
+ // is called in some passes that don't set base.Pos.
+ ir.SetPos(n)
// n.Type() can be nil for calls with no return value
assert(n.Typecheck() == 1)
transformArgs(n)
@@ -149,9 +161,10 @@ func transformCall(n *ir.CallExpr) {
}
typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args)
+ if l.Op() == ir.ODOTMETH && len(deref(n.X.Type().Recv().Type).RParams()) == 0 {
+ typecheck.FixMethodCall(n)
+ }
if t.NumResults() == 1 {
- n.SetType(l.Type().Results().Field(0).Type)
-
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME {
if sym := n.X.(*ir.Name).Sym(); types.IsRuntimePkg(sym.Pkg) && sym.Name == "getg" {
// Emit code for runtime.getg() directly instead of calling function.
@@ -167,6 +180,12 @@ func transformCall(n *ir.CallExpr) {
}
}
+// transformEarlyCall transforms the arguments of a call with an OFUNCINST node.
+func transformEarlyCall(n *ir.CallExpr) {
+ transformArgs(n)
+ typecheckaste(ir.OCALL, n.X, n.IsDDD, n.X.Type().Params(), n.Args)
+}
+
// transformCompare transforms a compare operation (currently just equals/not
// equals). Corresponds to the "comparison operators" case in
// typecheck.typecheck1, including tcArith.
@@ -185,7 +204,7 @@ func transformCompare(n *ir.BinaryExpr) {
aop, _ := typecheck.Assignop(lt, rt)
if aop != ir.OXXX {
types.CalcSize(lt)
- if rt.IsInterface() == lt.IsInterface() || lt.Width >= 1<<16 {
+ if lt.HasShape() || rt.IsInterface() == lt.IsInterface() || lt.Size() >= 1<<16 {
l = ir.NewConvExpr(base.Pos, aop, rt, l)
l.SetTypecheck(1)
}
@@ -198,7 +217,7 @@ func transformCompare(n *ir.BinaryExpr) {
aop, _ := typecheck.Assignop(rt, lt)
if aop != ir.OXXX {
types.CalcSize(rt)
- if rt.IsInterface() == lt.IsInterface() || rt.Width >= 1<<16 {
+ if rt.HasTParam() || rt.IsInterface() == lt.IsInterface() || rt.Size() >= 1<<16 {
r = ir.NewConvExpr(base.Pos, aop, lt, r)
r.SetTypecheck(1)
}
@@ -303,6 +322,10 @@ assignOK:
r := r.(*ir.TypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODOTTYPE2)
+ case ir.ODYNAMICDOTTYPE:
+ r := r.(*ir.DynamicTypeAssertExpr)
+ stmt.SetOp(ir.OAS2DOTTYPE)
+ r.SetOp(ir.ODYNAMICDOTTYPE2)
default:
break assignOK
}
@@ -323,11 +346,22 @@ assignOK:
stmt := stmt.(*ir.AssignListStmt)
stmt.SetOp(ir.OAS2FUNC)
r := rhs[0].(*ir.CallExpr)
- r.Use = ir.CallUseList
rtyp := r.Type()
+ mismatched := false
+ failed := false
for i := range lhs {
- checkLHS(i, rtyp.Field(i).Type)
+ result := rtyp.Field(i).Type
+ checkLHS(i, result)
+
+ if lhs[i].Type() == nil || result == nil {
+ failed = true
+ } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) {
+ mismatched = true
+ }
+ }
+ if mismatched && !failed {
+ typecheck.RewriteMultiValueCall(stmt, r)
}
return
}
@@ -340,12 +374,12 @@ assignOK:
}
}
-// Corresponds to typecheck.typecheckargs.
+// Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls.
func transformArgs(n ir.InitNode) {
var list []ir.Node
switch n := n.(type) {
default:
- base.Fatalf("typecheckargs %+v", n.Op())
+ base.Fatalf("transformArgs %+v", n.Op())
case *ir.CallExpr:
list = n.Args
if n.IsDDD {
@@ -363,46 +397,13 @@ func transformArgs(n ir.InitNode) {
return
}
- // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
-
// Save n as n.Orig for fmt.go.
if ir.Orig(n) == n {
n.(ir.OrigNode).SetOrig(ir.SepCopy(n))
}
- as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
- as.Rhs.Append(list...)
-
- // If we're outside of function context, then this call will
- // be executed during the generated init function. However,
- // init.go hasn't yet created it. Instead, associate the
- // temporary variables with InitTodoFunc for now, and init.go
- // will reassociate them later when it's appropriate.
- static := ir.CurFunc == nil
- if static {
- ir.CurFunc = typecheck.InitTodoFunc
- }
- list = nil
- for _, f := range t.FieldSlice() {
- t := typecheck.Temp(f.Type)
- as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, t))
- as.Lhs.Append(t)
- list = append(list, t)
- }
- if static {
- ir.CurFunc = nil
- }
-
- switch n := n.(type) {
- case *ir.CallExpr:
- n.Args = list
- case *ir.ReturnStmt:
- n.Results = list
- }
-
- transformAssign(as, as.Lhs, as.Rhs)
- as.SetTypecheck(1)
- n.PtrInit().Append(as)
+ // Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
+ typecheck.RewriteMultiValueCall(n, list[0])
}
// assignconvfn converts node n for assignment to type t. Corresponds to
@@ -412,11 +413,18 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node {
return n
}
- if types.Identical(n.Type(), t) {
+ if n.Op() == ir.OPAREN {
+ n = n.(*ir.ParenExpr).X
+ }
+
+ if types.IdenticalStrict(n.Type(), t) {
return n
}
- op, _ := typecheck.Assignop(n.Type(), t)
+ op, why := Assignop(n.Type(), t)
+ if op == ir.OXXX {
+ base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why)
+ }
r := ir.NewConvExpr(base.Pos, op, t, n)
r.SetTypecheck(1)
@@ -424,7 +432,34 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node {
return r
}
-// Corresponds to typecheck.typecheckaste.
+func Assignop(src, dst *types.Type) (ir.Op, string) {
+ if src == dst {
+ return ir.OCONVNOP, ""
+ }
+ if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
+ return ir.OXXX, ""
+ }
+
+ // 1. src type is identical to dst (taking shapes into account)
+ if types.Identical(src, dst) {
+ // We already know from assignconvfn above that IdenticalStrict(src,
+ // dst) is false, so the types are not exactly the same and one of
+ // src or dst is a shape. If dst is an interface (which means src is
+ // an interface too), we need a real OCONVIFACE op; otherwise we need a
+ // OCONVNOP. See issue #48453.
+ if dst.IsInterface() {
+ return ir.OCONVIFACE, ""
+ } else {
+ return ir.OCONVNOP, ""
+ }
+ }
+ return typecheck.Assignop1(src, dst)
+}
+
+// Corresponds to typecheck.typecheckaste, but we add an extra flag convifaceOnly
+// only. If convifaceOnly is true, we only do interface conversion. We use this to do
+// early insertion of CONVIFACE nodes during noder2, when the function or args may
+// have typeparams.
func typecheckaste(op ir.Op, call ir.Node, isddd bool, tstruct *types.Type, nl ir.Nodes) {
var t *types.Type
var i int
@@ -495,10 +530,16 @@ func transformSelect(sel *ir.SelectStmt) {
if ncase.Comm != nil {
n := ncase.Comm
oselrecv2 := func(dst, recv ir.Node, def bool) {
- n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
- n.Def = def
- n.SetTypecheck(1)
- ncase.Comm = n
+ selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+ if dst.Op() == ir.ONAME && dst.(*ir.Name).Defn == n {
+ // Must fix Defn for dst, since we are
+ // completely changing the node.
+ dst.(*ir.Name).Defn = selrecv
+ }
+ selrecv.Def = def
+ selrecv.SetTypecheck(1)
+ selrecv.SetInit(n.Init())
+ ncase.Comm = selrecv
}
switch n.Op() {
case ir.OAS:
@@ -537,13 +578,31 @@ func transformAsOp(n *ir.AssignOpStmt) {
}
// transformDot transforms an OXDOT (or ODOT) or ODOT, ODOTPTR, ODOTMETH,
-// ODOTINTER, or OCALLPART, as appropriate. It adds in extra nodes as needed to
+// ODOTINTER, or OMETHVALUE, as appropriate. It adds in extra nodes as needed to
// access embedded fields. Corresponds to typecheck.tcDot.
func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node {
assert(n.Type() != nil && n.Typecheck() == 1)
if n.Op() == ir.OXDOT {
n = typecheck.AddImplicitDots(n)
n.SetOp(ir.ODOT)
+
+ // Set the Selection field and typecheck flag for any new ODOT nodes
+ // added by AddImplicitDots(), and also transform to ODOTPTR if
+ // needed. Equivalent to 'n.X = typecheck(n.X, ctxExpr|ctxType)' in
+ // tcDot.
+ for n1 := n; n1.X.Op() == ir.ODOT; {
+ n1 = n1.X.(*ir.SelectorExpr)
+ if !n1.Implicit() {
+ break
+ }
+ t1 := n1.X.Type()
+ if t1.IsPtr() && !t1.Elem().IsInterface() {
+ t1 = t1.Elem()
+ n1.SetOp(ir.ODOTPTR)
+ }
+ typecheck.Lookdot(n1, t1, 0)
+ n1.SetTypecheck(1)
+ }
}
t := n.X.Type()
@@ -561,8 +620,9 @@ func transformDot(n *ir.SelectorExpr, isCall bool) ir.Node {
assert(f != nil)
if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && !isCall {
- n.SetOp(ir.OCALLPART)
- n.SetType(typecheck.MethodValueWrapper(n).Type())
+ n.SetOp(ir.OMETHVALUE)
+ // This converts a method type to a function type. See issue 47775.
+ n.SetType(typecheck.NewMethodType(n.Type(), nil))
}
return n
}
@@ -594,7 +654,11 @@ func transformMethodExpr(n *ir.SelectorExpr) (res ir.Node) {
s := n.Sel
m := typecheck.Lookdot1(n, s, t, ms, 0)
- assert(m != nil)
+ if !t.HasShape() {
+ // It's OK to not find the method if t is instantiated by shape types,
+ // because we will use the methods on the generic type anyway.
+ assert(m != nil)
+ }
n.SetOp(ir.OMETHEXPR)
n.Selection = m
@@ -790,7 +854,10 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
return transformRealImag(u1.(*ir.UnaryExpr))
case ir.OPANIC:
return transformPanic(u1.(*ir.UnaryExpr))
- case ir.OCLOSE, ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
+ // This corresponds to the EvalConst() call near end of typecheck().
+ return typecheck.EvalConst(u1)
+ case ir.OCLOSE, ir.ONEW:
// nothing more to do
return u1
}
@@ -856,7 +923,7 @@ func transformArrayLit(elemType *types.Type, bound int64, elts []ir.Node) int64
// transformCompLit transforms n to an OARRAYLIT, OSLICELIT, OMAPLIT, or
// OSTRUCTLIT node, with any needed conversions. Corresponds to
-// typecheck.tcCompLit.
+// typecheck.tcCompLit (and includes parts corresponding to tcStructLitKey).
func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
assert(n.Type() != nil && n.Typecheck() == 1)
lno := base.Pos
@@ -911,9 +978,7 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
f := t.Field(i)
n1 = assignconvfn(n1, f.Type)
- sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1)
- sk.Offset = f.Offset
- ls[i] = sk
+ ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1)
}
assert(len(ls) >= t.NumFields())
} else {
@@ -922,33 +987,34 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
for i, l := range ls {
ir.SetPos(l)
- if l.Op() == ir.OKEY {
- kv := l.(*ir.KeyExpr)
- key := kv.Key
-
- // Sym might have resolved to name in other top-level
- // package, because of import dot. Redirect to correct sym
- // before we do the lookup.
- s := key.Sym()
- if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil {
- s = typecheck.Lookup(s.Name)
- }
-
- // An OXDOT uses the Sym field to hold
- // the field to the right of the dot,
- // so s will be non-nil, but an OXDOT
- // is never a valid struct literal key.
- assert(!(s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank()))
+ kv := l.(*ir.KeyExpr)
+ key := kv.Key
- l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value)
- ls[i] = l
+ // Sym might have resolved to name in other top-level
+ // package, because of import dot. Redirect to correct sym
+ // before we do the lookup.
+ s := key.Sym()
+ if id, ok := key.(*ir.Ident); ok && typecheck.DotImportRefs[id] != nil {
+ s = typecheck.Lookup(s.Name)
+ }
+ if types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
+ // Exported field names should always have
+ // local pkg. We only need to do this
+ // adjustment for generic functions that are
+ // being transformed after being imported
+ // from another package.
+ s = typecheck.Lookup(s.Name)
}
- assert(l.Op() == ir.OSTRUCTKEY)
- l := l.(*ir.StructKeyExpr)
+ // An OXDOT uses the Sym field to hold
+ // the field to the right of the dot,
+ // so s will be non-nil, but an OXDOT
+ // is never a valid struct literal key.
+ assert(!(s == nil || key.Op() == ir.OXDOT || s.IsBlank()))
- f := typecheck.Lookdot1(nil, l.Field, t, t.Fields(), 0)
- l.Offset = f.Offset
+ f := typecheck.Lookdot1(nil, s, t, t.Fields(), 0)
+ l := ir.NewStructKeyExpr(l.Pos(), f, kv.Value)
+ ls[i] = l
l.Value = assignconvfn(l.Value, f.Type)
}
@@ -959,3 +1025,11 @@ func transformCompLit(n *ir.CompLitExpr) (res ir.Node) {
return n
}
+
+// transformAddr corresponds to typecheck.tcAddr.
+func transformAddr(n *ir.AddrExpr) {
+ switch n.X.Op() {
+ case ir.OARRAYLIT, ir.OMAPLIT, ir.OSLICELIT, ir.OSTRUCTLIT:
+ n.SetOp(ir.OPTRLIT)
+ }
+}
diff --git a/src/cmd/compile/internal/noder/types.go b/src/cmd/compile/internal/noder/types.go
index 8680559a412970277e2513fe6d1620a55d4393cc..4f6d8287201d4b685c3a8822b57e6b6f6b3f7ab8 100644
--- a/src/cmd/compile/internal/noder/types.go
+++ b/src/cmd/compile/internal/noder/types.go
@@ -5,7 +5,6 @@
package noder
import (
- "bytes"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
@@ -22,23 +21,32 @@ func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
case g.self:
return types.LocalPkg
case types2.Unsafe:
- return ir.Pkgs.Unsafe
+ return types.UnsafePkg
}
return types.NewPkg(pkg.Path(), pkg.Name())
}
+var universeAny = types2.Universe.Lookup("any").Type()
+
// typ converts a types2.Type to a types.Type, including caching of previously
// translated types.
func (g *irgen) typ(typ types2.Type) *types.Type {
+ // Defer the CheckSize calls until we have fully-defined a
+ // (possibly-recursive) top-level type.
+ types.DeferCheckSize()
res := g.typ1(typ)
-
- // Calculate the size for all concrete types seen by the frontend. The old
- // typechecker calls CheckSize() a lot, and we want to eliminate calling
- // it eventually, so we should do it here instead. We only call it for
- // top-level types (i.e. we do it here rather in typ1), to make sure that
- // recursive types have been fully constructed before we call CheckSize.
- if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() {
- types.CheckSize(res)
+ types.ResumeCheckSize()
+
+ // Finish up any types on typesToFinalize, now that we are at the top of a
+ // fully-defined (possibly recursive) type. fillinMethods could create more
+ // types to finalize.
+ for len(g.typesToFinalize) > 0 {
+ l := len(g.typesToFinalize)
+ info := g.typesToFinalize[l-1]
+ g.typesToFinalize = g.typesToFinalize[:l-1]
+ types.DeferCheckSize()
+ g.fillinMethods(info.typ, info.ntyp)
+ types.ResumeCheckSize()
}
return res
}
@@ -47,6 +55,12 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
// constructed part of a recursive type. Should not be called from outside this
// file (g.typ is the "external" entry point).
func (g *irgen) typ1(typ types2.Type) *types.Type {
+ // See issue 49583: the type checker has trouble keeping track of aliases,
+ // but for such a common alias as any we can improve things by preserving a
+ // pointer identity that can be checked when formatting type strings.
+ if typ == universeAny {
+ return types.AnyType
+ }
// Cache type2-to-type mappings. Important so that each defined generic
// type (instantiated or not) has a single types.Type representation.
// Also saves a lot of computation and memory by avoiding re-translating
@@ -54,6 +68,12 @@ func (g *irgen) typ1(typ types2.Type) *types.Type {
res, ok := g.typs[typ]
if !ok {
res = g.typ0(typ)
+ // Calculate the size for all concrete types seen by the frontend.
+ // This is the replacement for the CheckSize() calls in the types1
+ // typechecker. These will be deferred until the top-level g.typ().
+ if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() {
+ types.CheckSize(res)
+ }
g.typs[typ] = res
}
return res
@@ -61,25 +81,12 @@ func (g *irgen) typ1(typ types2.Type) *types.Type {
// instTypeName2 creates a name for an instantiated type, base on the type args
// (given as types2 types).
-func instTypeName2(name string, targs []types2.Type) string {
- b := bytes.NewBufferString(name)
- b.WriteByte('[')
- for i, targ := range targs {
- if i > 0 {
- b.WriteByte(',')
- }
- tname := types2.TypeString(targ,
- func(*types2.Package) string { return "" })
- if strings.Index(tname, ", ") >= 0 {
- // types2.TypeString puts spaces after a comma in a type
- // list, but we don't want spaces in our actual type names
- // and method/function names derived from them.
- tname = strings.Replace(tname, ", ", ",", -1)
- }
- b.WriteString(tname)
+func (g *irgen) instTypeName2(name string, targs *types2.TypeList) string {
+ rparams := make([]*types.Type, targs.Len())
+ for i := range rparams {
+ rparams[i] = g.typ(targs.At(i))
}
- b.WriteByte(']')
- return b.String()
+ return typecheck.InstTypeName(name, rparams)
}
// typ0 converts a types2.Type to a types.Type, but doesn't do the caching check
@@ -89,60 +96,76 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
case *types2.Basic:
return g.basic(typ)
case *types2.Named:
- if typ.TParams() != nil {
+ // If tparams is set, but targs is not, typ is a base generic
+ // type. typ is appearing as part of the source type of an alias,
+ // since that is the only use of a generic type that doesn't
+ // involve instantiation. We just translate the named type in the
+ // normal way below using g.obj().
+ if typ.TypeParams() != nil && typ.TypeArgs() != nil {
// typ is an instantiation of a defined (named) generic type.
// This instantiation should also be a defined (named) type.
// types2 gives us the substituted type in t.Underlying()
// The substituted type may or may not still have type
// params. We might, for example, be substituting one type
// param for another type param.
-
- if typ.TArgs() == nil {
- base.Fatalf("In typ0, Targs should be set if TParams is set")
- }
-
- // When converted to types.Type, typ must have a name,
- // based on the names of the type arguments. We need a
- // name to deal with recursive generic types (and it also
- // looks better when printing types).
- instName := instTypeName2(typ.Obj().Name(), typ.TArgs())
+ //
+ // When converted to types.Type, typ has a unique name,
+ // based on the names of the type arguments.
+ instName := g.instTypeName2(typ.Obj().Name(), typ.TypeArgs())
s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
if s.Def != nil {
- // We have already encountered this instantiation,
- // so use the type we previously created, since there
+ // We have already encountered this instantiation.
+ // Use the type we previously created, since there
// must be exactly one instance of a defined type.
return s.Def.Type()
}
+ // Make sure the base generic type exists in type1 (it may
+ // not yet if we are referecing an imported generic type, as
+ // opposed to a generic type declared in this package).
+ _ = g.obj(typ.Origin().Obj())
+
// Create a forwarding type first and put it in the g.typs
- // map, in order to deal with recursive generic types.
- // Fully set up the extra ntyp information (Def, RParams,
- // which may set HasTParam) before translating the
- // underlying type itself, so we handle recursion
- // correctly, including via method signatures.
- ntyp := newIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
+ // map, in order to deal with recursive generic types
+ // (including via method signatures). Set up the extra
+ // ntyp information (Def, RParams, which may set
+ // HasTParam) before translating the underlying type
+ // itself, so we handle recursion correctly.
+ ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s)
g.typs[typ] = ntyp
// If ntyp still has type params, then we must be
// referencing something like 'value[T2]', as when
- // specifying the generic receiver of a method,
- // where value was defined as "type value[T any]
- // ...". Save the type args, which will now be the
- // new type of the current type.
+ // specifying the generic receiver of a method, where
+ // value was defined as "type value[T any] ...". Save the
+ // type args, which will now be the new typeparams of the
+ // current type.
//
// If ntyp does not have type params, we are saving the
- // concrete types used to instantiate this type. We'll use
- // these when instantiating the methods of the
+ // non-generic types used to instantiate this type. We'll
+ // use these when instantiating the methods of the
// instantiated type.
- rparams := make([]*types.Type, len(typ.TArgs()))
- for i, targ := range typ.TArgs() {
- rparams[i] = g.typ1(targ)
+ targs := typ.TypeArgs()
+ rparams := make([]*types.Type, targs.Len())
+ for i := range rparams {
+ rparams[i] = g.typ1(targs.At(i))
}
ntyp.SetRParams(rparams)
//fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam())
+ // Save the symbol for the base generic type.
+ ntyp.SetOrigSym(g.pkg(typ.Obj().Pkg()).Lookup(typ.Obj().Name()))
ntyp.SetUnderlying(g.typ1(typ.Underlying()))
- g.fillinMethods(typ, ntyp)
+ if typ.NumMethods() != 0 {
+ // Save a delayed call to g.fillinMethods() (once
+ // potentially recursive types have been fully
+ // resolved).
+ g.typesToFinalize = append(g.typesToFinalize,
+ &typeDelayInfo{
+ typ: typ,
+ ntyp: ntyp,
+ })
+ }
return ntyp
}
obj := g.obj(typ.Obj())
@@ -183,12 +206,9 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
for i := range embeddeds {
// TODO(mdempsky): Get embedding position.
e := typ.EmbeddedType(i)
- if t := types2.AsInterface(e); t != nil && t.IsComparable() {
- // Ignore predefined type 'comparable', since it
- // doesn't resolve and it doesn't have any
- // relevant methods.
- continue
- }
+
+ // With Go 1.18, an embedded element can be any type, not
+ // just an interface.
embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e))
j++
}
@@ -197,27 +217,50 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
methods := make([]*types.Field, typ.NumExplicitMethods())
for i := range methods {
m := typ.ExplicitMethod(i)
- mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature))
+ mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature))
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:
- tp := types.NewTypeParam(g.tpkg(typ))
// Save the name of the type parameter in the sym of the type.
// Include the types2 subscript in the sym name
- sym := g.pkg(typ.Obj().Pkg()).Lookup(types2.TypeString(typ, func(*types2.Package) string { return "" }))
- tp.SetSym(sym)
+ pkg := g.tpkg(typ)
+ // Create the unique types1 name for a type param, using its context with a
+ // function, type, or method declaration.
+ assert(g.curDecl != "")
+ nm := g.curDecl + "." + typ.Obj().Name()
+ sym := pkg.Lookup(nm)
+ if sym.Def != nil {
+ // Make sure we use the same type param type for the same
+ // name, whether it is created during types1-import or
+ // this types2-to-types1 translation.
+ return sym.Def.Type()
+ }
+ tp := types.NewTypeParam(sym, typ.Index())
+ nname := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym)
+ sym.Def = nname
+ nname.SetType(tp)
+ tp.SetNod(nname)
// Set g.typs[typ] in case the bound methods reference typ.
g.typs[typ] = tp
- // TODO(danscales): we don't currently need to use the bounds
- // anywhere, so eventually we can probably remove.
- bound := g.typ1(typ.Bound())
- *tp.Methods() = *bound.Methods()
+ bound := g.typ1(typ.Constraint())
+ tp.SetBound(bound)
return tp
+ case *types2.Union:
+ nt := typ.Len()
+ tlist := make([]*types.Type, nt)
+ tildes := make([]bool, nt)
+ for i := range tlist {
+ t := typ.Term(i)
+ tlist[i] = g.typ1(t.Type())
+ tildes[i] = t.Tilde()
+ }
+ return types.NewUnion(tlist, tildes)
+
case *types2.Tuple:
// Tuples are used for the type of a function call (i.e. the
// return value of the function).
@@ -238,76 +281,92 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
}
}
-// fillinMethods fills in the method name nodes and types for a defined type. This
-// is needed for later typechecking when looking up methods of instantiated types,
-// and for actually generating the methods for instantiated types.
+// fillinMethods fills in the method name nodes and types for a defined type with at
+// least one method. This is needed for later typechecking when looking up methods of
+// instantiated types, and for actually generating the methods for instantiated
+// types.
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
- if typ.NumMethods() != 0 {
- targs := make([]ir.Node, len(typ.TArgs()))
- for i, targ := range typ.TArgs() {
- targs[i] = ir.TypeNode(g.typ1(targ))
- }
+ targs2 := typ.TypeArgs()
+ targs := make([]*types.Type, targs2.Len())
+ for i := range targs {
+ targs[i] = g.typ1(targs2.At(i))
+ }
- methods := make([]*types.Field, typ.NumMethods())
- for i := range methods {
- m := typ.Method(i)
- meth := g.obj(m)
- recvType := types2.AsSignature(m.Type()).Recv().Type()
- ptr := types2.AsPointer(recvType)
- if ptr != nil {
- recvType = ptr.Elem()
- }
- if recvType != types2.Type(typ) {
- // Unfortunately, meth is the type of the method of the
- // generic type, so we have to do a substitution to get
- // the name/type of the method of the instantiated type,
- // using m.Type().RParams() and typ.TArgs()
- inst2 := instTypeName2("", typ.TArgs())
- name := meth.Sym().Name
- i1 := strings.Index(name, "[")
- i2 := strings.Index(name[i1:], "]")
- assert(i1 >= 0 && i2 >= 0)
- // Generate the name of the instantiated method.
- name = name[0:i1] + inst2 + name[i1+i2+1:]
- newsym := meth.Sym().Pkg.Lookup(name)
- var meth2 *ir.Name
- if newsym.Def != nil {
- meth2 = newsym.Def.(*ir.Name)
- } else {
- meth2 = ir.NewNameAt(meth.Pos(), newsym)
- rparams := types2.AsSignature(m.Type()).RParams()
- tparams := make([]*types.Field, len(rparams))
- for i, rparam := range rparams {
- tparams[i] = types.NewField(src.NoXPos, nil, g.typ1(rparam.Type()))
- }
- assert(len(tparams) == len(targs))
- subst := &subster{
- g: g,
- tparams: tparams,
- targs: targs,
- }
- // Do the substitution of the type
- meth2.SetType(subst.typ(meth.Type()))
- newsym.Def = meth2
+ methods := make([]*types.Field, typ.NumMethods())
+ for i := range methods {
+ m := typ.Method(i)
+ recvType := deref2(types2.AsSignature(m.Type()).Recv().Type())
+ var meth *ir.Name
+ imported := false
+ if m.Pkg() != g.self {
+ // Imported methods cannot be loaded by name (what
+ // g.obj() does) - they must be loaded via their
+ // type.
+ meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name)
+ // XXX Because Obj() returns the object of the base generic
+ // type, we have to still do the method translation below.
+ imported = true
+ } else {
+ meth = g.obj(m)
+ }
+ assert(recvType == types2.Type(typ))
+ if imported {
+ // Unfortunately, meth is the type of the method of the
+ // generic type, so we have to do a substitution to get
+ // the name/type of the method of the instantiated type,
+ // using m.Type().RParams() and typ.TArgs()
+ inst2 := g.instTypeName2("", typ.TypeArgs())
+ name := meth.Sym().Name
+ i1 := strings.Index(name, "[")
+ i2 := strings.Index(name[i1:], "]")
+ assert(i1 >= 0 && i2 >= 0)
+ // Generate the name of the instantiated method.
+ name = name[0:i1] + inst2 + name[i1+i2+1:]
+ newsym := meth.Sym().Pkg.Lookup(name)
+ var meth2 *ir.Name
+ if newsym.Def != nil {
+ meth2 = newsym.Def.(*ir.Name)
+ } else {
+ meth2 = ir.NewNameAt(meth.Pos(), newsym)
+ rparams := types2.AsSignature(m.Type()).RecvTypeParams()
+ tparams := make([]*types.Type, rparams.Len())
+ // Set g.curDecl to be the method context, so type
+ // params in the receiver of the method that we are
+ // translating gets the right unique name. We could
+ // be in a top-level typeDecl, so save and restore
+ // the current contents of g.curDecl.
+ savedCurDecl := g.curDecl
+ g.curDecl = typ.Obj().Name() + "." + m.Name()
+ for i := range tparams {
+ tparams[i] = g.typ1(rparams.At(i))
}
- meth = meth2
+ g.curDecl = savedCurDecl
+ assert(len(tparams) == len(targs))
+ ts := typecheck.Tsubster{
+ Tparams: tparams,
+ Targs: targs,
+ }
+ // Do the substitution of the type
+ meth2.SetType(ts.Typ(meth.Type()))
+ newsym.Def = meth2
}
- methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
- methods[i].Nname = meth
- }
- ntyp.Methods().Set(methods)
- if !ntyp.HasTParam() {
- // Generate all the methods for a new fully-instantiated type.
- g.instTypeList = append(g.instTypeList, ntyp)
+ meth = meth2
}
+ methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
+ methods[i].Nname = meth
+ }
+ ntyp.Methods().Set(methods)
+ if !ntyp.HasTParam() && !ntyp.HasShape() {
+ // Generate all the methods for a new fully-instantiated type.
+ typecheck.NeedInstType(ntyp)
}
}
func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
- tparams2 := sig.TParams()
- tparams := make([]*types.Field, len(tparams2))
+ tparams2 := sig.TypeParams()
+ tparams := make([]*types.Field, tparams2.Len())
for i := range tparams {
- tp := tparams2[i]
+ tp := tparams2.At(i).Obj()
tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type()))
}
@@ -346,7 +405,7 @@ func (g *irgen) selector(obj types2.Object) *types.Sym {
return pkg.Lookup(name)
}
-// tpkg returns the package that a function, interface, or struct type
+// tpkg returns the package that a function, interface, struct, or typeparam type
// expression appeared in.
//
// Caveat: For the degenerate types "func()", "interface{}", and
@@ -356,36 +415,39 @@ func (g *irgen) selector(obj types2.Object) *types.Sym {
// particular types is because go/types does *not* report it for
// them. So in practice this limitation is probably moot.
func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
- anyObj := func() types2.Object {
- switch typ := typ.(type) {
- case *types2.Signature:
- if recv := typ.Recv(); recv != nil {
- return recv
- }
- if params := typ.Params(); params.Len() > 0 {
- return params.At(0)
- }
- if results := typ.Results(); results.Len() > 0 {
- return results.At(0)
- }
- case *types2.Struct:
- if typ.NumFields() > 0 {
- return typ.Field(0)
- }
- case *types2.Interface:
- if typ.NumExplicitMethods() > 0 {
- return typ.ExplicitMethod(0)
- }
- }
- return nil
- }
-
- if obj := anyObj(); obj != nil {
+ if obj := anyObj(typ); obj != nil {
return g.pkg(obj.Pkg())
}
return types.LocalPkg
}
+// anyObj returns some object accessible from typ, if any.
+func anyObj(typ types2.Type) types2.Object {
+ switch typ := typ.(type) {
+ case *types2.Signature:
+ if recv := typ.Recv(); recv != nil {
+ return recv
+ }
+ if params := typ.Params(); params.Len() > 0 {
+ return params.At(0)
+ }
+ if results := typ.Results(); results.Len() > 0 {
+ return results.At(0)
+ }
+ case *types2.Struct:
+ if typ.NumFields() > 0 {
+ return typ.Field(0)
+ }
+ case *types2.Interface:
+ if typ.NumExplicitMethods() > 0 {
+ return typ.ExplicitMethod(0)
+ }
+ case *types2.TypeParam:
+ return typ.Obj()
+ }
+ return nil
+}
+
func (g *irgen) basic(typ *types2.Basic) *types.Type {
switch typ.Name() {
case "byte":
@@ -430,3 +492,11 @@ var dirs = [...]types.ChanDir{
types2.SendOnly: types.Csend,
types2.RecvOnly: types.Crecv,
}
+
+// deref2 does a single deref of types2 type t, if it is a pointer type.
+func deref2(t types2.Type) types2.Type {
+ if ptr := types2.AsPointer(t); ptr != nil {
+ t = ptr.Elem()
+ }
+ return t
+}
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec0012db4cec44bb732597f5843ffb2db863f8b9
--- /dev/null
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -0,0 +1,334 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "bytes"
+ "fmt"
+ "internal/goversion"
+ "io"
+ "runtime"
+ "sort"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/inline"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
+ "cmd/internal/src"
+)
+
+// localPkgReader holds the package reader used for reading the local
+// package. It exists so the unified IR linker can refer back to it
+// later.
+var localPkgReader *pkgReader
+
+// unified construct the local package's IR from syntax's AST.
+//
+// The pipeline contains 2 steps:
+//
+// (1) Generate package export data "stub".
+//
+// (2) Generate package IR from package export data.
+//
+// The package data "stub" at step (1) contains everything from the local package,
+// but nothing that have been imported. When we're actually writing out export data
+// to the output files (see writeNewExport function), we run the "linker", which does
+// a few things:
+//
+// + Updates compiler extensions data (e.g., inlining cost, escape analysis results).
+//
+// + Handles re-exporting any transitive dependencies.
+//
+// + Prunes out any unnecessary details (e.g., non-inlineable functions, because any
+// downstream importers only care about inlinable functions).
+//
+// The source files are typechecked twice, once before writing export data
+// using types2 checker, once after read export data using gc/typecheck.
+// This duplication of work will go away once we always use types2 checker,
+// we can remove the gc/typecheck pass. The reason it is still here:
+//
+// + It reduces engineering costs in maintaining a fork of typecheck
+// (e.g., no need to backport fixes like CL 327651).
+//
+// + It makes it easier to pass toolstash -cmp.
+//
+// + Historically, we would always re-run the typechecker after import, even though
+// we know the imported data is valid. It's not ideal, but also not causing any
+// problem either.
+//
+// + There's still transformation that being done during gc/typecheck, like rewriting
+// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP.
+//
+// Using syntax+types2 tree, which already has a complete representation of generics,
+// the unified IR has the full typed AST for doing introspection during step (1).
+// In other words, we have all necessary information to build the generic IR form
+// (see writer.captureVars for an example).
+func unified(noders []*noder) {
+ inline.NewInline = InlineCall
+
+ if !quirksMode() {
+ writeNewExportFunc = writeNewExport
+ } else if base.Flag.G != 0 {
+ base.Errorf("cannot use -G and -d=quirksmode together")
+ }
+
+ newReadImportFunc = func(data string, pkg1 *types.Pkg, ctxt *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
+ pr := newPkgDecoder(pkg1.Path, data)
+
+ // Read package descriptors for both types2 and compiler backend.
+ readPackage(newPkgReader(pr), pkg1)
+ pkg2 = readPackage2(ctxt, packages, pr)
+ return
+ }
+
+ data := writePkgStub(noders)
+
+ // We already passed base.Flag.Lang to types2 to handle validating
+ // the user's source code. Bump it up now to the current version and
+ // re-parse, so typecheck doesn't complain if we construct IR that
+ // utilizes newer Go features.
+ base.Flag.Lang = fmt.Sprintf("go1.%d", goversion.Version)
+ types.ParseLangFlag()
+
+ assert(types.LocalPkg.Path == "")
+ types.LocalPkg.Height = 0 // reset so pkgReader.pkgIdx doesn't complain
+ target := typecheck.Target
+
+ typecheck.TypecheckAllowed = true
+
+ localPkgReader = newPkgReader(newPkgDecoder(types.LocalPkg.Path, data))
+ readPackage(localPkgReader, types.LocalPkg)
+
+ r := localPkgReader.newReader(relocMeta, privateRootIdx, syncPrivate)
+ r.pkgInit(types.LocalPkg, target)
+
+ // Type-check any top-level assignments. We ignore non-assignments
+ // here because other declarations are typechecked as they're
+ // constructed.
+ for i, ndecls := 0, len(target.Decls); i < ndecls; i++ {
+ switch n := target.Decls[i]; n.Op() {
+ case ir.OAS, ir.OAS2:
+ target.Decls[i] = typecheck.Stmt(n)
+ }
+ }
+
+ // Don't use range--bodyIdx can add closures to todoBodies.
+ for len(todoBodies) > 0 {
+ // The order we expand bodies doesn't matter, so pop from the end
+ // to reduce todoBodies reallocations if it grows further.
+ fn := todoBodies[len(todoBodies)-1]
+ todoBodies = todoBodies[:len(todoBodies)-1]
+
+ pri, ok := bodyReader[fn]
+ assert(ok)
+ pri.funcBody(fn)
+
+ // Instantiated generic function: add to Decls for typechecking
+ // and compilation.
+ if fn.OClosure == nil && len(pri.dict.targs) != 0 {
+ target.Decls = append(target.Decls, fn)
+ }
+ }
+ todoBodies = nil
+ todoBodiesDone = true
+
+ // Check that nothing snuck past typechecking.
+ for _, n := range target.Decls {
+ if n.Typecheck() == 0 {
+ base.FatalfAt(n.Pos(), "missed typecheck: %v", n)
+ }
+
+ // For functions, check that at least their first statement (if
+ // any) was typechecked too.
+ if fn, ok := n.(*ir.Func); ok && len(fn.Body) != 0 {
+ if stmt := fn.Body[0]; stmt.Typecheck() == 0 {
+ base.FatalfAt(stmt.Pos(), "missed typecheck: %v", stmt)
+ }
+ }
+ }
+
+ base.ExitIfErrors() // just in case
+}
+
+// writePkgStub type checks the given parsed source files,
+// writes an export data package stub representing them,
+// and returns the result.
+func writePkgStub(noders []*noder) string {
+ m, pkg, info := checkFiles(noders)
+
+ pw := newPkgWriter(m, pkg, info)
+
+ pw.collectDecls(noders)
+
+ publicRootWriter := pw.newWriter(relocMeta, syncPublic)
+ privateRootWriter := pw.newWriter(relocMeta, syncPrivate)
+
+ assert(publicRootWriter.idx == publicRootIdx)
+ assert(privateRootWriter.idx == privateRootIdx)
+
+ {
+ w := publicRootWriter
+ w.pkg(pkg)
+ w.bool(false) // has init; XXX
+
+ scope := pkg.Scope()
+ names := scope.Names()
+ w.len(len(names))
+ for _, name := range scope.Names() {
+ w.obj(scope.Lookup(name), nil)
+ }
+
+ w.sync(syncEOF)
+ w.flush()
+ }
+
+ {
+ w := privateRootWriter
+ w.pkgInit(noders)
+ w.flush()
+ }
+
+ var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
+ pw.dump(&sb)
+
+ // At this point, we're done with types2. Make sure the package is
+ // garbage collected.
+ freePackage(pkg)
+
+ return sb.String()
+}
+
+// freePackage ensures the given package is garbage collected.
+func freePackage(pkg *types2.Package) {
+ // The GC test below relies on a precise GC that runs finalizers as
+ // soon as objects are unreachable. Our implementation provides
+ // this, but other/older implementations may not (e.g., Go 1.4 does
+ // not because of #22350). To avoid imposing unnecessary
+ // restrictions on the GOROOT_BOOTSTRAP toolchain, we skip the test
+ // during bootstrapping.
+ if base.CompilerBootstrap {
+ return
+ }
+
+ // Set a finalizer on pkg so we can detect if/when it's collected.
+ done := make(chan struct{})
+ runtime.SetFinalizer(pkg, func(*types2.Package) { close(done) })
+
+ // Important: objects involved in cycles are not finalized, so zero
+ // out pkg to break its cycles and allow the finalizer to run.
+ *pkg = types2.Package{}
+
+ // It typically takes just 1 or 2 cycles to release pkg, but it
+ // doesn't hurt to try a few more times.
+ for i := 0; i < 10; i++ {
+ select {
+ case <-done:
+ return
+ default:
+ runtime.GC()
+ }
+ }
+
+ base.Fatalf("package never finalized")
+}
+
+func readPackage(pr *pkgReader, importpkg *types.Pkg) {
+ r := pr.newReader(relocMeta, publicRootIdx, syncPublic)
+
+ pkg := r.pkg()
+ assert(pkg == importpkg)
+
+ if r.bool() {
+ sym := pkg.Lookup(".inittask")
+ task := ir.NewNameAt(src.NoXPos, sym)
+ task.Class = ir.PEXTERN
+ sym.Def = task
+ }
+
+ for i, n := 0, r.len(); i < n; i++ {
+ r.sync(syncObject)
+ assert(!r.bool())
+ idx := r.reloc(relocObj)
+ assert(r.len() == 0)
+
+ path, name, code := r.p.peekObj(idx)
+ if code != objStub {
+ objReader[types.NewPkg(path, "").Lookup(name)] = pkgReaderIndex{pr, idx, nil}
+ }
+ }
+}
+
+func writeNewExport(out io.Writer) {
+ l := linker{
+ pw: newPkgEncoder(),
+
+ pkgs: make(map[string]int),
+ decls: make(map[*types.Sym]int),
+ }
+
+ publicRootWriter := l.pw.newEncoder(relocMeta, syncPublic)
+ assert(publicRootWriter.idx == publicRootIdx)
+
+ var selfPkgIdx int
+
+ {
+ pr := localPkgReader
+ r := pr.newDecoder(relocMeta, publicRootIdx, syncPublic)
+
+ r.sync(syncPkg)
+ selfPkgIdx = l.relocIdx(pr, relocPkg, r.reloc(relocPkg))
+
+ r.bool() // has init
+
+ for i, n := 0, r.len(); i < n; i++ {
+ r.sync(syncObject)
+ assert(!r.bool())
+ idx := r.reloc(relocObj)
+ assert(r.len() == 0)
+
+ xpath, xname, xtag := pr.peekObj(idx)
+ assert(xpath == pr.pkgPath)
+ assert(xtag != objStub)
+
+ if types.IsExported(xname) {
+ l.relocIdx(pr, relocObj, idx)
+ }
+ }
+
+ r.sync(syncEOF)
+ }
+
+ {
+ var idxs []int
+ for _, idx := range l.decls {
+ idxs = append(idxs, idx)
+ }
+ sort.Ints(idxs)
+
+ w := publicRootWriter
+
+ w.sync(syncPkg)
+ w.reloc(relocPkg, selfPkgIdx)
+
+ w.bool(typecheck.Lookup(".inittask").Def != nil)
+
+ w.len(len(idxs))
+ for _, idx := range idxs {
+ w.sync(syncObject)
+ w.bool(false)
+ w.reloc(relocObj, idx)
+ w.len(0)
+ }
+
+ w.sync(syncEOF)
+ w.flush()
+ }
+
+ l.pw.dump(out)
+}
diff --git a/src/cmd/compile/internal/noder/unified_test.go b/src/cmd/compile/internal/noder/unified_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d7334df282885f0c4ffb2ac6542c1585da7a6c04
--- /dev/null
+++ b/src/cmd/compile/internal/noder/unified_test.go
@@ -0,0 +1,160 @@
+// 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 noder_test
+
+import (
+ "encoding/json"
+ "flag"
+ exec "internal/execabs"
+ "os"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+var (
+ flagCmp = flag.Bool("cmp", false, "enable TestUnifiedCompare")
+ flagPkgs = flag.String("pkgs", "std", "list of packages to compare (ignored in -short mode)")
+ flagAll = flag.Bool("all", false, "enable testing of all GOOS/GOARCH targets")
+ flagParallel = flag.Bool("parallel", false, "test GOOS/GOARCH targets in parallel")
+)
+
+// TestUnifiedCompare implements a test similar to running:
+//
+// $ go build -toolexec="toolstash -cmp" std
+//
+// The -pkgs flag controls the list of packages tested.
+//
+// By default, only the native GOOS/GOARCH target is enabled. The -all
+// flag enables testing of non-native targets. The -parallel flag
+// additionally enables testing of targets in parallel.
+//
+// Caution: Testing all targets is very resource intensive! On an IBM
+// P920 (dual Intel Xeon Gold 6154 CPUs; 36 cores, 192GB RAM), testing
+// all targets in parallel takes about 5 minutes. Using the 'go test'
+// command's -run flag for subtest matching is recommended for less
+// powerful machines.
+func TestUnifiedCompare(t *testing.T) {
+ // TODO(mdempsky): Either re-enable or delete. Disabled for now to
+ // avoid impeding others' forward progress.
+ if !*flagCmp {
+ t.Skip("skipping TestUnifiedCompare (use -cmp to enable)")
+ }
+
+ targets, err := exec.Command("go", "tool", "dist", "list").Output()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, target := range strings.Fields(string(targets)) {
+ t.Run(target, func(t *testing.T) {
+ parts := strings.Split(target, "/")
+ goos, goarch := parts[0], parts[1]
+
+ if !(*flagAll || goos == runtime.GOOS && goarch == runtime.GOARCH) {
+ t.Skip("skipping non-native target (use -all to enable)")
+ }
+ if *flagParallel {
+ t.Parallel()
+ }
+
+ pkgs1 := loadPackages(t, goos, goarch, "-d=unified=0 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0")
+ pkgs2 := loadPackages(t, goos, goarch, "-d=unified=1 -d=inlfuncswithclosures=0 -d=unifiedquirks=1 -G=0")
+
+ if len(pkgs1) != len(pkgs2) {
+ t.Fatalf("length mismatch: %v != %v", len(pkgs1), len(pkgs2))
+ }
+
+ for i := range pkgs1 {
+ pkg1 := pkgs1[i]
+ pkg2 := pkgs2[i]
+
+ path := pkg1.ImportPath
+ if path != pkg2.ImportPath {
+ t.Fatalf("mismatched paths: %q != %q", path, pkg2.ImportPath)
+ }
+
+ // Packages that don't have any source files (e.g., packages
+ // unsafe, embed/internal/embedtest, and cmd/internal/moddeps).
+ if pkg1.Export == "" && pkg2.Export == "" {
+ continue
+ }
+
+ if pkg1.BuildID == pkg2.BuildID {
+ t.Errorf("package %q: build IDs unexpectedly matched", path)
+ }
+
+ // Unlike toolstash -cmp, we're comparing the same compiler
+ // binary against itself, just with different flags. So we
+ // don't need to worry about skipping over mismatched version
+ // strings, but we do need to account for differing build IDs.
+ //
+ // Fortunately, build IDs are cryptographic 256-bit hashes,
+ // and cmd/go provides us with them up front. So we can just
+ // use them as delimeters to split the files, and then check
+ // that the substrings are all equal.
+ file1 := strings.Split(readFile(t, pkg1.Export), pkg1.BuildID)
+ file2 := strings.Split(readFile(t, pkg2.Export), pkg2.BuildID)
+ if !reflect.DeepEqual(file1, file2) {
+ t.Errorf("package %q: compile output differs", path)
+ }
+ }
+ })
+ }
+}
+
+type pkg struct {
+ ImportPath string
+ Export string
+ BuildID string
+ Incomplete bool
+}
+
+func loadPackages(t *testing.T, goos, goarch, gcflags string) []pkg {
+ args := []string{"list", "-e", "-export", "-json", "-gcflags=all=" + gcflags, "--"}
+ if testing.Short() {
+ t.Log("short testing mode; only testing package runtime")
+ args = append(args, "runtime")
+ } else {
+ args = append(args, strings.Fields(*flagPkgs)...)
+ }
+
+ cmd := exec.Command("go", args...)
+ cmd.Env = append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch)
+ cmd.Stderr = os.Stderr
+ t.Logf("running %v", cmd)
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ var res []pkg
+ for dec := json.NewDecoder(stdout); dec.More(); {
+ var pkg pkg
+ if err := dec.Decode(&pkg); err != nil {
+ t.Fatal(err)
+ }
+ if pkg.Incomplete {
+ t.Fatalf("incomplete package: %q", pkg.ImportPath)
+ }
+ res = append(res, pkg)
+ }
+ if err := cmd.Wait(); err != nil {
+ t.Fatal(err)
+ }
+ return res
+}
+
+func readFile(t *testing.T, name string) string {
+ buf, err := os.ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return string(buf)
+}
diff --git a/src/cmd/compile/internal/noder/validate.go b/src/cmd/compile/internal/noder/validate.go
index b926222c89c417ccf7a691f3fc9fe60a0c68e1ae..dcacae7480c2975c50182aeb72d46d31e374d4f4 100644
--- a/src/cmd/compile/internal/noder/validate.go
+++ b/src/cmd/compile/internal/noder/validate.go
@@ -55,7 +55,15 @@ func (g *irgen) validate(n syntax.Node) {
case *syntax.CallExpr:
tv := g.info.Types[n.Fun]
if tv.IsBuiltin() {
- switch builtin := n.Fun.(type) {
+ fun := n.Fun
+ for {
+ builtin, ok := fun.(*syntax.ParenExpr)
+ if !ok {
+ break
+ }
+ fun = builtin.X
+ }
+ switch builtin := fun.(type) {
case *syntax.Name:
g.validateBuiltin(builtin.Value, n)
case *syntax.SelectorExpr:
@@ -73,7 +81,16 @@ func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
// Check that types2+gcSizes calculates sizes the same
// as cmd/compile does.
- got, ok := constant.Int64Val(g.info.Types[call].Value)
+ tv := g.info.Types[call]
+ if !tv.IsValue() {
+ base.FatalfAt(g.pos(call), "expected a value")
+ }
+
+ if tv.Value == nil {
+ break // unsafe op is not a constant, so no further validation
+ }
+
+ got, ok := constant.Int64Val(tv.Value)
if !ok {
base.FatalfAt(g.pos(call), "expected int64 constant value")
}
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..2bb0b4d5d750bb01388fe31eb32f9a9d4081e522
--- /dev/null
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -0,0 +1,1861 @@
+// UNREVIEWED
+
+// 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 noder
+
+import (
+ "fmt"
+ "go/constant"
+
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+)
+
+type pkgWriter struct {
+ pkgEncoder
+
+ m posMap
+ curpkg *types2.Package
+ info *types2.Info
+
+ posBasesIdx map[*syntax.PosBase]int
+ pkgsIdx map[*types2.Package]int
+ typsIdx map[types2.Type]int
+ globalsIdx map[types2.Object]int
+
+ funDecls map[*types2.Func]*syntax.FuncDecl
+ typDecls map[*types2.TypeName]typeDeclGen
+
+ linknames map[types2.Object]string
+ cgoPragmas [][]string
+
+ dups dupTypes
+}
+
+func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
+ return &pkgWriter{
+ pkgEncoder: newPkgEncoder(),
+
+ m: m,
+ curpkg: pkg,
+ info: info,
+
+ pkgsIdx: make(map[*types2.Package]int),
+ globalsIdx: make(map[types2.Object]int),
+ typsIdx: make(map[types2.Type]int),
+
+ posBasesIdx: make(map[*syntax.PosBase]int),
+
+ funDecls: make(map[*types2.Func]*syntax.FuncDecl),
+ typDecls: make(map[*types2.TypeName]typeDeclGen),
+
+ linknames: make(map[types2.Object]string),
+ }
+}
+
+func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) {
+ base.ErrorfAt(pw.m.pos(p), msg, args...)
+}
+
+func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) {
+ base.FatalfAt(pw.m.pos(p), msg, args...)
+}
+
+func (pw *pkgWriter) unexpected(what string, p poser) {
+ pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
+}
+
+type writer struct {
+ p *pkgWriter
+
+ encoder
+
+ // TODO(mdempsky): We should be able to prune localsIdx whenever a
+ // scope closes, and then maybe we can just use the same map for
+ // storing the TypeParams too (as their TypeName instead).
+
+ // variables declared within this function
+ localsIdx map[*types2.Var]int
+
+ closureVars []posObj
+ closureVarsIdx map[*types2.Var]int
+
+ dict *writerDict
+ derived bool
+}
+
+// A writerDict tracks types and objects that are used by a declaration.
+type writerDict struct {
+ implicits []*types2.TypeName
+
+ // derived is a slice of type indices for computing derived types
+ // (i.e., types that depend on the declaration's type parameters).
+ derived []derivedInfo
+
+ // derivedIdx maps a Type to its corresponding index within the
+ // derived slice, if present.
+ derivedIdx map[types2.Type]int
+
+ // funcs lists references to generic functions that were
+ // instantiated with derived types (i.e., that require
+ // sub-dictionaries when called at run time).
+ funcs []objInfo
+}
+
+type derivedInfo struct {
+ idx int
+ needed bool
+}
+
+type typeInfo struct {
+ idx int
+ derived bool
+}
+
+type objInfo struct {
+ idx int // index for the generic function declaration
+ explicits []typeInfo // info for the type arguments
+}
+
+func (info objInfo) anyDerived() bool {
+ for _, explicit := range info.explicits {
+ if explicit.derived {
+ return true
+ }
+ }
+ return false
+}
+
+func (info objInfo) equals(other objInfo) bool {
+ if info.idx != other.idx {
+ return false
+ }
+ assert(len(info.explicits) == len(other.explicits))
+ for i, targ := range info.explicits {
+ if targ != other.explicits[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func (pw *pkgWriter) newWriter(k reloc, marker syncMarker) *writer {
+ return &writer{
+ encoder: pw.newEncoder(k, marker),
+ p: pw,
+ }
+}
+
+// @@@ Positions
+
+func (w *writer) pos(p poser) {
+ w.sync(syncPos)
+ pos := p.Pos()
+
+ // TODO(mdempsky): Track down the remaining cases here and fix them.
+ if !w.bool(pos.IsKnown()) {
+ return
+ }
+
+ // TODO(mdempsky): Delta encoding. Also, if there's a b-side, update
+ // its position base too (but not vice versa!).
+ w.posBase(pos.Base())
+ w.uint(pos.Line())
+ w.uint(pos.Col())
+}
+
+func (w *writer) posBase(b *syntax.PosBase) {
+ w.reloc(relocPosBase, w.p.posBaseIdx(b))
+}
+
+func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) int {
+ if idx, ok := pw.posBasesIdx[b]; ok {
+ return idx
+ }
+
+ w := pw.newWriter(relocPosBase, syncPosBase)
+ w.p.posBasesIdx[b] = w.idx
+
+ w.string(trimFilename(b))
+
+ if !w.bool(b.IsFileBase()) {
+ w.pos(b)
+ w.uint(b.Line())
+ w.uint(b.Col())
+ }
+
+ return w.flush()
+}
+
+// @@@ Packages
+
+func (w *writer) pkg(pkg *types2.Package) {
+ w.sync(syncPkg)
+ w.reloc(relocPkg, w.p.pkgIdx(pkg))
+}
+
+func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int {
+ if idx, ok := pw.pkgsIdx[pkg]; ok {
+ return idx
+ }
+
+ w := pw.newWriter(relocPkg, syncPkgDef)
+ pw.pkgsIdx[pkg] = w.idx
+
+ if pkg == nil {
+ w.string("builtin")
+ } else {
+ var path string
+ if pkg != w.p.curpkg {
+ path = pkg.Path()
+ }
+ w.string(path)
+ w.string(pkg.Name())
+ w.len(pkg.Height())
+
+ w.len(len(pkg.Imports()))
+ for _, imp := range pkg.Imports() {
+ w.pkg(imp)
+ }
+ }
+
+ return w.flush()
+}
+
+// @@@ Types
+
+var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
+
+func (w *writer) typ(typ types2.Type) {
+ w.typInfo(w.p.typIdx(typ, w.dict))
+}
+
+func (w *writer) typInfo(info typeInfo) {
+ w.sync(syncType)
+ if w.bool(info.derived) {
+ w.len(info.idx)
+ w.derived = true
+ } else {
+ w.reloc(relocType, info.idx)
+ }
+}
+
+// typIdx returns the index where the export data description of type
+// can be read back in. If no such index exists yet, it's created.
+//
+// typIdx also reports whether typ is a derived type; that is, whether
+// its identity depends on type parameters.
+func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
+ if quirksMode() {
+ typ = pw.dups.orig(typ)
+ }
+
+ if idx, ok := pw.typsIdx[typ]; ok {
+ return typeInfo{idx: idx, derived: false}
+ }
+ if dict != nil {
+ if idx, ok := dict.derivedIdx[typ]; ok {
+ return typeInfo{idx: idx, derived: true}
+ }
+ }
+
+ w := pw.newWriter(relocType, syncTypeIdx)
+ w.dict = dict
+
+ switch typ := typ.(type) {
+ default:
+ base.Fatalf("unexpected type: %v (%T)", typ, typ)
+
+ case *types2.Basic:
+ switch kind := typ.Kind(); {
+ case kind == types2.Invalid:
+ base.Fatalf("unexpected types2.Invalid")
+
+ case types2.Typ[kind] == typ:
+ w.code(typeBasic)
+ w.len(int(kind))
+
+ default:
+ // Handle "byte" and "rune" as references to their TypeName.
+ obj := types2.Universe.Lookup(typ.Name())
+ assert(obj.Type() == typ)
+
+ w.code(typeNamed)
+ w.obj(obj, nil)
+ }
+
+ case *types2.Named:
+ // Type aliases can refer to uninstantiated generic types, so we
+ // might see len(TParams) != 0 && len(TArgs) == 0 here.
+ // TODO(mdempsky): Revisit after #46477 is resolved.
+ assert(typ.TypeParams().Len() == typ.TypeArgs().Len() || typ.TypeArgs().Len() == 0)
+
+ // TODO(mdempsky): Why do we need to loop here?
+ orig := typ
+ for orig.TypeArgs() != nil {
+ orig = orig.Origin()
+ }
+
+ w.code(typeNamed)
+ w.obj(orig.Obj(), typ.TypeArgs())
+
+ case *types2.TypeParam:
+ index := func() int {
+ for idx, name := range w.dict.implicits {
+ if name.Type().(*types2.TypeParam) == typ {
+ return idx
+ }
+ }
+
+ return len(w.dict.implicits) + typ.Index()
+ }()
+
+ w.derived = true
+ w.code(typeTypeParam)
+ w.len(index)
+
+ case *types2.Array:
+ w.code(typeArray)
+ w.uint64(uint64(typ.Len()))
+ w.typ(typ.Elem())
+
+ case *types2.Chan:
+ w.code(typeChan)
+ w.len(int(typ.Dir()))
+ w.typ(typ.Elem())
+
+ case *types2.Map:
+ w.code(typeMap)
+ w.typ(typ.Key())
+ w.typ(typ.Elem())
+
+ case *types2.Pointer:
+ w.code(typePointer)
+ w.typ(typ.Elem())
+
+ case *types2.Signature:
+ base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
+ w.code(typeSignature)
+ w.signature(typ)
+
+ case *types2.Slice:
+ w.code(typeSlice)
+ w.typ(typ.Elem())
+
+ case *types2.Struct:
+ w.code(typeStruct)
+ w.structType(typ)
+
+ case *types2.Interface:
+ if typ == anyTypeName.Type() {
+ w.code(typeNamed)
+ w.obj(anyTypeName, nil)
+ break
+ }
+
+ w.code(typeInterface)
+ w.interfaceType(typ)
+
+ case *types2.Union:
+ w.code(typeUnion)
+ w.unionType(typ)
+ }
+
+ if w.derived {
+ idx := len(dict.derived)
+ dict.derived = append(dict.derived, derivedInfo{idx: w.flush()})
+ dict.derivedIdx[typ] = idx
+ return typeInfo{idx: idx, derived: true}
+ }
+
+ pw.typsIdx[typ] = w.idx
+ return typeInfo{idx: w.flush(), derived: false}
+}
+
+func (w *writer) structType(typ *types2.Struct) {
+ w.len(typ.NumFields())
+ for i := 0; i < typ.NumFields(); i++ {
+ f := typ.Field(i)
+ w.pos(f)
+ w.selector(f)
+ w.typ(f.Type())
+ w.string(typ.Tag(i))
+ w.bool(f.Embedded())
+ }
+}
+
+func (w *writer) unionType(typ *types2.Union) {
+ w.len(typ.Len())
+ for i := 0; i < typ.Len(); i++ {
+ t := typ.Term(i)
+ w.bool(t.Tilde())
+ w.typ(t.Type())
+ }
+}
+
+func (w *writer) interfaceType(typ *types2.Interface) {
+ w.len(typ.NumExplicitMethods())
+ w.len(typ.NumEmbeddeds())
+
+ for i := 0; i < typ.NumExplicitMethods(); i++ {
+ m := typ.ExplicitMethod(i)
+ sig := m.Type().(*types2.Signature)
+ assert(sig.TypeParams() == nil)
+
+ w.pos(m)
+ w.selector(m)
+ w.signature(sig)
+ }
+
+ for i := 0; i < typ.NumEmbeddeds(); i++ {
+ w.typ(typ.EmbeddedType(i))
+ }
+}
+
+func (w *writer) signature(sig *types2.Signature) {
+ w.sync(syncSignature)
+ w.params(sig.Params())
+ w.params(sig.Results())
+ w.bool(sig.Variadic())
+}
+
+func (w *writer) params(typ *types2.Tuple) {
+ w.sync(syncParams)
+ w.len(typ.Len())
+ for i := 0; i < typ.Len(); i++ {
+ w.param(typ.At(i))
+ }
+}
+
+func (w *writer) param(param *types2.Var) {
+ w.sync(syncParam)
+ w.pos(param)
+ w.localIdent(param)
+ w.typ(param.Type())
+}
+
+// @@@ Objects
+
+func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
+ explicitInfos := make([]typeInfo, explicits.Len())
+ for i := range explicitInfos {
+ explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict)
+ }
+ info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos}
+
+ if _, ok := obj.(*types2.Func); ok && info.anyDerived() {
+ idx := -1
+ for i, prev := range w.dict.funcs {
+ if prev.equals(info) {
+ idx = i
+ }
+ }
+ if idx < 0 {
+ idx = len(w.dict.funcs)
+ w.dict.funcs = append(w.dict.funcs, info)
+ }
+
+ // TODO(mdempsky): Push up into expr; this shouldn't appear
+ // outside of expression context.
+ w.sync(syncObject)
+ w.bool(true)
+ w.len(idx)
+ return
+ }
+
+ // TODO(mdempsky): Push up into typIdx; this shouldn't be needed
+ // except while writing out types.
+ if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
+ decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
+ assert(ok)
+ if len(decl.implicits) != 0 {
+ w.derived = true
+ }
+ }
+
+ w.sync(syncObject)
+ w.bool(false)
+ w.reloc(relocObj, info.idx)
+
+ w.len(len(info.explicits))
+ for _, info := range info.explicits {
+ w.typInfo(info)
+ }
+}
+
+func (pw *pkgWriter) objIdx(obj types2.Object) int {
+ if idx, ok := pw.globalsIdx[obj]; ok {
+ return idx
+ }
+
+ dict := &writerDict{
+ derivedIdx: make(map[types2.Type]int),
+ }
+
+ if isDefinedType(obj) && obj.Pkg() == pw.curpkg {
+ decl, ok := pw.typDecls[obj.(*types2.TypeName)]
+ assert(ok)
+ dict.implicits = decl.implicits
+ }
+
+ w := pw.newWriter(relocObj, syncObject1)
+ wext := pw.newWriter(relocObjExt, syncObject1)
+ wname := pw.newWriter(relocName, syncObject1)
+ wdict := pw.newWriter(relocObjDict, syncObject1)
+
+ pw.globalsIdx[obj] = w.idx // break cycles
+ assert(wext.idx == w.idx)
+ assert(wname.idx == w.idx)
+ assert(wdict.idx == w.idx)
+
+ w.dict = dict
+ wext.dict = dict
+
+ code := w.doObj(wext, obj)
+ w.flush()
+ wext.flush()
+
+ wname.qualifiedIdent(obj)
+ wname.code(code)
+ wname.flush()
+
+ wdict.objDict(obj, w.dict)
+ wdict.flush()
+
+ return w.idx
+}
+
+func (w *writer) doObj(wext *writer, obj types2.Object) codeObj {
+ if obj.Pkg() != w.p.curpkg {
+ return objStub
+ }
+
+ switch obj := obj.(type) {
+ default:
+ w.p.unexpected("object", obj)
+ panic("unreachable")
+
+ case *types2.Const:
+ w.pos(obj)
+ w.typ(obj.Type())
+ w.value(obj.Val())
+ return objConst
+
+ case *types2.Func:
+ decl, ok := w.p.funDecls[obj]
+ assert(ok)
+ sig := obj.Type().(*types2.Signature)
+
+ w.pos(obj)
+ w.typeParamNames(sig.TypeParams())
+ w.signature(sig)
+ w.pos(decl)
+ wext.funcExt(obj)
+ return objFunc
+
+ case *types2.TypeName:
+ decl, ok := w.p.typDecls[obj]
+ assert(ok)
+
+ if obj.IsAlias() {
+ w.pos(obj)
+ w.typ(obj.Type())
+ return objAlias
+ }
+
+ named := obj.Type().(*types2.Named)
+ assert(named.TypeArgs() == nil)
+
+ w.pos(obj)
+ w.typeParamNames(named.TypeParams())
+ wext.typeExt(obj)
+ w.typExpr(decl.Type)
+
+ w.len(named.NumMethods())
+ for i := 0; i < named.NumMethods(); i++ {
+ w.method(wext, named.Method(i))
+ }
+
+ return objType
+
+ case *types2.Var:
+ w.pos(obj)
+ w.typ(obj.Type())
+ wext.varExt(obj)
+ return objVar
+ }
+}
+
+// typExpr writes the type represented by the given expression.
+func (w *writer) typExpr(expr syntax.Expr) {
+ tv, ok := w.p.info.Types[expr]
+ assert(ok)
+ assert(tv.IsType())
+ w.typ(tv.Type)
+}
+
+// objDict writes the dictionary needed for reading the given object.
+func (w *writer) objDict(obj types2.Object, dict *writerDict) {
+ // TODO(mdempsky): Split objDict into multiple entries? reader.go
+ // doesn't care about the type parameter bounds, and reader2.go
+ // doesn't care about referenced functions.
+
+ w.dict = dict // TODO(mdempsky): This is a bit sketchy.
+
+ w.len(len(dict.implicits))
+
+ tparams := objTypeParams(obj)
+ ntparams := tparams.Len()
+ w.len(ntparams)
+ for i := 0; i < ntparams; i++ {
+ w.typ(tparams.At(i).Constraint())
+ }
+
+ nderived := len(dict.derived)
+ w.len(nderived)
+ for _, typ := range dict.derived {
+ w.reloc(relocType, typ.idx)
+ w.bool(typ.needed)
+ }
+
+ nfuncs := len(dict.funcs)
+ w.len(nfuncs)
+ for _, fn := range dict.funcs {
+ w.reloc(relocObj, fn.idx)
+ w.len(len(fn.explicits))
+ for _, targ := range fn.explicits {
+ w.typInfo(targ)
+ }
+ }
+
+ assert(len(dict.derived) == nderived)
+ assert(len(dict.funcs) == nfuncs)
+}
+
+func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
+ w.sync(syncTypeParamNames)
+
+ ntparams := tparams.Len()
+ for i := 0; i < ntparams; i++ {
+ tparam := tparams.At(i).Obj()
+ w.pos(tparam)
+ w.localIdent(tparam)
+ }
+}
+
+func (w *writer) method(wext *writer, meth *types2.Func) {
+ decl, ok := w.p.funDecls[meth]
+ assert(ok)
+ sig := meth.Type().(*types2.Signature)
+
+ w.sync(syncMethod)
+ w.pos(meth)
+ w.selector(meth)
+ w.typeParamNames(sig.RecvTypeParams())
+ w.param(sig.Recv())
+ w.signature(sig)
+
+ w.pos(decl) // XXX: Hack to workaround linker limitations.
+ wext.funcExt(meth)
+}
+
+// qualifiedIdent writes out the name of an object declared at package
+// scope. (For now, it's also used to refer to local defined types.)
+func (w *writer) qualifiedIdent(obj types2.Object) {
+ w.sync(syncSym)
+
+ name := obj.Name()
+ if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
+ decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
+ assert(ok)
+ if decl.gen != 0 {
+ // TODO(mdempsky): Find a better solution than embedding middle
+ // dot in the symbol name; this is terrible.
+ name = fmt.Sprintf("%s·%v", name, decl.gen)
+ }
+ }
+
+ w.pkg(obj.Pkg())
+ w.string(name)
+}
+
+// TODO(mdempsky): We should be able to omit pkg from both localIdent
+// and selector, because they should always be known from context.
+// However, past frustrations with this optimization in iexport make
+// me a little nervous to try it again.
+
+// localIdent writes the name of a locally declared object (i.e.,
+// objects that can only be accessed by name, within the context of a
+// particular function).
+func (w *writer) localIdent(obj types2.Object) {
+ assert(!isGlobal(obj))
+ w.sync(syncLocalIdent)
+ w.pkg(obj.Pkg())
+ w.string(obj.Name())
+}
+
+// selector writes the name of a field or method (i.e., objects that
+// can only be accessed using selector expressions).
+func (w *writer) selector(obj types2.Object) {
+ w.sync(syncSelector)
+ w.pkg(obj.Pkg())
+ w.string(obj.Name())
+}
+
+// @@@ Compiler extensions
+
+func (w *writer) funcExt(obj *types2.Func) {
+ decl, ok := w.p.funDecls[obj]
+ assert(ok)
+
+ // TODO(mdempsky): Extend these pragma validation flags to account
+ // for generics. E.g., linkname probably doesn't make sense at
+ // least.
+
+ pragma := asPragmaFlag(decl.Pragma)
+ if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {
+ w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")
+ }
+
+ if decl.Body != nil {
+ if pragma&ir.Noescape != 0 {
+ w.p.errorf(decl, "can only use //go:noescape with external func implementations")
+ }
+ } else {
+ if base.Flag.Complete || decl.Name.Value == "init" {
+ // Linknamed functions are allowed to have no body. Hopefully
+ // the linkname target has a body. See issue 23311.
+ if _, ok := w.p.linknames[obj]; !ok {
+ w.p.errorf(decl, "missing function body")
+ }
+ }
+ }
+
+ sig, block := obj.Type().(*types2.Signature), decl.Body
+ body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict)
+ assert(len(closureVars) == 0)
+
+ w.sync(syncFuncExt)
+ w.pragmaFlag(pragma)
+ w.linkname(obj)
+ w.bool(false) // stub extension
+ w.reloc(relocBody, body)
+ w.sync(syncEOF)
+}
+
+func (w *writer) typeExt(obj *types2.TypeName) {
+ decl, ok := w.p.typDecls[obj]
+ assert(ok)
+
+ w.sync(syncTypeExt)
+
+ w.pragmaFlag(asPragmaFlag(decl.Pragma))
+
+ // No LSym.SymIdx info yet.
+ w.int64(-1)
+ w.int64(-1)
+}
+
+func (w *writer) varExt(obj *types2.Var) {
+ w.sync(syncVarExt)
+ w.linkname(obj)
+}
+
+func (w *writer) linkname(obj types2.Object) {
+ w.sync(syncLinkname)
+ w.int64(-1)
+ w.string(w.p.linknames[obj])
+}
+
+func (w *writer) pragmaFlag(p ir.PragmaFlag) {
+ w.sync(syncPragma)
+ w.int(int(p))
+}
+
+// @@@ Function bodies
+
+func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx int, closureVars []posObj) {
+ w := pw.newWriter(relocBody, syncFuncBody)
+ w.dict = dict
+
+ w.funcargs(sig)
+ if w.bool(block != nil) {
+ w.stmts(block.List)
+ w.pos(block.Rbrace)
+ }
+
+ return w.flush(), w.closureVars
+}
+
+func (w *writer) funcargs(sig *types2.Signature) {
+ do := func(params *types2.Tuple, result bool) {
+ for i := 0; i < params.Len(); i++ {
+ w.funcarg(params.At(i), result)
+ }
+ }
+
+ if recv := sig.Recv(); recv != nil {
+ w.funcarg(recv, false)
+ }
+ do(sig.Params(), false)
+ do(sig.Results(), true)
+}
+
+func (w *writer) funcarg(param *types2.Var, result bool) {
+ if param.Name() != "" || result {
+ w.addLocal(param)
+ }
+}
+
+func (w *writer) addLocal(obj *types2.Var) {
+ w.sync(syncAddLocal)
+ idx := len(w.localsIdx)
+ if enableSync {
+ w.int(idx)
+ }
+ if w.localsIdx == nil {
+ w.localsIdx = make(map[*types2.Var]int)
+ }
+ w.localsIdx[obj] = idx
+}
+
+func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
+ w.sync(syncUseObjLocal)
+
+ if idx, ok := w.localsIdx[obj]; w.bool(ok) {
+ w.len(idx)
+ return
+ }
+
+ idx, ok := w.closureVarsIdx[obj]
+ if !ok {
+ if w.closureVarsIdx == nil {
+ w.closureVarsIdx = make(map[*types2.Var]int)
+ }
+ idx = len(w.closureVars)
+ w.closureVars = append(w.closureVars, posObj{pos, obj})
+ w.closureVarsIdx[obj] = idx
+ }
+ w.len(idx)
+}
+
+func (w *writer) openScope(pos syntax.Pos) {
+ w.sync(syncOpenScope)
+ w.pos(pos)
+}
+
+func (w *writer) closeScope(pos syntax.Pos) {
+ w.sync(syncCloseScope)
+ w.pos(pos)
+ w.closeAnotherScope()
+}
+
+func (w *writer) closeAnotherScope() {
+ w.sync(syncCloseAnotherScope)
+}
+
+// @@@ Statements
+
+func (w *writer) stmt(stmt syntax.Stmt) {
+ var stmts []syntax.Stmt
+ if stmt != nil {
+ stmts = []syntax.Stmt{stmt}
+ }
+ w.stmts(stmts)
+}
+
+func (w *writer) stmts(stmts []syntax.Stmt) {
+ w.sync(syncStmts)
+ for _, stmt := range stmts {
+ w.stmt1(stmt)
+ }
+ w.code(stmtEnd)
+ w.sync(syncStmtsEnd)
+}
+
+func (w *writer) stmt1(stmt syntax.Stmt) {
+ switch stmt := stmt.(type) {
+ default:
+ w.p.unexpected("statement", stmt)
+
+ case nil, *syntax.EmptyStmt:
+ return
+
+ case *syntax.AssignStmt:
+ switch {
+ case stmt.Rhs == nil:
+ w.code(stmtIncDec)
+ w.op(binOps[stmt.Op])
+ w.expr(stmt.Lhs)
+ w.pos(stmt)
+
+ case stmt.Op != 0 && stmt.Op != syntax.Def:
+ w.code(stmtAssignOp)
+ w.op(binOps[stmt.Op])
+ w.expr(stmt.Lhs)
+ w.pos(stmt)
+ w.expr(stmt.Rhs)
+
+ default:
+ w.code(stmtAssign)
+ w.pos(stmt)
+ w.exprList(stmt.Rhs)
+ w.assignList(stmt.Lhs)
+ }
+
+ case *syntax.BlockStmt:
+ w.code(stmtBlock)
+ w.blockStmt(stmt)
+
+ case *syntax.BranchStmt:
+ w.code(stmtBranch)
+ w.pos(stmt)
+ w.op(branchOps[stmt.Tok])
+ w.optLabel(stmt.Label)
+
+ case *syntax.CallStmt:
+ w.code(stmtCall)
+ w.pos(stmt)
+ w.op(callOps[stmt.Tok])
+ w.expr(stmt.Call)
+
+ case *syntax.DeclStmt:
+ for _, decl := range stmt.DeclList {
+ w.declStmt(decl)
+ }
+
+ case *syntax.ExprStmt:
+ w.code(stmtExpr)
+ w.expr(stmt.X)
+
+ case *syntax.ForStmt:
+ w.code(stmtFor)
+ w.forStmt(stmt)
+
+ case *syntax.IfStmt:
+ w.code(stmtIf)
+ w.ifStmt(stmt)
+
+ case *syntax.LabeledStmt:
+ w.code(stmtLabel)
+ w.pos(stmt)
+ w.label(stmt.Label)
+ w.stmt1(stmt.Stmt)
+
+ case *syntax.ReturnStmt:
+ w.code(stmtReturn)
+ w.pos(stmt)
+ w.exprList(stmt.Results)
+
+ case *syntax.SelectStmt:
+ w.code(stmtSelect)
+ w.selectStmt(stmt)
+
+ case *syntax.SendStmt:
+ w.code(stmtSend)
+ w.pos(stmt)
+ w.expr(stmt.Chan)
+ w.expr(stmt.Value)
+
+ case *syntax.SwitchStmt:
+ w.code(stmtSwitch)
+ w.switchStmt(stmt)
+ }
+}
+
+func (w *writer) assignList(expr syntax.Expr) {
+ exprs := unpackListExpr(expr)
+ w.len(len(exprs))
+
+ for _, expr := range exprs {
+ if name, ok := expr.(*syntax.Name); ok && name.Value != "_" {
+ if obj, ok := w.p.info.Defs[name]; ok {
+ obj := obj.(*types2.Var)
+
+ w.bool(true)
+ w.pos(obj)
+ w.localIdent(obj)
+ w.typ(obj.Type())
+
+ // TODO(mdempsky): Minimize locals index size by deferring
+ // this until the variables actually come into scope.
+ w.addLocal(obj)
+ continue
+ }
+ }
+
+ w.bool(false)
+ w.expr(expr)
+ }
+}
+
+func (w *writer) declStmt(decl syntax.Decl) {
+ switch decl := decl.(type) {
+ default:
+ w.p.unexpected("declaration", decl)
+
+ case *syntax.ConstDecl:
+
+ case *syntax.TypeDecl:
+ // Quirk: The legacy inliner doesn't support inlining functions
+ // with type declarations. Unified IR doesn't have any need to
+ // write out type declarations explicitly (they're always looked
+ // up via global index tables instead), so we just write out a
+ // marker so the reader knows to synthesize a fake declaration to
+ // prevent inlining.
+ if quirksMode() {
+ w.code(stmtTypeDeclHack)
+ }
+
+ case *syntax.VarDecl:
+ values := unpackListExpr(decl.Values)
+
+ // Quirk: When N variables are declared with N initialization
+ // values, we need to decompose that into N interleaved
+ // declarations+initializations, because it leads to different
+ // (albeit semantically equivalent) code generation.
+ if quirksMode() && len(decl.NameList) == len(values) {
+ for i, name := range decl.NameList {
+ w.code(stmtAssign)
+ w.pos(decl)
+ w.exprList(values[i])
+ w.assignList(name)
+ }
+ break
+ }
+
+ w.code(stmtAssign)
+ w.pos(decl)
+ w.exprList(decl.Values)
+ w.assignList(namesAsExpr(decl.NameList))
+ }
+}
+
+func (w *writer) blockStmt(stmt *syntax.BlockStmt) {
+ w.sync(syncBlockStmt)
+ w.openScope(stmt.Pos())
+ w.stmts(stmt.List)
+ w.closeScope(stmt.Rbrace)
+}
+
+func (w *writer) forStmt(stmt *syntax.ForStmt) {
+ w.sync(syncForStmt)
+ w.openScope(stmt.Pos())
+
+ if rang, ok := stmt.Init.(*syntax.RangeClause); w.bool(ok) {
+ w.pos(rang)
+ w.expr(rang.X)
+ w.assignList(rang.Lhs)
+ } else {
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+ w.expr(stmt.Cond)
+ w.stmt(stmt.Post)
+ }
+
+ w.blockStmt(stmt.Body)
+ w.closeAnotherScope()
+}
+
+func (w *writer) ifStmt(stmt *syntax.IfStmt) {
+ w.sync(syncIfStmt)
+ w.openScope(stmt.Pos())
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+ w.expr(stmt.Cond)
+ w.blockStmt(stmt.Then)
+ w.stmt(stmt.Else)
+ w.closeAnotherScope()
+}
+
+func (w *writer) selectStmt(stmt *syntax.SelectStmt) {
+ w.sync(syncSelectStmt)
+
+ w.pos(stmt)
+ w.len(len(stmt.Body))
+ for i, clause := range stmt.Body {
+ if i > 0 {
+ w.closeScope(clause.Pos())
+ }
+ w.openScope(clause.Pos())
+
+ w.pos(clause)
+ w.stmt(clause.Comm)
+ w.stmts(clause.Body)
+ }
+ if len(stmt.Body) > 0 {
+ w.closeScope(stmt.Rbrace)
+ }
+}
+
+func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {
+ w.sync(syncSwitchStmt)
+
+ w.openScope(stmt.Pos())
+ w.pos(stmt)
+ w.stmt(stmt.Init)
+
+ if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.bool(ok) {
+ w.pos(guard)
+ if tag := guard.Lhs; w.bool(tag != nil) {
+ w.pos(tag)
+ w.string(tag.Value)
+ }
+ w.expr(guard.X)
+ } else {
+ w.expr(stmt.Tag)
+ }
+
+ w.len(len(stmt.Body))
+ for i, clause := range stmt.Body {
+ if i > 0 {
+ w.closeScope(clause.Pos())
+ }
+ w.openScope(clause.Pos())
+
+ w.pos(clause)
+ w.exprList(clause.Cases)
+
+ if obj, ok := w.p.info.Implicits[clause]; ok {
+ // TODO(mdempsky): These pos details are quirkish, but also
+ // necessary so the variable's position is correct for DWARF
+ // scope assignment later. It would probably be better for us to
+ // instead just set the variable's DWARF scoping info earlier so
+ // we can give it the correct position information.
+ pos := clause.Pos()
+ if typs := unpackListExpr(clause.Cases); len(typs) != 0 {
+ pos = typeExprEndPos(typs[len(typs)-1])
+ }
+ w.pos(pos)
+
+ obj := obj.(*types2.Var)
+ w.typ(obj.Type())
+ w.addLocal(obj)
+ }
+
+ w.stmts(clause.Body)
+ }
+ if len(stmt.Body) > 0 {
+ w.closeScope(stmt.Rbrace)
+ }
+
+ w.closeScope(stmt.Rbrace)
+}
+
+func (w *writer) label(label *syntax.Name) {
+ w.sync(syncLabel)
+
+ // TODO(mdempsky): Replace label strings with dense indices.
+ w.string(label.Value)
+}
+
+func (w *writer) optLabel(label *syntax.Name) {
+ w.sync(syncOptLabel)
+ if w.bool(label != nil) {
+ w.label(label)
+ }
+}
+
+// @@@ Expressions
+
+func (w *writer) expr(expr syntax.Expr) {
+ expr = unparen(expr) // skip parens; unneeded after typecheck
+
+ obj, inst := lookupObj(w.p.info, expr)
+ targs := inst.TypeArgs
+
+ if tv, ok := w.p.info.Types[expr]; ok {
+ // TODO(mdempsky): Be more judicious about which types are marked as "needed".
+ if inst.Type != nil {
+ w.needType(inst.Type)
+ } else {
+ w.needType(tv.Type)
+ }
+
+ if tv.IsType() {
+ w.code(exprType)
+ w.typ(tv.Type)
+ return
+ }
+
+ if tv.Value != nil {
+ pos := expr.Pos()
+ if quirksMode() {
+ if obj != nil {
+ // Quirk: IR (and thus iexport) doesn't track position
+ // information for uses of declared objects.
+ pos = syntax.Pos{}
+ } else if tv.Value.Kind() == constant.String {
+ // Quirk: noder.sum picks a particular position for certain
+ // string concatenations.
+ pos = sumPos(expr)
+ }
+ }
+
+ w.code(exprConst)
+ w.pos(pos)
+ w.typ(tv.Type)
+ w.value(tv.Value)
+
+ // TODO(mdempsky): These details are only important for backend
+ // diagnostics. Explore writing them out separately.
+ w.op(constExprOp(expr))
+ w.string(syntax.String(expr))
+ return
+ }
+ }
+
+ if obj != nil {
+ if isGlobal(obj) {
+ w.code(exprName)
+ w.obj(obj, targs)
+ return
+ }
+
+ obj := obj.(*types2.Var)
+ assert(targs.Len() == 0)
+
+ w.code(exprLocal)
+ w.useLocal(expr.Pos(), obj)
+ return
+ }
+
+ switch expr := expr.(type) {
+ default:
+ w.p.unexpected("expression", expr)
+
+ case nil: // absent slice index, for condition, or switch tag
+ w.code(exprNone)
+
+ case *syntax.Name:
+ assert(expr.Value == "_")
+ w.code(exprBlank)
+
+ case *syntax.CompositeLit:
+ w.code(exprCompLit)
+ w.compLit(expr)
+
+ case *syntax.FuncLit:
+ w.code(exprFuncLit)
+ w.funcLit(expr)
+
+ case *syntax.SelectorExpr:
+ sel, ok := w.p.info.Selections[expr]
+ assert(ok)
+
+ w.code(exprSelector)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.selector(sel.Obj())
+
+ case *syntax.IndexExpr:
+ tv, ok := w.p.info.Types[expr.Index]
+ assert(ok && tv.IsValue())
+
+ w.code(exprIndex)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.expr(expr.Index)
+
+ case *syntax.SliceExpr:
+ w.code(exprSlice)
+ w.expr(expr.X)
+ w.pos(expr)
+ for _, n := range &expr.Index {
+ w.expr(n)
+ }
+
+ case *syntax.AssertExpr:
+ w.code(exprAssert)
+ w.expr(expr.X)
+ w.pos(expr)
+ w.expr(expr.Type)
+
+ case *syntax.Operation:
+ if expr.Y == nil {
+ w.code(exprUnaryOp)
+ w.op(unOps[expr.Op])
+ w.pos(expr)
+ w.expr(expr.X)
+ break
+ }
+
+ w.code(exprBinaryOp)
+ w.op(binOps[expr.Op])
+ w.expr(expr.X)
+ w.pos(expr)
+ w.expr(expr.Y)
+
+ case *syntax.CallExpr:
+ tv, ok := w.p.info.Types[expr.Fun]
+ assert(ok)
+ if tv.IsType() {
+ assert(len(expr.ArgList) == 1)
+ assert(!expr.HasDots)
+
+ w.code(exprConvert)
+ w.typ(tv.Type)
+ w.pos(expr)
+ w.expr(expr.ArgList[0])
+ break
+ }
+
+ writeFunExpr := func() {
+ if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok {
+ if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
+ w.expr(selector.X)
+ w.bool(true) // method call
+ w.pos(selector)
+ w.selector(sel.Obj())
+ return
+ }
+ }
+
+ w.expr(expr.Fun)
+ w.bool(false) // not a method call (i.e., normal function call)
+ }
+
+ w.code(exprCall)
+ writeFunExpr()
+ w.pos(expr)
+ w.exprs(expr.ArgList)
+ w.bool(expr.HasDots)
+ }
+}
+
+func (w *writer) compLit(lit *syntax.CompositeLit) {
+ tv, ok := w.p.info.Types[lit]
+ assert(ok)
+
+ w.sync(syncCompLit)
+ w.pos(lit)
+ w.typ(tv.Type)
+
+ typ := tv.Type
+ if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
+ typ = ptr.Elem()
+ }
+ str, isStruct := typ.Underlying().(*types2.Struct)
+
+ w.len(len(lit.ElemList))
+ for i, elem := range lit.ElemList {
+ if isStruct {
+ if kv, ok := elem.(*syntax.KeyValueExpr); ok {
+ // use position of expr.Key rather than of elem (which has position of ':')
+ w.pos(kv.Key)
+ w.len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name)))
+ elem = kv.Value
+ } else {
+ w.pos(elem)
+ w.len(i)
+ }
+ } else {
+ if kv, ok := elem.(*syntax.KeyValueExpr); w.bool(ok) {
+ // use position of expr.Key rather than of elem (which has position of ':')
+ w.pos(kv.Key)
+ w.expr(kv.Key)
+ elem = kv.Value
+ }
+ }
+ w.pos(elem)
+ w.expr(elem)
+ }
+}
+
+func (w *writer) funcLit(expr *syntax.FuncLit) {
+ tv, ok := w.p.info.Types[expr]
+ assert(ok)
+ sig := tv.Type.(*types2.Signature)
+
+ body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict)
+
+ w.sync(syncFuncLit)
+ w.pos(expr)
+ w.pos(expr.Type) // for QuirksMode
+ w.signature(sig)
+
+ w.len(len(closureVars))
+ for _, cv := range closureVars {
+ w.pos(cv.pos)
+ if quirksMode() {
+ cv.pos = expr.Body.Rbrace
+ }
+ w.useLocal(cv.pos, cv.obj)
+ }
+
+ w.reloc(relocBody, body)
+}
+
+type posObj struct {
+ pos syntax.Pos
+ obj *types2.Var
+}
+
+func (w *writer) exprList(expr syntax.Expr) {
+ w.sync(syncExprList)
+ w.exprs(unpackListExpr(expr))
+}
+
+func (w *writer) exprs(exprs []syntax.Expr) {
+ if len(exprs) == 0 {
+ assert(exprs == nil)
+ }
+
+ w.sync(syncExprs)
+ w.len(len(exprs))
+ for _, expr := range exprs {
+ w.expr(expr)
+ }
+}
+
+func (w *writer) op(op ir.Op) {
+ // TODO(mdempsky): Remove in favor of explicit codes? Would make
+ // export data more stable against internal refactorings, but low
+ // priority at the moment.
+ assert(op != 0)
+ w.sync(syncOp)
+ w.len(int(op))
+}
+
+func (w *writer) needType(typ types2.Type) {
+ // Decompose tuple into component element types.
+ if typ, ok := typ.(*types2.Tuple); ok {
+ for i := 0; i < typ.Len(); i++ {
+ w.needType(typ.At(i).Type())
+ }
+ return
+ }
+
+ if info := w.p.typIdx(typ, w.dict); info.derived {
+ w.dict.derived[info.idx].needed = true
+ }
+}
+
+// @@@ Package initialization
+
+// Caution: This code is still clumsy, because toolstash -cmp is
+// particularly sensitive to it.
+
+type typeDeclGen struct {
+ *syntax.TypeDecl
+ gen int
+
+ // Implicit type parameters in scope at this type declaration.
+ implicits []*types2.TypeName
+}
+
+type fileImports struct {
+ importedEmbed, importedUnsafe bool
+}
+
+type declCollector struct {
+ pw *pkgWriter
+ typegen *int
+ file *fileImports
+ withinFunc bool
+ implicits []*types2.TypeName
+}
+
+func (c *declCollector) withTParams(obj types2.Object) *declCollector {
+ tparams := objTypeParams(obj)
+ n := tparams.Len()
+ if n == 0 {
+ return c
+ }
+
+ copy := *c
+ copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
+ for i := 0; i < n; i++ {
+ copy.implicits = append(copy.implicits, tparams.At(i).Obj())
+ }
+ return ©
+}
+
+func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
+ pw := c.pw
+
+ switch n := n.(type) {
+ case *syntax.File:
+ pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
+
+ case *syntax.ImportDecl:
+ pw.checkPragmas(n.Pragma, 0, false)
+
+ switch pkgNameOf(pw.info, n).Imported().Path() {
+ case "embed":
+ c.file.importedEmbed = true
+ case "unsafe":
+ c.file.importedUnsafe = true
+ }
+
+ case *syntax.ConstDecl:
+ pw.checkPragmas(n.Pragma, 0, false)
+
+ case *syntax.FuncDecl:
+ pw.checkPragmas(n.Pragma, funcPragmas, false)
+
+ obj := pw.info.Defs[n.Name].(*types2.Func)
+ pw.funDecls[obj] = n
+
+ return c.withTParams(obj)
+
+ case *syntax.TypeDecl:
+ obj := pw.info.Defs[n.Name].(*types2.TypeName)
+ d := typeDeclGen{TypeDecl: n, implicits: c.implicits}
+
+ if n.Alias {
+ pw.checkPragmas(n.Pragma, 0, false)
+ } else {
+ pw.checkPragmas(n.Pragma, typePragmas, false)
+
+ // Assign a unique ID to function-scoped defined types.
+ if c.withinFunc {
+ *c.typegen++
+ d.gen = *c.typegen
+ }
+ }
+
+ pw.typDecls[obj] = d
+
+ // TODO(mdempsky): Omit? Not strictly necessary; only matters for
+ // type declarations within function literals within parameterized
+ // type declarations, but types2 the function literals will be
+ // constant folded away.
+ return c.withTParams(obj)
+
+ case *syntax.VarDecl:
+ pw.checkPragmas(n.Pragma, 0, true)
+
+ if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 {
+ if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil {
+ pw.errorf(p.Embeds[0].Pos, "%s", err)
+ }
+ }
+
+ // Workaround for #46208. For variable declarations that
+ // declare multiple variables and have an explicit type
+ // expression, the type expression is evaluated multiple
+ // times. This affects toolstash -cmp, because iexport is
+ // sensitive to *types.Type pointer identity.
+ if quirksMode() && n.Type != nil {
+ tv, ok := pw.info.Types[n.Type]
+ assert(ok)
+ assert(tv.IsType())
+ for _, name := range n.NameList {
+ obj := pw.info.Defs[name].(*types2.Var)
+ pw.dups.add(obj.Type(), tv.Type)
+ }
+ }
+
+ case *syntax.BlockStmt:
+ if !c.withinFunc {
+ copy := *c
+ copy.withinFunc = true
+ return ©
+ }
+ }
+
+ return c
+}
+
+func (pw *pkgWriter) collectDecls(noders []*noder) {
+ var typegen int
+ for _, p := range noders {
+ var file fileImports
+
+ syntax.Walk(p.file, &declCollector{
+ pw: pw,
+ typegen: &typegen,
+ file: &file,
+ })
+
+ pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...)
+
+ for _, l := range p.linknames {
+ if !file.importedUnsafe {
+ pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
+ continue
+ }
+
+ switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
+ case *types2.Func, *types2.Var:
+ if _, ok := pw.linknames[obj]; !ok {
+ pw.linknames[obj] = l.remote
+ } else {
+ pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local)
+ }
+
+ default:
+ // TODO(mdempsky): Enable after #42938 is fixed.
+ if false {
+ pw.errorf(l.pos, "//go:linkname must refer to declared function or variable")
+ }
+ }
+ }
+ }
+}
+
+func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) {
+ if p == nil {
+ return
+ }
+ pragma := p.(*pragmas)
+
+ for _, pos := range pragma.Pos {
+ if pos.Flag&^allowed != 0 {
+ pw.errorf(pos.Pos, "misplaced compiler directive")
+ }
+ }
+
+ if !embedOK {
+ for _, e := range pragma.Embeds {
+ pw.errorf(e.Pos, "misplaced go:embed directive")
+ }
+ }
+}
+
+func (w *writer) pkgInit(noders []*noder) {
+ if quirksMode() {
+ posBases := posBasesOf(noders)
+ w.len(len(posBases))
+ for _, posBase := range posBases {
+ w.posBase(posBase)
+ }
+
+ objs := importedObjsOf(w.p.curpkg, w.p.info, noders)
+ w.len(len(objs))
+ for _, obj := range objs {
+ w.qualifiedIdent(obj)
+ }
+ }
+
+ w.len(len(w.p.cgoPragmas))
+ for _, cgoPragma := range w.p.cgoPragmas {
+ w.strings(cgoPragma)
+ }
+
+ w.sync(syncDecls)
+ for _, p := range noders {
+ for _, decl := range p.file.DeclList {
+ w.pkgDecl(decl)
+ }
+ }
+ w.code(declEnd)
+
+ w.sync(syncEOF)
+}
+
+func (w *writer) pkgDecl(decl syntax.Decl) {
+ switch decl := decl.(type) {
+ default:
+ w.p.unexpected("declaration", decl)
+
+ case *syntax.ImportDecl:
+
+ case *syntax.ConstDecl:
+ w.code(declOther)
+ w.pkgObjs(decl.NameList...)
+
+ case *syntax.FuncDecl:
+ if decl.Name.Value == "_" {
+ break // skip blank functions
+ }
+
+ obj := w.p.info.Defs[decl.Name].(*types2.Func)
+ sig := obj.Type().(*types2.Signature)
+
+ if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
+ break // skip generic functions
+ }
+
+ if recv := sig.Recv(); recv != nil {
+ w.code(declMethod)
+ w.typ(recvBase(recv))
+ w.selector(obj)
+ break
+ }
+
+ w.code(declFunc)
+ w.pkgObjs(decl.Name)
+
+ case *syntax.TypeDecl:
+ if len(decl.TParamList) != 0 {
+ break // skip generic type decls
+ }
+
+ if decl.Name.Value == "_" {
+ break // skip blank type decls
+ }
+
+ name := w.p.info.Defs[decl.Name].(*types2.TypeName)
+ // Skip type declarations for interfaces that are only usable as
+ // type parameter bounds.
+ if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
+ break
+ }
+
+ // Skip aliases to uninstantiated generic types.
+ // TODO(mdempsky): Revisit after #46477 is resolved.
+ if name.IsAlias() {
+ named, ok := name.Type().(*types2.Named)
+ if ok && named.TypeParams().Len() != 0 && named.TypeArgs().Len() == 0 {
+ break
+ }
+ }
+
+ w.code(declOther)
+ w.pkgObjs(decl.Name)
+
+ case *syntax.VarDecl:
+ w.code(declVar)
+ w.pos(decl)
+ w.pkgObjs(decl.NameList...)
+ w.exprList(decl.Values)
+
+ var embeds []pragmaEmbed
+ if p, ok := decl.Pragma.(*pragmas); ok {
+ embeds = p.Embeds
+ }
+ w.len(len(embeds))
+ for _, embed := range embeds {
+ w.pos(embed.Pos)
+ w.strings(embed.Patterns)
+ }
+ }
+}
+
+func (w *writer) pkgObjs(names ...*syntax.Name) {
+ w.sync(syncDeclNames)
+ w.len(len(names))
+
+ for _, name := range names {
+ obj, ok := w.p.info.Defs[name]
+ assert(ok)
+
+ w.sync(syncDeclName)
+ w.obj(obj, nil)
+ }
+}
+
+// @@@ Helpers
+
+// isDefinedType reports whether obj is a defined type.
+func isDefinedType(obj types2.Object) bool {
+ if obj, ok := obj.(*types2.TypeName); ok {
+ return !obj.IsAlias()
+ }
+ return false
+}
+
+// isGlobal reports whether obj was declared at package scope.
+//
+// Caveat: blank objects are not declared.
+func isGlobal(obj types2.Object) bool {
+ return obj.Parent() == obj.Pkg().Scope()
+}
+
+// lookupObj returns the object that expr refers to, if any. If expr
+// is an explicit instantiation of a generic object, then the instance
+// object is returned as well.
+func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
+ if index, ok := expr.(*syntax.IndexExpr); ok {
+ args := unpackListExpr(index.Index)
+ if len(args) == 1 {
+ tv, ok := info.Types[args[0]]
+ assert(ok)
+ if tv.IsValue() {
+ return // normal index expression
+ }
+ }
+
+ expr = index.X
+ }
+
+ // Strip package qualifier, if present.
+ if sel, ok := expr.(*syntax.SelectorExpr); ok {
+ if !isPkgQual(info, sel) {
+ return // normal selector expression
+ }
+ expr = sel.Sel
+ }
+
+ if name, ok := expr.(*syntax.Name); ok {
+ obj = info.Uses[name]
+ inst = info.Instances[name]
+ }
+ return
+}
+
+// isPkgQual reports whether the given selector expression is a
+// package-qualified identifier.
+func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool {
+ if name, ok := sel.X.(*syntax.Name); ok {
+ _, isPkgName := info.Uses[name].(*types2.PkgName)
+ return isPkgName
+ }
+ return false
+}
+
+// recvBase returns the base type for the given receiver parameter.
+func recvBase(recv *types2.Var) *types2.Named {
+ typ := recv.Type()
+ if ptr, ok := typ.(*types2.Pointer); ok {
+ typ = ptr.Elem()
+ }
+ return typ.(*types2.Named)
+}
+
+// namesAsExpr returns a list of names as a syntax.Expr.
+func namesAsExpr(names []*syntax.Name) syntax.Expr {
+ if len(names) == 1 {
+ return names[0]
+ }
+
+ exprs := make([]syntax.Expr, len(names))
+ for i, name := range names {
+ exprs[i] = name
+ }
+ return &syntax.ListExpr{ElemList: exprs}
+}
+
+// fieldIndex returns the index of the struct field named by key.
+func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
+ field := info.Uses[key].(*types2.Var)
+
+ for i := 0; i < str.NumFields(); i++ {
+ if str.Field(i) == field {
+ return i
+ }
+ }
+
+ panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str))
+}
+
+// objTypeParams returns the type parameters on the given object.
+func objTypeParams(obj types2.Object) *types2.TypeParamList {
+ switch obj := obj.(type) {
+ case *types2.Func:
+ sig := obj.Type().(*types2.Signature)
+ if sig.Recv() != nil {
+ return sig.RecvTypeParams()
+ }
+ return sig.TypeParams()
+ case *types2.TypeName:
+ if !obj.IsAlias() {
+ return obj.Type().(*types2.Named).TypeParams()
+ }
+ }
+ return nil
+}
+
+func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag {
+ if p == nil {
+ return 0
+ }
+ return p.(*pragmas).Flag
+}
diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go
index 7cad2622146d0e580cb257348ec234ecb05ba713..40f14082600c3d1515c3d1e9350d961f4dbcb77e 100644
--- a/src/cmd/compile/internal/pkginit/init.go
+++ b/src/cmd/compile/internal/pkginit/init.go
@@ -6,14 +6,62 @@ package pkginit
import (
"cmd/compile/internal/base"
- "cmd/compile/internal/deadcode"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
+ "cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
+ "cmd/internal/src"
)
+// MakeInit creates a synthetic init function to handle any
+// package-scope initialization statements.
+//
+// TODO(mdempsky): Move into noder, so that the types2-based frontends
+// can use Info.InitOrder instead.
+func MakeInit() {
+ nf := initOrder(typecheck.Target.Decls)
+ if len(nf) == 0 {
+ return
+ }
+
+ // Make a function that contains all the initialization statements.
+ base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
+ initializers := typecheck.Lookup("init")
+ fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
+ for _, dcl := range typecheck.InitTodoFunc.Dcl {
+ dcl.Curfn = fn
+ }
+ fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
+ typecheck.InitTodoFunc.Dcl = nil
+
+ // Suppress useless "can inline" diagnostics.
+ // Init functions are only called dynamically.
+ fn.SetInlinabilityChecked(true)
+
+ fn.Body = nf
+ typecheck.FinishFuncBody()
+
+ typecheck.Func(fn)
+ ir.WithFunc(fn, func() {
+ typecheck.Stmts(nf)
+ })
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+
+ // Prepend to Inits, so it runs first, before any user-declared init
+ // functions.
+ typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...)
+
+ if typecheck.InitTodoFunc.Dcl != nil {
+ // We only generate temps using InitTodoFunc if there
+ // are package-scope initialization statements, so
+ // something's weird if we get here.
+ base.Fatalf("InitTodoFunc still has declarations")
+ }
+ typecheck.InitTodoFunc = nil
+}
+
// Task makes and returns an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
@@ -21,8 +69,6 @@ import (
// 2) Initialize all the variables that have initializers.
// 3) Run any init functions.
func Task() *ir.Name {
- nf := initOrder(typecheck.Target.Decls)
-
var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
@@ -38,39 +84,28 @@ func Task() *ir.Name {
deps = append(deps, n.(*ir.Name).Linksym())
}
- // Make a function that contains all the initialization statements.
- if len(nf) > 0 {
- base.Pos = nf[0].Pos() // prolog/epilog gets line number of first init stmt
- initializers := typecheck.Lookup("init")
- fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
- for _, dcl := range typecheck.InitTodoFunc.Dcl {
- dcl.Curfn = fn
- }
- fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
- typecheck.InitTodoFunc.Dcl = nil
-
- fn.Body = nf
- typecheck.FinishFuncBody()
-
- typecheck.Func(fn)
- ir.CurFunc = fn
- typecheck.Stmts(nf)
- ir.CurFunc = nil
- typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
- fns = append(fns, fn.Linksym())
- }
- if typecheck.InitTodoFunc.Dcl != nil {
- // We only generate temps using InitTodoFunc if there
- // are package-scope initialization statements, so
- // something's weird if we get here.
- base.Fatalf("InitTodoFunc still has declarations")
- }
- typecheck.InitTodoFunc = nil
-
// Record user init functions.
for _, fn := range typecheck.Target.Inits {
- // Must happen after initOrder; see #43444.
- deadcode.Func(fn)
+ if fn.Sym().Name == "init" {
+ // Synthetic init function for initialization of package-scope
+ // variables. We can use staticinit to optimize away static
+ // assignments.
+ s := staticinit.Schedule{
+ Plans: make(map[ir.Node]*staticinit.Plan),
+ Temps: make(map[ir.Node]*ir.Name),
+ }
+ for _, n := range fn.Body {
+ s.StaticInit(n)
+ }
+ fn.Body = s.Out
+ ir.WithFunc(fn, func() {
+ typecheck.Stmts(fn.Body)
+ })
+
+ if len(fn.Body) == 0 {
+ fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
+ }
+ }
// Skip init functions with empty bodies.
if len(fn.Body) == 1 {
diff --git a/src/cmd/compile/internal/pkginit/initorder.go b/src/cmd/compile/internal/pkginit/initorder.go
index 97d69629fbae9e5167ec68abe39c9264a8c979cc..a50975343fbced39fdd911c769575e0ceff8edb1 100644
--- a/src/cmd/compile/internal/pkginit/initorder.go
+++ b/src/cmd/compile/internal/pkginit/initorder.go
@@ -11,7 +11,6 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
- "cmd/compile/internal/staticinit"
)
// Package initialization
@@ -78,10 +77,7 @@ type InitOrder struct {
// corresponding list of statements to include in the init() function
// body.
func initOrder(l []ir.Node) []ir.Node {
- s := staticinit.Schedule{
- Plans: make(map[ir.Node]*staticinit.Plan),
- Temps: make(map[ir.Node]*ir.Name),
- }
+ var res ir.Nodes
o := InitOrder{
blocking: make(map[ir.Node][]ir.Node),
order: make(map[ir.Node]int),
@@ -92,7 +88,7 @@ func initOrder(l []ir.Node) []ir.Node {
switch n.Op() {
case ir.OAS, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
o.processAssign(n)
- o.flushReady(s.StaticInit)
+ o.flushReady(func(n ir.Node) { res.Append(n) })
case ir.ODCLCONST, ir.ODCLFUNC, ir.ODCLTYPE:
// nop
default:
@@ -125,7 +121,7 @@ func initOrder(l []ir.Node) []ir.Node {
base.Fatalf("expected empty map: %v", o.blocking)
}
- return s.Out
+ return res
}
func (o *InitOrder) processAssign(n ir.Node) {
@@ -304,7 +300,7 @@ func (d *initDeps) visit(n ir.Node) {
n := n.(*ir.ClosureExpr)
d.inspectList(n.Func.Body)
- case ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
+ case ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
d.foundDep(ir.MethodExprName(n))
}
}
diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go
index 590290fa371008db507ade8df55c54b4bc160f69..20fd8cec54f397fb9d05f490798b0c4f3965ae17 100644
--- a/src/cmd/compile/internal/ppc64/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -16,13 +16,14 @@ func Init(arch *ssagen.ArchInfo) {
arch.LinkArch = &ppc64.Linkppc64le
}
arch.REGSP = ppc64.REGSP
- arch.MAXWIDTH = 1 << 60
+ arch.MAXWIDTH = 1 << 50
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnopdefer
arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock
+ arch.LoadRegResult = loadRegResult
+ arch.SpillArgReg = spillArgReg
}
diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
index c76962cfb811334d577ffc49b427533f0a8517b8..3ae6422bf9e26b0a1fb08e1f94f3a17a8965d3f1 100644
--- a/src/cmd/compile/internal/ppc64/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -53,30 +53,3 @@ func ginsnop(pp *objw.Progs) *obj.Prog {
p.To.Reg = ppc64.REG_R0
return p
}
-
-func ginsnopdefer(pp *objw.Progs) *obj.Prog {
- // On PPC64 two nops are required in the defer case.
- //
- // (see gc/cgen.go, gc/plive.go -- copy of comment below)
- //
- // On ppc64, when compiling Go into position
- // independent code on ppc64le we insert an
- // instruction to reload the TOC pointer from the
- // stack as well. See the long comment near
- // jmpdefer in runtime/asm_ppc64.s for why.
- // If the MOVD is not needed, insert a hardware NOP
- // so that the same number of instructions are used
- // on ppc64 in both shared and non-shared modes.
-
- ginsnop(pp)
- if base.Ctxt.Flag_shared {
- p := pp.Prog(ppc64.AMOVD)
- p.From.Type = obj.TYPE_MEM
- p.From.Offset = 24
- p.From.Reg = ppc64.REGSP
- p.To.Type = obj.TYPE_REG
- p.To.Reg = ppc64.REG_R2
- return p
- }
- return ginsnop(pp)
-}
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index 11226f65a0f01639359fe2992e2f266b2301be20..98316c16fab7f9b6756fa7c0cadd6f1e891c3af3 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -8,6 +8,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/logopt"
+ "cmd/compile/internal/objw"
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/types"
@@ -502,6 +503,20 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Reg = v.Args[0].Reg()
ssagen.AddrAuto(&p.To, v)
+ case ssa.OpArgIntReg, ssa.OpArgFloatReg:
+ // The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
+ // The loop only runs once.
+ for _, a := range v.Block.Func.RegArgs {
+ // Pass the spill/unspill information along to the assembler, offset by size of
+ // the saved LR slot.
+ addr := ssagen.SpillSlotAddr(a, ppc64.REGSP, base.Ctxt.FixedFrameSize())
+ s.FuncInfo().AddSpill(
+ obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
+ }
+ v.Block.Func.RegArgs = nil
+
+ ssagen.CheckArgReg(v)
+
case ssa.OpPPC64DIVD:
// For now,
//
@@ -886,6 +901,13 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
+ case ssa.OpPPC64DCBT:
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_MEM
+ p.From.Reg = v.Args[0].Reg()
+ p.To.Type = obj.TYPE_CONST
+ p.To.Offset = v.AuxInt
+
case ssa.OpPPC64MOVWstorezero, ssa.OpPPC64MOVHstorezero, ssa.OpPPC64MOVBstorezero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
@@ -1829,6 +1851,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.OpPPC64CALLstatic:
s.Call(v)
+ case ssa.OpPPC64CALLtail:
+ s.TailCall(v)
+
case ssa.OpPPC64CALLclosure, ssa.OpPPC64CALLinter:
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_REG
@@ -1980,14 +2005,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockPPC64EQ, ssa.BlockPPC64NE,
ssa.BlockPPC64LT, ssa.BlockPPC64GE,
@@ -2027,3 +2047,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
b.Fatalf("branch not implemented: %s", b.LongString())
}
}
+
+func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
+ p := s.Prog(loadByType(t))
+ p.From.Type = obj.TYPE_MEM
+ p.From.Name = obj.NAME_AUTO
+ p.From.Sym = n.Linksym()
+ p.From.Offset = n.FrameOffset() + off
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = reg
+ return p
+}
+
+func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
+ p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
+ p.To.Name = obj.NAME_PARAM
+ p.To.Sym = n.Linksym()
+ p.Pos = p.Pos.WithNotStmt()
+ return p
+}
diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go
index 0707e0b61caf4861033d424acc525d028a2bf484..d000618bd64f576153f24335ddfa31a30dabfb21 100644
--- a/src/cmd/compile/internal/reflectdata/alg.go
+++ b/src/cmd/compile/internal/reflectdata/alg.go
@@ -48,12 +48,12 @@ func eqCanPanic(t *types.Type) bool {
func AlgType(t *types.Type) types.AlgKind {
a, _ := types.AlgType(t)
if a == types.AMEM {
- if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Width {
+ if t.Alignment() < int64(base.Ctxt.Arch.Alignment) && t.Alignment() < t.Size() {
// For example, we can't treat [2]int16 as an int32 if int32s require
// 4-byte alignment. See issue 46283.
return a
}
- switch t.Width {
+ switch t.Size() {
case 0:
return types.AMEM0
case 1:
@@ -110,7 +110,7 @@ func genhash(t *types.Type) *obj.LSym {
// For other sizes of plain memory, we build a closure
// that calls memhash_varlen. The size of the memory is
// encoded in the first slot of the closure.
- closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Width))
+ closure := TypeLinksymLookup(fmt.Sprintf(".hashfunc%d", t.Size()))
if len(closure.P) > 0 { // already generated
return closure
}
@@ -119,7 +119,7 @@ func genhash(t *types.Type) *obj.LSym {
}
ot := 0
ot = objw.SymPtr(closure, ot, memhashvarlen, 0)
- ot = objw.Uintptr(closure, ot, uint64(t.Width)) // size encoded in closure
+ ot = objw.Uintptr(closure, ot, uint64(t.Size())) // size encoded in closure
objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
return closure
case types.ASPECIAL:
@@ -354,7 +354,7 @@ func geneq(t *types.Type) *obj.LSym {
case types.AMEM:
// make equality closure. The size of the type
// is encoded in the closure.
- closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Width))
+ closure := TypeLinksymLookup(fmt.Sprintf(".eqfunc%d", t.Size()))
if len(closure.P) != 0 {
return closure
}
@@ -363,7 +363,7 @@ func geneq(t *types.Type) *obj.LSym {
}
ot := 0
ot = objw.SymPtr(closure, ot, memequalvarlen, 0)
- ot = objw.Uintptr(closure, ot, uint64(t.Width))
+ ot = objw.Uintptr(closure, ot, uint64(t.Size()))
objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
return closure
case types.ASPECIAL:
@@ -679,8 +679,7 @@ func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
fn := typecheck.LookupRuntime("memequal")
fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
- call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{sptr, tptr, ir.Copy(slen)})
- typecheck.Call(call)
+ call := typecheck.Call(base.Pos, fn, []ir.Node{sptr, tptr, ir.Copy(slen)}, false).(*ir.CallExpr)
cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, slen, tlen)
cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
@@ -716,8 +715,7 @@ func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
sdata.SetTypecheck(1)
tdata.SetTypecheck(1)
- call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, []ir.Node{stab, sdata, tdata})
- typecheck.Call(call)
+ call := typecheck.Call(base.Pos, fn, []ir.Node{stab, sdata, tdata}, false).(*ir.CallExpr)
cmp := ir.NewBinaryExpr(base.Pos, ir.OEQ, stab, ttab)
cmp = typecheck.Expr(cmp).(*ir.BinaryExpr)
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index eb9a8a6c9bc73ade1974d19e63cdbe225224935f..b1e2902385be45fd319eb0aaff306a433118e1ad 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -7,7 +7,6 @@ package reflectdata
import (
"encoding/binary"
"fmt"
- "internal/buildcfg"
"os"
"sort"
"strings"
@@ -19,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"
@@ -28,35 +28,27 @@ import (
"cmd/internal/src"
)
-type itabEntry struct {
- t, itype *types.Type
- lsym *obj.LSym // symbol of the itab itself
-
- // symbols of each method in
- // the itab, sorted by byte offset;
- // filled in by CompileITabs
- entries []*obj.LSym
-}
-
type ptabEntry struct {
s *types.Sym
t *types.Type
}
-func CountTabs() (numPTabs, numITabs int) {
- return len(ptabs), len(itabs)
+func CountPTabs() int {
+ return len(ptabs)
}
// runtime interface and reflection data structures
var (
- signatmu sync.Mutex // protects signatset and signatslice
- signatset = make(map[*types.Type]struct{})
- signatslice []*types.Type
+ // protects signatset and signatslice
+ signatmu sync.Mutex
+ // Tracking which types need runtime type descriptor
+ signatset = make(map[*types.Type]struct{})
+ // Queue of types wait to be generated runtime type descriptor
+ signatslice []typeAndStr
gcsymmu sync.Mutex // protects gcsymset and gcsymslice
gcsymset = make(map[*types.Type]struct{})
- itabs []itabEntry
ptabs []*ir.Name
)
@@ -105,10 +97,10 @@ func MapBucketType(t *types.Type) *types.Type {
elemtype := t.Elem()
types.CalcSize(keytype)
types.CalcSize(elemtype)
- if keytype.Width > MAXKEYSIZE {
+ if keytype.Size() > MAXKEYSIZE {
keytype = types.NewPtr(keytype)
}
- if elemtype.Width > MAXELEMSIZE {
+ if elemtype.Size() > MAXELEMSIZE {
elemtype = types.NewPtr(elemtype)
}
@@ -153,46 +145,46 @@ func MapBucketType(t *types.Type) *types.Type {
if BUCKETSIZE < 8 {
base.Fatalf("bucket size too small for proper alignment")
}
- if keytype.Align > BUCKETSIZE {
+ if uint8(keytype.Alignment()) > BUCKETSIZE {
base.Fatalf("key align too big for %v", t)
}
- if elemtype.Align > BUCKETSIZE {
+ if uint8(elemtype.Alignment()) > BUCKETSIZE {
base.Fatalf("elem align too big for %v", t)
}
- if keytype.Width > MAXKEYSIZE {
+ if keytype.Size() > MAXKEYSIZE {
base.Fatalf("key size to large for %v", t)
}
- if elemtype.Width > MAXELEMSIZE {
+ if elemtype.Size() > MAXELEMSIZE {
base.Fatalf("elem size to large for %v", t)
}
- if t.Key().Width > MAXKEYSIZE && !keytype.IsPtr() {
+ if t.Key().Size() > MAXKEYSIZE && !keytype.IsPtr() {
base.Fatalf("key indirect incorrect for %v", t)
}
- if t.Elem().Width > MAXELEMSIZE && !elemtype.IsPtr() {
+ if t.Elem().Size() > MAXELEMSIZE && !elemtype.IsPtr() {
base.Fatalf("elem indirect incorrect for %v", t)
}
- if keytype.Width%int64(keytype.Align) != 0 {
+ if keytype.Size()%keytype.Alignment() != 0 {
base.Fatalf("key size not a multiple of key align for %v", t)
}
- if elemtype.Width%int64(elemtype.Align) != 0 {
+ if elemtype.Size()%elemtype.Alignment() != 0 {
base.Fatalf("elem size not a multiple of elem align for %v", t)
}
- if bucket.Align%keytype.Align != 0 {
+ if uint8(bucket.Alignment())%uint8(keytype.Alignment()) != 0 {
base.Fatalf("bucket align not multiple of key align %v", t)
}
- if bucket.Align%elemtype.Align != 0 {
+ if uint8(bucket.Alignment())%uint8(elemtype.Alignment()) != 0 {
base.Fatalf("bucket align not multiple of elem align %v", t)
}
- if keys.Offset%int64(keytype.Align) != 0 {
+ if keys.Offset%keytype.Alignment() != 0 {
base.Fatalf("bad alignment of keys in bmap for %v", t)
}
- if elems.Offset%int64(elemtype.Align) != 0 {
+ if elems.Offset%elemtype.Alignment() != 0 {
base.Fatalf("bad alignment of elems in bmap for %v", t)
}
// Double-check that overflow field is final memory in struct,
// with no padding at end.
- if overflow.Offset != bucket.Width-int64(types.PtrSize) {
+ if overflow.Offset != bucket.Size()-int64(types.PtrSize) {
base.Fatalf("bad offset of overflow in bmap for %v", t)
}
@@ -242,8 +234,8 @@ func MapType(t *types.Type) *types.Type {
// The size of hmap should be 48 bytes on 64 bit
// and 28 bytes on 32 bit platforms.
- if size := int64(8 + 5*types.PtrSize); hmap.Width != size {
- base.Fatalf("hmap size not correct: got %d, want %d", hmap.Width, size)
+ if size := int64(8 + 5*types.PtrSize); hmap.Size() != size {
+ base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
}
t.MapType().Hmap = hmap
@@ -302,8 +294,8 @@ func MapIterType(t *types.Type) *types.Type {
hiter := types.NewStruct(types.NoPkg, fields)
hiter.SetNoalg(true)
types.CalcSize(hiter)
- if hiter.Width != int64(12*types.PtrSize) {
- base.Fatalf("hash_iter size not correct %d %d", hiter.Width, 12*types.PtrSize)
+ if hiter.Size() != int64(12*types.PtrSize) {
+ base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 12*types.PtrSize)
}
t.MapType().Hiter = hiter
hiter.StructType().Map = t
@@ -313,6 +305,10 @@ func MapIterType(t *types.Type) *types.Type {
// methods returns the methods of the non-interface type t, sorted by name.
// Generates stub functions as needed.
func methods(t *types.Type) []*typeSig {
+ if t.HasShape() {
+ // Shape types have no methods.
+ return nil
+ }
// method type
mt := types.ReceiverBaseType(t)
@@ -321,13 +317,6 @@ func methods(t *types.Type) []*typeSig {
}
typecheck.CalcMethods(mt)
- // type stored in interface word
- it := t
-
- if !types.IsDirectIface(it) {
- it = types.NewPtr(t)
- }
-
// make list of methods for t,
// generating code if necessary.
var ms []*typeSig
@@ -341,7 +330,11 @@ func methods(t *types.Type) []*typeSig {
if f.Type.Recv() == nil {
base.Fatalf("receiver with no type on %v method %v %v", mt, f.Sym, f)
}
- if f.Nointerface() {
+ if f.Nointerface() && !t.IsFullyInstantiated() {
+ // Skip creating method wrappers if f is nointerface. But, if
+ // t is an instantiated type, we still have to call
+ // methodWrapper, because methodWrapper generates the actual
+ // generic method on the type as well.
continue
}
@@ -355,11 +348,16 @@ func methods(t *types.Type) []*typeSig {
sig := &typeSig{
name: f.Sym,
- isym: methodWrapper(it, f),
- tsym: methodWrapper(t, f),
+ isym: methodWrapper(t, f, true),
+ tsym: methodWrapper(t, f, false),
type_: typecheck.NewMethodType(f.Type, t),
mtype: typecheck.NewMethodType(f.Type, nil),
}
+ if f.Nointerface() {
+ // In the case of a nointerface method on an instantiated
+ // type, don't actually apppend the typeSig.
+ continue
+ }
ms = append(ms, sig)
}
@@ -394,7 +392,7 @@ func imethods(t *types.Type) []*typeSig {
// IfaceType.Method is not in the reflect data.
// Generate the method body, so that compiled
// code can refer to it.
- methodWrapper(t, f)
+ methodWrapper(t, f, false)
}
return methods
@@ -719,7 +717,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
// ptrToThis typeOff
// }
ot := 0
- ot = objw.Uintptr(lsym, ot, uint64(t.Width))
+ ot = objw.Uintptr(lsym, ot, uint64(t.Size()))
ot = objw.Uintptr(lsym, ot, uint64(ptrdata))
ot = objw.Uint32(lsym, ot, types.TypeHash(t))
@@ -735,7 +733,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
}
exported := false
- p := t.LongString()
+ p := t.NameString()
// If we're writing out type T,
// we are very likely to write out type *T as well.
// Use the string "*T"[1:] for "T", so that the two
@@ -756,16 +754,16 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
ot = objw.Uint8(lsym, ot, tflag)
// runtime (and common sense) expects alignment to be a power of two.
- i := int(t.Align)
+ i := int(uint8(t.Alignment()))
if i == 0 {
i = 1
}
if i&(i-1) != 0 {
- base.Fatalf("invalid alignment %d for %v", t.Align, t)
+ base.Fatalf("invalid alignment %d for %v", uint8(t.Alignment()), t)
}
- ot = objw.Uint8(lsym, ot, t.Align) // align
- ot = objw.Uint8(lsym, ot, t.Align) // fieldAlign
+ ot = objw.Uint8(lsym, ot, uint8(t.Alignment())) // align
+ ot = objw.Uint8(lsym, ot, uint8(t.Alignment())) // fieldAlign
i = kinds[t.Kind()]
if types.IsDirectIface(t) {
@@ -799,11 +797,11 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
// TrackSym returns the symbol for tracking use of field/method f, assumed
// to be a member of struct/interface type t.
func TrackSym(t *types.Type, f *types.Field) *obj.LSym {
- return base.PkgLinksym("go.track", t.ShortString()+"."+f.Sym.Name, obj.ABI0)
+ return base.PkgLinksym("go.track", t.LinkString()+"."+f.Sym.Name, obj.ABI0)
}
func TypeSymPrefix(prefix string, t *types.Type) *types.Sym {
- p := prefix + "." + t.ShortString()
+ p := prefix + "." + t.LinkString()
s := types.TypeSymLookup(p)
// This function is for looking up type-related generated functions
@@ -848,16 +846,33 @@ func TypePtr(t *types.Type) *ir.AddrExpr {
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
-func ITabAddr(t, itype *types.Type) *ir.AddrExpr {
- if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
- base.Fatalf("ITabAddr(%v, %v)", t, itype)
- }
- s, existed := ir.Pkgs.Itab.LookupOK(t.ShortString() + "," + itype.ShortString())
+// ITabLsym returns the LSym representing the itab for concrete type typ implementing
+// interface iface. A dummy tab will be created in the unusual case where typ doesn't
+// implement iface. Normally, this wouldn't happen, because the typechecker would
+// have reported a compile-time error. This situation can only happen when the
+// destination type of a type assert or a type in a type switch is parameterized, so
+// it may sometimes, but not always, be a type that can't implement the specified
+// interface.
+func ITabLsym(typ, iface *types.Type) *obj.LSym {
+ s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
+ lsym := s.Linksym()
+
if !existed {
- itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()})
+ writeITab(lsym, typ, iface, true)
}
+ return lsym
+}
+// ITabAddr returns an expression representing a pointer to the itab
+// for concrete type typ implementing interface iface.
+func ITabAddr(typ, iface *types.Type) *ir.AddrExpr {
+ s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
lsym := s.Linksym()
+
+ if !existed {
+ writeITab(lsym, typ, iface, false)
+ }
+
n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
@@ -914,11 +929,12 @@ func hashMightPanic(t *types.Type) bool {
}
}
-// formalType replaces byte and rune aliases with real types.
+// formalType replaces predeclared aliases with real types.
// They've been separate internally to make error messages
// better, but we have to merge them in the reflect tables.
func formalType(t *types.Type) *types.Type {
- if t == types.ByteType || t == types.RuneType {
+ switch t {
+ case types.AnyType, types.ByteType, types.RuneType:
return types.Types[t.Kind()]
}
return t
@@ -926,7 +942,7 @@ func formalType(t *types.Type) *types.Type {
func writeType(t *types.Type) *obj.LSym {
t = formalType(t)
- if t.IsUntyped() {
+ if t.IsUntyped() || t.HasTParam() {
base.Fatalf("writeType %v", t)
}
@@ -945,25 +961,22 @@ func writeType(t *types.Type) *obj.LSym {
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
tbase = t.Elem()
}
- dupok := 0
- if tbase.Sym() == nil {
- dupok = obj.DUPOK
+ if tbase.Kind() == types.TFORW {
+ base.Fatalf("unresolved defined type: %v", tbase)
}
- if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc
- // named types from other files are defined only by those files
- if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg {
- if i := typecheck.BaseTypeIndex(t); i >= 0 {
- lsym.Pkg = tbase.Sym().Pkg.Prefix
- lsym.SymIdx = int32(i)
- lsym.Set(obj.AttrIndexed, true)
- }
- return lsym
- }
- // TODO(mdempsky): Investigate whether this can happen.
- if tbase.Kind() == types.TFORW {
- return lsym
+ if !NeedEmit(tbase) {
+ if i := typecheck.BaseTypeIndex(t); i >= 0 {
+ lsym.Pkg = tbase.Sym().Pkg.Prefix
+ lsym.SymIdx = int32(i)
+ lsym.Set(obj.AttrIndexed, true)
}
+
+ // TODO(mdempsky): Investigate whether this still happens.
+ // If we know we don't need to emit code for a type,
+ // we should have a link-symbol index for it.
+ // See also TODO in NeedEmit.
+ return lsym
}
ot := 0
@@ -1087,20 +1100,20 @@ func writeType(t *types.Type) *obj.LSym {
var flags uint32
// Note: flags must match maptype accessors in ../../../../runtime/type.go
// and maptype builder in ../../../../reflect/type.go:MapOf.
- if t.Key().Width > MAXKEYSIZE {
+ if t.Key().Size() > MAXKEYSIZE {
ot = objw.Uint8(lsym, ot, uint8(types.PtrSize))
flags |= 1 // indirect key
} else {
- ot = objw.Uint8(lsym, ot, uint8(t.Key().Width))
+ ot = objw.Uint8(lsym, ot, uint8(t.Key().Size()))
}
- if t.Elem().Width > MAXELEMSIZE {
+ if t.Elem().Size() > MAXELEMSIZE {
ot = objw.Uint8(lsym, ot, uint8(types.PtrSize))
flags |= 2 // indirect value
} else {
- ot = objw.Uint8(lsym, ot, uint8(t.Elem().Width))
+ ot = objw.Uint8(lsym, ot, uint8(t.Elem().Size()))
}
- ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Width))
+ ot = objw.Uint16(lsym, ot, uint16(MapBucketType(t).Size()))
if types.IsReflexive(t.Key()) {
flags |= 4 // reflexive key
}
@@ -1184,7 +1197,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.
@@ -1226,108 +1241,25 @@ func InterfaceMethodOffset(ityp *types.Type, i int64) int64 {
return int64(commonSize()+4*types.PtrSize+uncommonSize(ityp)) + i*8
}
-// for each itabEntry, gather the methods on
-// the concrete type that implement the interface
-func CompileITabs() {
- for i := range itabs {
- tab := &itabs[i]
- methods := genfun(tab.t, tab.itype)
- if len(methods) == 0 {
- continue
- }
- tab.entries = methods
- }
-}
-
-// for the given concrete type and interface
-// type, return the (sorted) set of methods
-// on the concrete type that implement the interface
-func genfun(t, it *types.Type) []*obj.LSym {
- if t == nil || it == nil {
- return nil
- }
- sigs := imethods(it)
- methods := methods(t)
- out := make([]*obj.LSym, 0, len(sigs))
- // TODO(mdempsky): Short circuit before calling methods(t)?
- // See discussion on CL 105039.
- if len(sigs) == 0 {
- return nil
- }
-
- // both sigs and methods are sorted by name,
- // so we can find the intersect in a single pass
- for _, m := range methods {
- if m.name == sigs[0].name {
- out = append(out, m.isym)
- sigs = sigs[1:]
- if len(sigs) == 0 {
- break
- }
- }
- }
-
- if len(sigs) != 0 {
- base.Fatalf("incomplete itab")
- }
-
- return out
-}
-
-// ITabSym uses the information gathered in
-// CompileITabs to de-virtualize interface methods.
-// Since this is called by the SSA backend, it shouldn't
-// generate additional Nodes, Syms, etc.
-func ITabSym(it *obj.LSym, offset int64) *obj.LSym {
- var syms []*obj.LSym
- if it == nil {
- return nil
- }
-
- for i := range itabs {
- e := &itabs[i]
- if e.lsym == it {
- syms = e.entries
- break
- }
- }
- if syms == nil {
- return nil
- }
-
- // keep this arithmetic in sync with *itab layout
- methodnum := int((offset - 2*int64(types.PtrSize) - 8) / int64(types.PtrSize))
- if methodnum >= len(syms) {
- return nil
- }
- return syms[methodnum]
-}
-
// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
func NeedRuntimeType(t *types.Type) {
if t.HasTParam() {
- // Generic types don't have a runtime type descriptor (but will
- // have a dictionary)
+ // Generic types don't really exist at run-time and have no runtime
+ // type descriptor. But we do write out shape types.
return
}
if _, ok := signatset[t]; !ok {
signatset[t] = struct{}{}
- signatslice = append(signatslice, t)
+ signatslice = append(signatslice, typeAndStr{t: t, short: types.TypeSymName(t), regular: t.String()})
}
}
func WriteRuntimeTypes() {
- // Process signatset. Use a loop, as writeType adds
- // entries to signatset while it is being processed.
- signats := make([]typeAndStr, len(signatslice))
+ // Process signatslice. Use a loop, as writeType adds
+ // entries to signatslice while it is being processed.
for len(signatslice) > 0 {
- signats = signats[:0]
- // Transfer entries to a slice and sort, for reproducible builds.
- for _, t := range signatslice {
- signats = append(signats, typeAndStr{t: t, short: types.TypeSymName(t), regular: t.String()})
- delete(signatset, t)
- }
- signatslice = signatslice[:0]
+ signats := signatslice
+ // Sort for reproducible builds.
sort.Sort(typesByString(signats))
for _, ts := range signats {
t := ts.t
@@ -1336,6 +1268,7 @@ func WriteRuntimeTypes() {
writeType(types.NewPtr(t))
}
}
+ signatslice = signatslice[len(signats):]
}
// Emit GC data symbols.
@@ -1349,29 +1282,67 @@ func WriteRuntimeTypes() {
}
}
-func WriteTabs() {
- // process itabs
- for _, i := range itabs {
- // dump empty itab symbol into i.sym
- // type itab struct {
- // inter *interfacetype
- // _type *_type
- // hash uint32
- // _ [4]byte
- // fun [1]uintptr // variable sized
- // }
- o := objw.SymPtr(i.lsym, 0, writeType(i.itype), 0)
- o = objw.SymPtr(i.lsym, o, writeType(i.t), 0)
- o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash
- o += 4 // skip unused field
- for _, fn := range genfun(i.t, i.itype) {
- o = objw.SymPtrWeak(i.lsym, o, fn, 0) // method pointer for each method
+// writeITab writes the itab for concrete type typ implementing interface iface. If
+// allowNonImplement is true, allow the case where typ does not implement iface, and just
+// create a dummy itab with zeroed-out method entries.
+func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) {
+ // TODO(mdempsky): Fix methodWrapper, geneq, and genhash (and maybe
+ // others) to stop clobbering these.
+ oldpos, oldfn := base.Pos, ir.CurFunc
+ defer func() { base.Pos, ir.CurFunc = oldpos, oldfn }()
+
+ if typ == nil || (typ.IsPtr() && typ.Elem() == nil) || typ.IsUntyped() || iface == nil || !iface.IsInterface() || iface.IsEmptyInterface() {
+ base.Fatalf("writeITab(%v, %v)", typ, iface)
+ }
+
+ sigs := iface.AllMethods().Slice()
+ entries := make([]*obj.LSym, 0, len(sigs))
+
+ // both sigs and methods are sorted by name,
+ // so we can find the intersection in a single pass
+ for _, m := range methods(typ) {
+ if m.name == sigs[0].Sym {
+ entries = append(entries, m.isym)
+ if m.isym == nil {
+ panic("NO ISYM")
+ }
+ sigs = sigs[1:]
+ if len(sigs) == 0 {
+ break
+ }
+ }
+ }
+ completeItab := len(sigs) == 0
+ if !allowNonImplement && !completeItab {
+ base.Fatalf("incomplete itab")
+ }
+
+ // dump empty itab symbol into i.sym
+ // type itab struct {
+ // inter *interfacetype
+ // _type *_type
+ // hash uint32
+ // _ [4]byte
+ // fun [1]uintptr // variable sized
+ // }
+ o := objw.SymPtr(lsym, 0, writeType(iface), 0)
+ o = objw.SymPtr(lsym, o, writeType(typ), 0)
+ o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
+ o += 4 // skip unused field
+ for _, fn := range entries {
+ if !completeItab {
+ // If typ doesn't implement iface, make method entries be zero.
+ o = objw.Uintptr(lsym, o, 0)
+ } else {
+ o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
}
- // Nothing writes static itabs, so they are read only.
- objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
- i.lsym.Set(obj.AttrContentAddressable, true)
}
+ // Nothing writes static itabs, so they are read only.
+ objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
+ lsym.Set(obj.AttrContentAddressable, true)
+}
+func WriteTabs() {
// process ptabs
if types.LocalPkg.Name == "main" && len(ptabs) > 0 {
ot := 0
@@ -1426,6 +1397,9 @@ func WriteBasicTypes() {
}
writeType(types.NewPtr(types.Types[types.TSTRING]))
writeType(types.NewPtr(types.Types[types.TUNSAFEPTR]))
+ if base.Flag.G > 0 {
+ writeType(types.AnyType)
+ }
// emit type structs for error and func(error) string.
// The latter is the type of an auto-generated wrapper.
@@ -1446,6 +1420,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", ""))
}
@@ -1453,7 +1430,7 @@ func WriteBasicTypes() {
type typeAndStr struct {
t *types.Type
- short string
+ short string // "short" here means NameString
regular string
}
@@ -1466,8 +1443,13 @@ func (a typesByString) Less(i, j int) bool {
}
// When the only difference between the types is whether
// they refer to byte or uint8, such as **byte vs **uint8,
- // the types' ShortStrings can be identical.
+ // the types' NameStrings can be identical.
// To preserve deterministic sort ordering, sort these by String().
+ //
+ // TODO(mdempsky): This all seems suspect. Using LinkString would
+ // avoid naming collisions, and there shouldn't be a reason to care
+ // about "byte" vs "uint8": they share the same runtime type
+ // descriptor anyway.
if a[i].regular != a[j].regular {
return a[i].regular < a[j].regular
}
@@ -1594,7 +1576,7 @@ func fillptrmask(t *types.Type, ptrmask []byte) {
// For non-trivial arrays, the program describes the full t.Width size.
func dgcprog(t *types.Type, write bool) (*obj.LSym, int64) {
types.CalcSize(t)
- if t.Width == types.BADWIDTH {
+ if t.Size() == types.BADWIDTH {
base.Fatalf("dgcprog: %v badwidth", t)
}
lsym := TypeLinksymPrefix(".gcprog", t)
@@ -1603,8 +1585,8 @@ func dgcprog(t *types.Type, write bool) (*obj.LSym, int64) {
p.emit(t, 0)
offset := p.w.BitIndex() * int64(types.PtrSize)
p.end()
- if ptrdata := types.PtrDataSize(t); offset < ptrdata || offset > t.Width {
- base.Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width)
+ if ptrdata := types.PtrDataSize(t); offset < ptrdata || offset > t.Size() {
+ base.Fatalf("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Size())
}
return lsym, offset
}
@@ -1653,7 +1635,7 @@ func (p *gcProg) emit(t *types.Type, offset int64) {
if !t.HasPointers() {
return
}
- if t.Width == int64(types.PtrSize) {
+ if t.Size() == int64(types.PtrSize) {
p.w.Ptr(offset / int64(types.PtrSize))
return
}
@@ -1685,16 +1667,16 @@ func (p *gcProg) emit(t *types.Type, offset int64) {
elem = elem.Elem()
}
- if !p.w.ShouldRepeat(elem.Width/int64(types.PtrSize), count) {
+ if !p.w.ShouldRepeat(elem.Size()/int64(types.PtrSize), count) {
// Cheaper to just emit the bits.
for i := int64(0); i < count; i++ {
- p.emit(elem, offset+i*elem.Width)
+ p.emit(elem, offset+i*elem.Size())
}
return
}
p.emit(elem, offset)
- p.w.ZeroUntil((offset + elem.Width) / int64(types.PtrSize))
- p.w.Repeat(elem.Width/int64(types.PtrSize), count-1)
+ p.w.ZeroUntil((offset + elem.Size()) / int64(types.PtrSize))
+ p.w.Repeat(elem.Size()/int64(types.PtrSize), count-1)
case types.TSTRUCT:
for _, t1 := range t.Fields().Slice() {
@@ -1741,6 +1723,49 @@ func CollectPTabs() {
}
}
+// NeedEmit reports whether typ is a type that we need to emit code
+// for (e.g., runtime type descriptors, method wrappers).
+func NeedEmit(typ *types.Type) bool {
+ // TODO(mdempsky): Export data should keep track of which anonymous
+ // and instantiated types were emitted, so at least downstream
+ // packages can skip re-emitting them.
+ //
+ // Perhaps we can just generalize the linker-symbol indexing to
+ // track the index of arbitrary types, not just defined types, and
+ // use its presence to detect this. The same idea would work for
+ // instantiated generic functions too.
+
+ switch sym := typ.Sym(); {
+ case sym == nil:
+ // Anonymous type; possibly never seen before or ever again.
+ // Need to emit to be safe (however, see TODO above).
+ return true
+
+ case sym.Pkg == types.LocalPkg:
+ // Local defined type; our responsibility.
+ return true
+
+ case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == types.UnsafePkg):
+ // Package runtime is responsible for including code for builtin
+ // types (predeclared and package unsafe).
+ return true
+
+ case typ.IsFullyInstantiated():
+ // Instantiated type; possibly instantiated with unique type arguments.
+ // Need to emit to be safe (however, see TODO above).
+ return true
+
+ case typ.HasShape():
+ // Shape type; need to emit even though it lives in the .shape package.
+ // TODO: make sure the linker deduplicates them (see dupok in writeType above).
+ return true
+
+ default:
+ // Should have been emitted by an imported package.
+ return false
+ }
+}
+
// Generate a wrapper function to convert from
// a receiver of type T to a receiver of type U.
// That is,
@@ -1761,7 +1786,39 @@ func CollectPTabs() {
//
// rcvr - U
// method - M func (t T)(), a TFIELD type struct
-func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
+//
+// Also wraps methods on instantiated generic types for use in itab entries.
+// For an instantiated generic type G[int], we generate wrappers like:
+// G[int] pointer shaped:
+// func (x G[int]) f(arg) {
+// .inst.G[int].f(dictionary, x, arg)
+// }
+// G[int] not pointer shaped:
+// func (x *G[int]) f(arg) {
+// .inst.G[int].f(dictionary, *x, arg)
+// }
+// These wrappers are always fully stenciled.
+func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym {
+ orig := rcvr
+ if forItab && !types.IsDirectIface(rcvr) {
+ rcvr = rcvr.PtrTo()
+ }
+
+ generic := false
+ // We don't need a dictionary if we are reaching a method (possibly via an
+ // embedded field) which is an interface method.
+ if !types.IsInterfaceMethod(method.Type) {
+ rcvr1 := deref(rcvr)
+ if len(rcvr1.RParams()) > 0 {
+ // If rcvr has rparams, remember method as generic, which
+ // means we need to add a dictionary to the wrapper.
+ generic = true
+ if rcvr.HasShape() {
+ base.Fatalf("method on type instantiated with shapes, rcvr:%+v", rcvr)
+ }
+ }
+ }
+
newnam := ir.MethodSym(rcvr, method.Sym)
lsym := newnam.Linksym()
if newnam.Siggen() {
@@ -1769,19 +1826,19 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
}
newnam.SetSiggen(true)
- if types.Identical(rcvr, method.Type.Recv().Type) {
+ // Except in quirks mode, unified IR creates its own wrappers.
+ if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
return lsym
}
- // Only generate (*T).M wrappers for T.M in T's own package.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
- rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg {
+ methodrcvr := method.Type.Recv().Type
+ // For generic methods, we need to generate the wrapper even if the receiver
+ // types are identical, because we want to add the dictionary.
+ if !generic && types.Identical(rcvr, methodrcvr) {
return lsym
}
- // Only generate I.M wrappers for I in I's own package
- // but keep doing it for error.Error (was issue #29304).
- if rcvr.IsInterface() && rcvr.Sym() != nil && rcvr.Sym().Pkg != types.LocalPkg && rcvr != types.ErrorType {
+ if !NeedEmit(rcvr) || rcvr.IsPtr() && !NeedEmit(rcvr.Elem()) {
return lsym
}
@@ -1801,10 +1858,10 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
nthis := ir.AsNode(tfn.Type().Recv().Nname)
- methodrcvr := method.Type.Recv().Type
+ indirect := rcvr.IsPtr() && rcvr.Elem() == methodrcvr
// generate nil pointer check for better error
- if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
+ if indirect {
// generating wrapper from *T to T.
n := ir.NewIfStmt(base.Pos, nil, nil, nil)
n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil())
@@ -1814,7 +1871,6 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
}
dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym))
-
// generate call
// It's not possible to use a tail call when dynamic linking on ppc64le. The
// bad scenario is when a local call is made to the wrapper: the wrapper will
@@ -1822,24 +1878,72 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
// the TOC to the appropriate value for that module. But if it returns
// directly to the wrapper's caller, nothing will reset it to the correct
// value for that function.
- //
- // Disable tailcall for RegabiArgs for now. The IR does not connect the
- // arguments with the OTAILCALL node, and the arguments are not marshaled
- // correctly.
- if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs {
- // generate tail call: adjust pointer receiver and jump to embedded method.
- left := dot.X // skip final .M
- if !left.Type().IsPtr() {
- left = typecheck.NodAddr(left)
- }
- as := ir.NewAssignStmt(base.Pos, nthis, typecheck.ConvNop(left, rcvr))
- fn.Body.Append(as)
- fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name)))
- } else {
- fn.SetWrapper(true) // ignore frame for panic+recover matching
+ if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !generic {
call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
call.Args = ir.ParamNames(tfn.Type())
call.IsDDD = tfn.Type().IsVariadic()
+ fn.Body.Append(ir.NewTailCallStmt(base.Pos, call))
+ } else {
+ fn.SetWrapper(true) // ignore frame for panic+recover matching
+ var call *ir.CallExpr
+
+ if generic && dot.X != nthis {
+ // If there is embedding involved, then we should do the
+ // normal non-generic embedding wrapper below, which calls
+ // the wrapper for the real receiver type using dot as an
+ // argument. There is no need for generic processing (adding
+ // a dictionary) for this wrapper.
+ generic = false
+ }
+
+ if generic {
+ targs := deref(rcvr).RParams()
+ // The wrapper for an auto-generated pointer/non-pointer
+ // receiver method should share the same dictionary as the
+ // corresponding original (user-written) method.
+ baseOrig := orig
+ if baseOrig.IsPtr() && !methodrcvr.IsPtr() {
+ baseOrig = baseOrig.Elem()
+ } else if !baseOrig.IsPtr() && methodrcvr.IsPtr() {
+ baseOrig = types.NewPtr(baseOrig)
+ }
+ args := []ir.Node{getDictionary(ir.MethodSym(baseOrig, method.Sym), targs)}
+ if indirect {
+ args = append(args, ir.NewStarExpr(base.Pos, dot.X))
+ } else if methodrcvr.IsPtr() && methodrcvr.Elem() == dot.X.Type() {
+ // Case where method call is via a non-pointer
+ // embedded field with a pointer method.
+ args = append(args, typecheck.NodAddrAt(base.Pos, dot.X))
+ } else {
+ args = append(args, dot.X)
+ }
+ args = append(args, ir.ParamNames(tfn.Type())...)
+
+ // Target method uses shaped names.
+ targs2 := make([]*types.Type, len(targs))
+ for i, t := range targs {
+ targs2[i] = typecheck.Shapify(t, i)
+ }
+ targs = targs2
+
+ sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, false, true)
+ if sym.Def == nil {
+ // Currently we make sure that we have all the instantiations
+ // we need by generating them all in ../noder/stencil.go:instantiateMethods
+ // TODO: maybe there's a better, more incremental way to generate
+ // only the instantiations we need?
+ base.Fatalf("instantiation %s not found", sym.Name)
+ }
+ target := ir.AsNode(sym.Def)
+ call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
+ // Fill-in the generic method node that was not filled in
+ // in instantiateMethod.
+ method.Nname = fn.Nname
+ } else {
+ call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ }
+ call.IsDDD = tfn.Type().IsVariadic()
if method.Type.NumResults() > 0 {
ret := ir.NewReturnStmt(base.Pos, nil)
ret.Results = []ir.Node{call}
@@ -1858,13 +1962,10 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
ir.CurFunc = fn
typecheck.Stmts(fn.Body)
- // Inline calls within (*T).M wrappers. This is safe because we only
- // generate those wrappers within the same compilation unit as (T).M.
- // TODO(mdempsky): Investigate why we can't enable this more generally.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil {
+ if AfterGlobalEscapeAnalysis {
inline.InlineCalls(fn)
+ escape.Batch([]*ir.Func{fn}, false)
}
- escape.Batch([]*ir.Func{fn}, false)
ir.CurFunc = nil
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
@@ -1872,11 +1973,21 @@ func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
return lsym
}
+// AfterGlobalEscapeAnalysis tracks whether package gc has already
+// performed the main, global escape analysis pass. If so,
+// methodWrapper takes responsibility for escape analyzing any
+// generated wrappers.
+var AfterGlobalEscapeAnalysis bool
+
var ZeroSize int64
// MarkTypeUsedInInterface marks that type t is converted to an interface.
// This information is used in the linker in dead method elimination.
func MarkTypeUsedInInterface(t *types.Type, from *obj.LSym) {
+ if t.HasShape() {
+ // Shape types shouldn't be put in interfaces, so we shouldn't ever get here.
+ base.Fatalf("shape types have no methods %+v", t)
+ }
tsym := TypeLinksym(t)
// Emit a marker relocation. The linker will know the type is converted
// to an interface if "from" is reachable.
@@ -1894,12 +2005,88 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
}
dot := n.X.(*ir.SelectorExpr)
ityp := dot.X.Type()
+ if ityp.HasShape() {
+ // 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
- // dot.Xoffset is the method index * PtrSize (the offset of code pointer
+ // dot.Offset() is the method index * PtrSize (the offset of code pointer
// in itab).
midx := dot.Offset() / int64(types.PtrSize)
r.Add = InterfaceMethodOffset(ityp, midx)
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 {
+ if len(targs) == 0 {
+ base.Fatalf("%s should have type arguments", gf.Name)
+ }
+ for _, t := range targs {
+ if t.HasShape() {
+ base.Fatalf("dictionary for %s should only use concrete types: %+v", gf.Name, t)
+ }
+ }
+
+ sym := typecheck.MakeDictSym(gf, targs, true)
+
+ // Dictionary should already have been generated by instantiateMethods().
+ if lsym := sym.Linksym(); len(lsym.P) == 0 {
+ base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name)
+ }
+
+ // Make (or reuse) a node referencing the dictionary symbol.
+ var n *ir.Name
+ if sym.Def != nil {
+ n = sym.Def.(*ir.Name)
+ } else {
+ n = typecheck.NewName(sym)
+ n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
+ n.SetTypecheck(1)
+ n.Class = ir.PEXTERN
+ sym.Def = n
+ }
+
+ // Return the address of the dictionary.
+ np := typecheck.NodAddr(n)
+ // Note: treat dictionary pointers as uintptrs, so they aren't pointers
+ // with respect to GC. That saves on stack scanning work, write barriers, etc.
+ // We can get away with it because dictionaries are global variables.
+ np.SetType(types.Types[types.TUINTPTR])
+ np.SetTypecheck(1)
+ return np
+}
+
+func deref(t *types.Type) *types.Type {
+ if t.IsPtr() {
+ return t.Elem()
+ }
+ return t
+}
diff --git a/src/cmd/compile/internal/riscv64/galign.go b/src/cmd/compile/internal/riscv64/galign.go
index 338248a7cf27ab13b71ec4ed25f90a015fdc6511..846ed8fb3802e43d603abe6bb0de2617a177dca8 100644
--- a/src/cmd/compile/internal/riscv64/galign.go
+++ b/src/cmd/compile/internal/riscv64/galign.go
@@ -16,7 +16,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.MAXWIDTH = 1 << 50
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.ZeroRange = zeroRange
arch.SSAMarkMoves = ssaMarkMoves
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index 64a9b3b33b9aca34cfd15284ff14eb765a67d52a..1359b6a0c388b416dbcda35ef746d0c88497ef44 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -272,7 +272,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS,
ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES,
ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD,
- ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED:
+ ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED,
+ ssa.OpRISCV64FSGNJD:
r := v.Reg()
r1 := v.Args[0].Reg()
r2 := v.Args[1].Reg()
@@ -282,7 +283,54 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
- case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
+ case ssa.OpRISCV64LoweredMuluhilo:
+ r0 := v.Args[0].Reg()
+ r1 := v.Args[1].Reg()
+ p := s.Prog(riscv.AMULHU)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = r1
+ p.Reg = r0
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg0()
+ p1 := s.Prog(riscv.AMUL)
+ p1.From.Type = obj.TYPE_REG
+ p1.From.Reg = r1
+ p1.Reg = r0
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = v.Reg1()
+ case ssa.OpRISCV64LoweredMuluover:
+ r0 := v.Args[0].Reg()
+ r1 := v.Args[1].Reg()
+ p := s.Prog(riscv.AMULHU)
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = r1
+ p.Reg = r0
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = v.Reg1()
+ p1 := s.Prog(riscv.AMUL)
+ p1.From.Type = obj.TYPE_REG
+ p1.From.Reg = r1
+ p1.Reg = r0
+ p1.To.Type = obj.TYPE_REG
+ p1.To.Reg = v.Reg0()
+ p2 := s.Prog(riscv.ASNEZ)
+ p2.From.Type = obj.TYPE_REG
+ p2.From.Reg = v.Reg1()
+ p2.To.Type = obj.TYPE_REG
+ p2.To.Reg = v.Reg1()
+ case ssa.OpRISCV64FMADDD, ssa.OpRISCV64FMSUBD, ssa.OpRISCV64FNMADDD, ssa.OpRISCV64FNMSUBD:
+ r := v.Reg()
+ r1 := v.Args[0].Reg()
+ r2 := v.Args[1].Reg()
+ r3 := v.Args[2].Reg()
+ p := s.Prog(v.Op.Asm())
+ p.From.Type = obj.TYPE_REG
+ p.From.Reg = r2
+ p.Reg = r1
+ p.SetRestArgs([]obj.Addr{{Type: obj.TYPE_REG, Reg: r3}})
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = r
+ case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FABSD, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
@@ -365,6 +413,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
s.Call(v)
+ case ssa.OpRISCV64CALLtail:
+ s.TailCall(v)
case ssa.OpRISCV64LoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -677,14 +727,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ,
ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ,
ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go
index b004a2db0a39b3f61c9b8e1d93d648d3d44bcd0a..d880834c220d91c763667614a46b241fb23484bb 100644
--- a/src/cmd/compile/internal/s390x/galign.go
+++ b/src/cmd/compile/internal/s390x/galign.go
@@ -16,7 +16,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index ddc05b36add0fa1b9fd22c3697c12be3b124e6eb..deb6c790069ca29870bbe2a96ba9e6c53725a554 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -556,6 +556,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg()
case ssa.OpS390XCALLstatic, ssa.OpS390XCALLclosure, ssa.OpS390XCALLinter:
s.Call(v)
+ case ssa.OpS390XCALLtail:
+ s.TailCall(v)
case ssa.OpS390XLoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
@@ -899,17 +901,11 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Br(s390x.ABR, b.Succs[0].Block())
}
return
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
return
case ssa.BlockRet:
s.Prog(obj.ARET)
return
- case ssa.BlockRetJmp:
- p := s.Prog(s390x.ABR)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
- return
}
// Handle s390x-specific blocks. These blocks all have a
diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
index 71ca774431e33cb99a4344d1aaa6e341a052997a..6ff3188f9b20159bcd71cefd626c1221e03d40ef 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 1d34f8160b1ea50a5b9d30fa7fdbb4e01c0bc393..be5f9e0a8b45454df60cf16b423d5f05347a8775 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/check.go b/src/cmd/compile/internal/ssa/check.go
index 969fd96dbf573cf67483a1f740f5880fb5d8940e..28edfd2237d9253c88731c3c6791e2db5700663b 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -66,9 +66,6 @@ func checkFunc(f *Func) {
if !b.Controls[0].Type.IsMemory() {
f.Fatalf("retjmp block %s has non-memory control value %s", b, b.Controls[0].LongString())
}
- if b.Aux == nil {
- f.Fatalf("retjmp block %s has nil Aux field", b)
- }
case BlockPlain:
if len(b.Succs) != 1 {
f.Fatalf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs))
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index cd8eba405d5c7bc95fa23175aca1652bde7b13db..f87ea5b893ef7561d06144bea1805232b9185ef4 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -10,9 +10,11 @@ import (
"fmt"
"hash/crc32"
"internal/buildcfg"
+ "io"
"log"
"math/rand"
"os"
+ "path/filepath"
"regexp"
"runtime"
"sort"
@@ -59,7 +61,7 @@ func Compile(f *Func) {
printFunc(f)
}
f.HTMLWriter.WritePhase("start", "start")
- if BuildDump != "" && BuildDump == f.Name {
+ if BuildDump[f.Name] {
f.dumpFile("build")
}
if checkEnabled {
@@ -163,25 +165,37 @@ func Compile(f *Func) {
phaseName = ""
}
-// dumpFile creates a file from the phase name and function name
-// Dumping is done to files to avoid buffering huge strings before
-// output.
-func (f *Func) dumpFile(phaseName string) {
+// DumpFileForPhase creates a file from the function name and phase name,
+// warning and returning nil if this is not possible.
+func (f *Func) DumpFileForPhase(phaseName string) io.WriteCloser {
f.dumpFileSeq++
fname := fmt.Sprintf("%s_%02d__%s.dump", f.Name, int(f.dumpFileSeq), phaseName)
fname = strings.Replace(fname, " ", "_", -1)
fname = strings.Replace(fname, "/", "_", -1)
fname = strings.Replace(fname, ":", "_", -1)
+ if ssaDir := os.Getenv("GOSSADIR"); ssaDir != "" {
+ fname = filepath.Join(ssaDir, fname)
+ }
+
fi, err := os.Create(fname)
if err != nil {
f.Warnl(src.NoXPos, "Unable to create after-phase dump file %s", fname)
- return
+ return nil
}
+ return fi
+}
- p := stringFuncPrinter{w: fi}
- fprintFunc(p, f)
- fi.Close()
+// dumpFile creates a file from the phase name and function name
+// Dumping is done to files to avoid buffering huge strings before
+// output.
+func (f *Func) dumpFile(phaseName string) {
+ fi := f.DumpFileForPhase(phaseName)
+ if fi != nil {
+ p := stringFuncPrinter{w: fi}
+ fprintFunc(p, f)
+ fi.Close()
+ }
}
type pass struct {
@@ -224,7 +238,9 @@ var IntrinsicsDisable bool
var BuildDebug int
var BuildTest int
var BuildStats int
-var BuildDump string // name of function to dump after initial build of ssa
+var BuildDump map[string]bool = make(map[string]bool) // names of functions to dump after initial build of ssa
+
+var GenssaDump map[string]bool = make(map[string]bool) // names of functions to dump after ssa has been converted to asm
// PhaseOption sets the specified flag in the specified ssa phase,
// returning empty string if this was successful or a string explaining
@@ -248,7 +264,7 @@ func PhaseOption(phase, flag string, val int, valString string) string {
switch phase {
case "", "help":
lastcr := 0
- phasenames := " check, all, build, intrinsics"
+ phasenames := " check, all, build, intrinsics, genssa"
for _, p := range passes {
pn := strings.Replace(p.name, " ", "_", -1)
if len(pn)+len(phasenames)-lastcr > 70 {
@@ -278,6 +294,7 @@ where:
Phase "all" supports flags "time", "mem", and "dump".
Phase "intrinsics" supports flags "on", "off", and "debug".
+Phase "genssa" (assembly generation) supports the flag "dump".
If the "dump" flag is specified, the output is written on a file named
___.dump; otherwise it is directed to stdout.
@@ -339,10 +356,11 @@ commas. For example:
case "dump":
alldump = val != 0
if alldump {
- BuildDump = valString
+ BuildDump[valString] = true
+ GenssaDump[valString] = true
}
default:
- return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+ return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/all/{time,mem,dump=function_name})", flag, phase)
}
}
@@ -355,7 +373,7 @@ commas. For example:
case "debug":
IntrinsicsDebug = val
default:
- return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+ return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/intrinsics/{on,off,debug})", flag, phase)
}
return ""
}
@@ -368,9 +386,18 @@ commas. For example:
case "stats":
BuildStats = val
case "dump":
- BuildDump = valString
+ BuildDump[valString] = true
+ default:
+ return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/build/{debug,test,stats,dump=function_name})", flag, phase)
+ }
+ return ""
+ }
+ if phase == "genssa" {
+ switch flag {
+ case "dump":
+ GenssaDump[valString] = true
default:
- return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option", flag, phase)
+ return fmt.Sprintf("Did not find a flag matching %s in -d=ssa/%s debug option (expected ssa/genssa/dump=function_name)", flag, phase)
}
return ""
}
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index a8393a19995a5f922a419fdd899acedf76a12bef..5ab7240acf683da59192ba04fc91c12770bfe664 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -149,12 +149,6 @@ type Frontend interface {
// for the parts of that compound type.
SplitSlot(parent *LocalSlot, suffix string, offset int64, t *types.Type) LocalSlot
- // DerefItab dereferences an itab function
- // entry, given the symbol of the itab and
- // the byte offset of the function pointer.
- // It may return nil.
- DerefItab(sym *obj.LSym, offset int64) *obj.LSym
-
// Line returns a string describing the given position.
Line(src.XPos) string
@@ -177,7 +171,7 @@ type Frontend interface {
}
// NewConfig returns a new configuration object for the given architecture.
-func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config {
+func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat bool) *Config {
c := &Config{arch: arch, Types: types}
c.useAvg = true
c.useHmul = true
@@ -196,7 +190,7 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.floatParamRegs = paramFloatRegAMD64
c.FPReg = framepointerRegAMD64
c.LinkReg = linkRegAMD64
- c.hasGReg = buildcfg.Experiment.RegabiG
+ c.hasGReg = true
case "386":
c.PtrSize = 4
c.RegSize = 4
@@ -228,6 +222,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.registers = registersARM64[:]
c.gpRegMask = gpRegMaskARM64
c.fpRegMask = fpRegMaskARM64
+ c.intParamRegs = paramIntRegARM64
+ c.floatParamRegs = paramFloatRegARM64
c.FPReg = framepointerRegARM64
c.LinkReg = linkRegARM64
c.hasGReg = true
@@ -243,9 +239,10 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.registers = registersPPC64[:]
c.gpRegMask = gpRegMaskPPC64
c.fpRegMask = fpRegMaskPPC64
+ c.intParamRegs = paramIntRegPPC64
+ c.floatParamRegs = paramFloatRegPPC64
c.FPReg = framepointerRegPPC64
c.LinkReg = linkRegPPC64
- c.noDuffDevice = true // TODO: Resolve PPC64 DuffDevice (has zero, but not copy)
c.hasGReg = true
case "mips64":
c.BigEndian = true
@@ -324,6 +321,10 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize bool) *Config
c.optimize = optimize
c.useSSE = true
c.UseFMA = true
+ c.SoftFloat = softfloat
+ if softfloat {
+ c.floatParamRegs = nil // no FP registers in softfloat mode
+ }
c.ABI0 = abi.NewABIConfig(0, 0, ctxt.FixedFrameSize())
c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.FixedFrameSize())
diff --git a/src/cmd/compile/internal/ssa/critical.go b/src/cmd/compile/internal/ssa/critical.go
index b85721eba488323fa1ce39c6aeb8707157f4e832..500ce3ae61ce093de51b3c703160d042db2121ca 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 5d10dfe025e97ccd2dcad6af9500eeb04f7df40f..b47b106975ac13f36b4b14784019dcdd1f673cf1 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/debug.go b/src/cmd/compile/internal/ssa/debug.go
index 8e2872363b6edbceaa5a3c1c00c396569092b9bc..aad59fa24ec9c46e60b49315bc4a2aeacfe77ec7 100644
--- a/src/cmd/compile/internal/ssa/debug.go
+++ b/src/cmd/compile/internal/ssa/debug.go
@@ -34,6 +34,9 @@ type FuncDebug struct {
VarSlots [][]SlotID
// The location list data, indexed by VarID. Must be processed by PutLocationList.
LocationLists [][]byte
+ // Register-resident output parameters for the function. This is filled in at
+ // SSA generation time.
+ RegOutputParams []*ir.Name
// Filled in by the user. Translates Block and Value ID to PC.
GetPC func(ID, ID) int64
@@ -378,7 +381,7 @@ func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
split, _ = sc.lookup(*ls.SplitOf)
}
k := slotKey{
- name: ls.N, offset: ls.Off, width: ls.Type.Width,
+ name: ls.N, offset: ls.Off, width: ls.Type.Size(),
splitOf: split, splitOffset: ls.SplitOffset,
}
if idx, ok := sc.slmap[k]; ok {
@@ -548,10 +551,10 @@ func PopulateABIInRegArgOps(f *Func) {
f.Entry.Values = append(newValues, f.Entry.Values...)
}
-// BuildFuncDebug returns debug information for f.
+// BuildFuncDebug debug information for f, placing the results in "rval".
// f must be fully processed, so that each Value is where it will be when
// machine code is emitted.
-func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
+func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
if f.RegAlloc == nil {
f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
}
@@ -661,12 +664,11 @@ func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset fu
blockLocs := state.liveness()
state.buildLocationLists(blockLocs)
- return &FuncDebug{
- Slots: state.slots,
- VarSlots: state.varSlots,
- Vars: state.vars,
- LocationLists: state.lists,
- }
+ // Populate "rval" with what we've computed.
+ rval.Slots = state.slots
+ rval.VarSlots = state.varSlots
+ rval.Vars = state.vars
+ rval.LocationLists = state.lists
}
// liveness walks the function in control flow order, calculating the start
@@ -1120,54 +1122,93 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
v.Op == OpArgIntReg || v.Op == OpArgFloatReg
}
+ blockPrologComplete := func(v *Value) bool {
+ if b.ID != state.f.Entry.ID {
+ return !opcodeTable[v.Op].zeroWidth
+ } else {
+ return v.Op == OpInitMem
+ }
+ }
+
+ // Examine the prolog portion of the block to process special
+ // zero-width ops such as Arg, Phi, LoweredGetClosurePtr (etc)
+ // whose lifetimes begin at the block starting point. In an
+ // entry block, allow for the possibility that we may see Arg
+ // ops that appear _after_ other non-zero-width operations.
+ // Example:
+ //
+ // v33 = ArgIntReg {foo+0} [0] : AX (foo)
+ // v34 = ArgIntReg {bar+0} [0] : BX (bar)
+ // ...
+ // v77 = StoreReg v67 : ctx+8[unsafe.Pointer]
+ // v78 = StoreReg v68 : ctx[unsafe.Pointer]
+ // v79 = Arg <*uint8> {args} : args[*uint8] (args[*uint8])
+ // v80 = Arg {args} [8] : args+8[int] (args+8[int])
+ // ...
+ // v1 = InitMem
+ //
+ // We can stop scanning the initial portion of the block when
+ // we either see the InitMem op (for entry blocks) or the
+ // first non-zero-width op (for other blocks).
+ for idx := 0; idx < len(b.Values); idx++ {
+ v := b.Values[idx]
+ if blockPrologComplete(v) {
+ break
+ }
+ // Consider only "lifetime begins at block start" ops.
+ if !mustBeFirst(v) && v.Op != OpArg {
+ continue
+ }
+ slots := state.valueNames[v.ID]
+ reg, _ := state.f.getHome(v.ID).(*Register)
+ changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
+ if changed {
+ for _, varID := range state.changedVars.contents() {
+ state.updateVar(VarID(varID), v.Block, BlockStart)
+ }
+ state.changedVars.clear()
+ }
+ }
+
+ // Now examine the block again, handling things other than the
+ // "begins at block start" lifetimes.
zeroWidthPending := false
- blockPrologComplete := false // set to true at first non-zero-width op
- apcChangedSize := 0 // size of changedVars for leading Args, Phi, ClosurePtr
+ prologComplete := false
// expect to see values in pattern (apc)* (zerowidth|real)*
for _, v := range b.Values {
+ if blockPrologComplete(v) {
+ prologComplete = true
+ }
slots := state.valueNames[v.ID]
reg, _ := state.f.getHome(v.ID).(*Register)
changed := state.processValue(v, slots, reg) // changed == added to state.changedVars
if opcodeTable[v.Op].zeroWidth {
+ if prologComplete && mustBeFirst(v) {
+ panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
+ }
if changed {
if mustBeFirst(v) || v.Op == OpArg {
- // These ranges begin at true beginning of block, not after first instruction
- if blockPrologComplete && mustBeFirst(v) {
- panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
- }
- apcChangedSize = len(state.changedVars.contents())
- // Other zero-width ops must wait on a "real" op.
- zeroWidthPending = true
+ // already taken care of above
continue
}
+ zeroWidthPending = true
}
continue
}
-
if !changed && !zeroWidthPending {
continue
}
- // Not zero-width; i.e., a "real" instruction.
+ // Not zero-width; i.e., a "real" instruction.
zeroWidthPending = false
- blockPrologComplete = true
- for i, varID := range state.changedVars.contents() {
- if i < apcChangedSize { // buffered true start-of-block changes
- state.updateVar(VarID(varID), v.Block, BlockStart)
- } else {
- state.updateVar(VarID(varID), v.Block, v)
- }
+ for _, varID := range state.changedVars.contents() {
+ state.updateVar(VarID(varID), v.Block, v)
}
state.changedVars.clear()
- apcChangedSize = 0
}
- for i, varID := range state.changedVars.contents() {
- if i < apcChangedSize { // buffered true start-of-block changes
- state.updateVar(VarID(varID), b, BlockStart)
- } else {
- state.updateVar(VarID(varID), b, BlockEnd)
- }
+ for _, varID := range state.changedVars.contents() {
+ state.updateVar(VarID(varID), b, BlockEnd)
}
prevBlock = b
@@ -1554,7 +1595,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
return true
}
-// BuildFuncDebugNoOptimized constructs a FuncDebug object with
+// BuildFuncDebugNoOptimized populates a FuncDebug object "rval" with
// entries corresponding to the register-resident input parameters for
// the function "f"; it is used when we are compiling without
// optimization but the register ABI is enabled. For each reg param,
@@ -1562,8 +1603,7 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
// the input register, and the second element holds the stack location
// of the param (the assumption being that when optimization is off,
// each input param reg will be spilled in the prolog.
-func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32) *FuncDebug {
- fd := FuncDebug{}
+func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type.FuncType())
@@ -1577,7 +1617,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
}
if numRegParams == 0 {
- return &fd
+ return
}
state := debugState{f: f}
@@ -1587,7 +1627,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
// Allocate location lists.
- fd.LocationLists = make([][]byte, numRegParams)
+ rval.LocationLists = make([][]byte, numRegParams)
// Locate the value corresponding to the last spill of
// an input register.
@@ -1603,10 +1643,10 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
n := inp.Name.(*ir.Name)
sl := LocalSlot{N: n, Type: inp.Type, Off: 0}
- fd.Vars = append(fd.Vars, n)
- fd.Slots = append(fd.Slots, sl)
- slid := len(fd.VarSlots)
- fd.VarSlots = append(fd.VarSlots, []SlotID{SlotID(slid)})
+ rval.Vars = append(rval.Vars, n)
+ rval.Slots = append(rval.Slots, sl)
+ slid := len(rval.VarSlots)
+ rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
if afterPrologVal == ID(-1) {
// This can happen for degenerate functions with infinite
@@ -1623,7 +1663,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// Param is arriving in one or more registers. We need a 2-element
// location expression for it. First entry in location list
// will correspond to lifetime in input registers.
- list, sizeIdx := setupLocList(ctxt, f, fd.LocationLists[pidx],
+ list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
BlockStart.ID, afterPrologVal)
if list == nil {
pidx++
@@ -1649,7 +1689,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
if len(inp.Registers) > 1 {
list = append(list, dwarf.DW_OP_piece)
- ts := rtypes[k].Width
+ ts := rtypes[k].Size()
list = dwarf.AppendUleb128(list, uint64(ts))
if padding[k] > 0 {
if loggingEnabled {
@@ -1688,8 +1728,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// fill in size
ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
- fd.LocationLists[pidx] = list
+ rval.LocationLists[pidx] = list
pidx++
}
- return &fd
}
diff --git a/src/cmd/compile/internal/ssa/debug_lines_test.go b/src/cmd/compile/internal/ssa/debug_lines_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c0ccdb1c93e32281fa41479546ff237d60831189
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/debug_lines_test.go
@@ -0,0 +1,258 @@
+// 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 ssa_test
+
+import (
+ "bufio"
+ "bytes"
+ "flag"
+ "internal/buildcfg"
+ "runtime"
+ "sort"
+
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "strconv"
+ "testing"
+)
+
+// Matches lines in genssa output that are marked "isstmt", and the parenthesized plus-prefixed line number is a submatch
+var asmLine *regexp.Regexp = regexp.MustCompile(`^\s[vb][0-9]+\s+[0-9]+\s\(\+([0-9]+)\)`)
+
+// this matches e.g. ` v123456789 000007 (+9876654310) MOVUPS X15, ""..autotmp_2-32(SP)`
+
+// Matches lines in genssa output that describe an inlined file.
+// Note it expects an unadventurous choice of basename.
+var sepRE = regexp.QuoteMeta(string(filepath.Separator))
+var inlineLine *regexp.Regexp = regexp.MustCompile(`^#\s.*` + sepRE + `[-a-zA-Z0-9_]+\.go:([0-9]+)`)
+
+// this matches e.g. # /pa/inline-dumpxxxx.go:6
+
+var testGoArchFlag = flag.String("arch", "", "run test for specified architecture")
+
+func testGoArch() string {
+ if *testGoArchFlag == "" {
+ return runtime.GOARCH
+ }
+ return *testGoArchFlag
+}
+
+func TestDebugLinesSayHi(t *testing.T) {
+ // This test is potentially fragile, the goal is that debugging should step properly through "sayhi"
+ // If the blocks are reordered in a way that changes the statement order but execution flows correctly,
+ // then rearrange the expected numbers. Register abi and not-register-abi also have different sequences,
+ // at least for now.
+
+ switch testGoArch() {
+ case "arm64", "amd64": // register ABI
+ testDebugLines(t, "-N -l", "sayhi.go", "sayhi", []int{8, 9, 10, 11}, false)
+
+ case "arm", "386": // probably not register ABI for a while
+ testDebugLines(t, "-N -l", "sayhi.go", "sayhi", []int{9, 10, 11}, false)
+
+ default: // expect ppc64le and riscv will pick up register ABI soonish, not sure about others
+ t.Skip("skipped for many architectures, also changes w/ register ABI")
+ }
+}
+
+func TestDebugLinesPushback(t *testing.T) {
+ if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows.
+ t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin")
+ }
+
+ switch testGoArch() {
+ default:
+ t.Skip("skipped for many architectures")
+
+ case "arm64", "amd64": // register ABI
+ fn := "(*List[go.shape.int_0]).PushBack"
+ if buildcfg.Experiment.Unified {
+ // Unified mangles differently
+ fn = "(*List[int]).PushBack"
+ }
+ testDebugLines(t, "-N -l -G=3", "pushback.go", fn, []int{17, 18, 19, 20, 21, 22, 24}, true)
+ }
+}
+
+func TestDebugLinesConvert(t *testing.T) {
+ if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { // in particular, it could be windows.
+ t.Skip("this test depends on creating a file with a wonky name, only works for sure on Linux and Darwin")
+ }
+
+ switch testGoArch() {
+ default:
+ t.Skip("skipped for many architectures")
+
+ case "arm64", "amd64": // register ABI
+ fn := "G[go.shape.int_0]"
+ if buildcfg.Experiment.Unified {
+ // Unified mangles differently
+ fn = "G[int]"
+ }
+ testDebugLines(t, "-N -l -G=3", "convertline.go", fn, []int{9, 10, 11}, true)
+ }
+}
+
+func TestInlineLines(t *testing.T) {
+ if runtime.GOARCH != "amd64" && *testGoArchFlag == "" {
+ // As of september 2021, works for everything except mips64, but still potentially fragile
+ t.Skip("only runs for amd64 unless -arch explicitly supplied")
+ }
+
+ want := [][]int{{3}, {4, 10}, {4, 10, 16}, {4, 10}, {4, 11, 16}, {4, 11}, {4}, {5, 10}, {5, 10, 16}, {5, 10}, {5, 11, 16}, {5, 11}, {5}}
+ testInlineStack(t, "inline-dump.go", "f", want)
+}
+
+func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte {
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir, err := ioutil.TempDir("", "debug_lines_test")
+ if err != nil {
+ panic(fmt.Sprintf("Problem creating TempDir, error %v", err))
+ }
+ if testing.Verbose() {
+ fmt.Printf("Preserving temporary directory %s\n", tmpdir)
+ } else {
+ defer os.RemoveAll(tmpdir)
+ }
+
+ source, err := filepath.Abs(filepath.Join("testdata", file))
+ if err != nil {
+ panic(fmt.Sprintf("Could not get abspath of testdata directory and file, %v", err))
+ }
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "foo.o", "-gcflags=-d=ssa/genssa/dump="+function+" "+moreGCFlags, source)
+ cmd.Dir = tmpdir
+ cmd.Env = replaceEnv(cmd.Env, "GOSSADIR", tmpdir)
+ testGoos := "linux" // default to linux
+ if testGoArch() == "wasm" {
+ testGoos = "js"
+ }
+ cmd.Env = replaceEnv(cmd.Env, "GOOS", testGoos)
+ cmd.Env = replaceEnv(cmd.Env, "GOARCH", testGoArch())
+
+ if testing.Verbose() {
+ fmt.Printf("About to run %s\n", asCommandLine("", cmd))
+ }
+
+ var stdout, stderr bytes.Buffer
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ if err := cmd.Run(); err != nil {
+ t.Fatalf("error running cmd %s: %v\nstdout:\n%sstderr:\n%s\n", asCommandLine("", cmd), err, stdout.String(), stderr.String())
+ }
+
+ if s := stderr.String(); s != "" {
+ t.Fatalf("Wanted empty stderr, instead got:\n%s\n", s)
+ }
+
+ dumpFile := filepath.Join(tmpdir, function+"_01__genssa.dump")
+ dumpBytes, err := os.ReadFile(dumpFile)
+ if err != nil {
+ t.Fatalf("Could not read dump file %s, err=%v", dumpFile, err)
+ }
+ return dumpBytes
+}
+
+func sortInlineStacks(x [][]int) {
+ sort.Slice(x, func(i, j int) bool {
+ if len(x[i]) != len(x[j]) {
+ return len(x[i]) < len(x[j])
+ }
+ for k := range x[i] {
+ if x[i][k] != x[j][k] {
+ return x[i][k] < x[j][k]
+ }
+ }
+ return false
+ })
+}
+
+// testInlineStack ensures that inlining is described properly in the comments in the dump file
+func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) {
+ // this is an inlining reporting test, not an optimization test. -N makes it less fragile
+ dumpBytes := compileAndDump(t, file, function, "-N")
+ dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
+ dumpLineNum := 0
+ var gotStmts []int
+ var gotStacks [][]int
+ for dump.Scan() {
+ line := dump.Text()
+ dumpLineNum++
+ matches := inlineLine.FindStringSubmatch(line)
+ if len(matches) == 2 {
+ stmt, err := strconv.ParseInt(matches[1], 10, 32)
+ if err != nil {
+ t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err)
+ }
+ if testing.Verbose() {
+ fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line)
+ }
+ gotStmts = append(gotStmts, int(stmt))
+ } else if len(gotStmts) > 0 {
+ gotStacks = append(gotStacks, gotStmts)
+ gotStmts = nil
+ }
+ }
+ if len(gotStmts) > 0 {
+ gotStacks = append(gotStacks, gotStmts)
+ gotStmts = nil
+ }
+ sortInlineStacks(gotStacks)
+ sortInlineStacks(wantStacks)
+ if !reflect.DeepEqual(wantStacks, gotStacks) {
+ t.Errorf("wanted inlines %+v but got %+v", wantStacks, gotStacks)
+ }
+
+}
+
+// testDebugLines compiles testdata/ with flags -N -l and -d=ssa/genssa/dump=
+// then verifies that the statement-marked lines in that file are the same as those in wantStmts
+// These files must all be short because this is super-fragile.
+// "go build" is run in a temporary directory that is normally deleted, unless -test.v
+func testDebugLines(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) {
+ dumpBytes := compileAndDump(t, file, function, gcflags)
+ dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
+ var gotStmts []int
+ dumpLineNum := 0
+ for dump.Scan() {
+ line := dump.Text()
+ dumpLineNum++
+ matches := asmLine.FindStringSubmatch(line)
+ if len(matches) == 2 {
+ stmt, err := strconv.ParseInt(matches[1], 10, 32)
+ if err != nil {
+ t.Fatalf("Expected to parse a line number but saw %s instead on dump line #%d, error %v", matches[1], dumpLineNum, err)
+ }
+ if testing.Verbose() {
+ fmt.Printf("Saw stmt# %d for submatch '%s' on dump line #%d = '%s'\n", stmt, matches[1], dumpLineNum, line)
+ }
+ gotStmts = append(gotStmts, int(stmt))
+ }
+ }
+ if ignoreRepeats { // remove repeats from gotStmts
+ newGotStmts := []int{gotStmts[0]}
+ for _, x := range gotStmts {
+ if x != newGotStmts[len(newGotStmts)-1] {
+ newGotStmts = append(newGotStmts, x)
+ }
+ }
+ if !reflect.DeepEqual(wantStmts, newGotStmts) {
+ t.Errorf("wanted stmts %v but got %v (with repeats still in: %v)", wantStmts, newGotStmts, gotStmts)
+ }
+
+ } else {
+ if !reflect.DeepEqual(wantStmts, gotStmts) {
+ t.Errorf("wanted stmts %v but got %v", wantStmts, gotStmts)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go
index 33463125424f27b8185ca2c7cd392894cdedf4cf..b20041c1b575a02926aefcf591c5993fe16c349a 100644
--- a/src/cmd/compile/internal/ssa/debug_test.go
+++ b/src/cmd/compile/internal/ssa/debug_test.go
@@ -952,6 +952,9 @@ func (s *ioState) readSimpleExpecting(expectedRE string) tstring {
// replaceEnv returns a new environment derived from env
// by removing any existing definition of ev and adding ev=evv.
func replaceEnv(env []string, ev string, evv string) []string {
+ if env == nil {
+ env = os.Environ()
+ }
evplus := ev + "="
var found bool
for i, v := range env {
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index 7e973ab20591fb8ae42eddcc542f6ca6c1c67451..a3cea855f2fa456a31e18ae0560d9d773ca5f689 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -24,7 +24,7 @@ type selKey struct {
type Abi1RO uint8 // An offset within a parameter's slice of register indices, for abi1.
func isBlockMultiValueExit(b *Block) bool {
- return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && len(b.Controls) > 0 && b.Controls[0].Op == OpMakeResult
+ return (b.Kind == BlockRet || b.Kind == BlockRetJmp) && b.Controls[0] != nil && b.Controls[0].Op == OpMakeResult
}
func badVal(s string, v *Value) error {
@@ -176,7 +176,7 @@ func (c *registerCursor) hasRegs() bool {
type expandState struct {
f *Func
abi1 *abi.ABIConfig
- debug bool
+ debug int // odd values log lost statement markers, so likely settings are 1 (stmts), 2 (expansion), and 3 (both)
canSSAType func(*types.Type) bool
regSize int64
sp *Value
@@ -215,7 +215,7 @@ func (x *expandState) isAlreadyExpandedAggregateType(t *types.Type) bool {
return false
}
return t.IsStruct() || t.IsArray() || t.IsComplex() || t.IsInterface() || t.IsString() || t.IsSlice() ||
- t.Size() > x.regSize && t.IsInteger()
+ (t.Size() > x.regSize && (t.IsInteger() || (x.f.Config.SoftFloat && t.IsFloat())))
}
// offsetFrom creates an offset from a pointer, simplifying chained offsets and offsets from SP
@@ -302,7 +302,7 @@ func (x *expandState) Printf(format string, a ...interface{}) (n int, err error)
//
// TODO when registers really arrive, must also decompose anything split across two registers or registers and memory.
func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64, regOffset Abi1RO) []*LocalSlot {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("rewriteSelect(%s; %s; memOff=%d; regOff=%d)\n", leaf.LongString(), selector.LongString(), offset, regOffset)
@@ -325,7 +325,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
} else {
x.f.Fatalf("Unexpected %s type, selector=%s, leaf=%s\n", selector.Op.String(), selector.LongString(), leaf.LongString())
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("---%s, break\n", selector.Op.String())
}
case OpArg:
@@ -335,7 +335,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
} else {
x.f.Fatalf("Unexpected OpArg type, selector=%s, leaf=%s\n", selector.LongString(), leaf.LongString())
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("---OpArg, break\n")
}
break
@@ -380,6 +380,12 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
// The OpLoad was created to load the single field of the IData
// This case removes that StructSelect.
if leafType != selector.Type {
+ if x.f.Config.SoftFloat && selector.Type.IsFloat() {
+ if x.debug > 1 {
+ x.Printf("---OpLoad, break\n")
+ }
+ break // softfloat pass will take care of that
+ }
x.f.Fatalf("Unexpected Load as selector, leaf=%s, selector=%s\n", leaf.LongString(), selector.LongString())
}
leaf.copyOf(selector)
@@ -462,7 +468,7 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
} else {
w := call.Block.NewValue2(leaf.Pos, OpLoad, leafType, off, call)
leaf.copyOf(w)
- if x.debug {
+ if x.debug > 1 {
x.Printf("---new %s\n", w.LongString())
}
}
@@ -525,11 +531,11 @@ func (x *expandState) rewriteSelect(leaf *Value, selector *Value, offset int64,
case OpComplexReal:
ls := x.rewriteSelect(leaf, selector.Args[0], offset, regOffset)
- locs = x.splitSlots(ls, ".real", 0, leafType)
+ locs = x.splitSlots(ls, ".real", 0, selector.Type)
case OpComplexImag:
- ls := x.rewriteSelect(leaf, selector.Args[0], offset+leafType.Width, regOffset+RO_complex_imag) // result is FloatNN, width of result is offset of imaginary part.
- locs = x.splitSlots(ls, ".imag", leafType.Width, leafType)
+ ls := x.rewriteSelect(leaf, selector.Args[0], offset+selector.Type.Size(), regOffset+RO_complex_imag) // result is FloatNN, width of result is offset of imaginary part.
+ locs = x.splitSlots(ls, ".imag", selector.Type.Size(), selector.Type)
case OpStringLen, OpSliceLen:
ls := x.rewriteSelect(leaf, selector.Args[0], offset+x.ptrSize, regOffset+RO_slice_len)
@@ -610,7 +616,7 @@ outer:
}
return path
case types.TINT64, types.TUINT64:
- if container.Width == x.regSize {
+ if container.Size() == x.regSize {
return path
}
if offset == x.hiOffset {
@@ -676,19 +682,19 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
for i := 0; i < len(rts); i++ {
rt := rts[i]
off := offs[i]
- fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Width, rt.Align)
+ fmt.Printf("rt=%s, off=%d, rt.Width=%d, rt.Align=%d\n", rt.String(), off, rt.Size(), uint8(rt.Alignment()))
}
panic(fmt.Errorf("offset %d of requested register %d should be zero, source=%s", offs[loadRegOffset], loadRegOffset, source.LongString()))
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("decompose arg %s has %d locs\n", source.LongString(), len(locs))
}
for i := loadRegOffset; i < last; i++ {
rt := rts[i]
off := offs[i]
- w := x.commonArgs[selKey{source, off, rt.Width, rt}]
+ w := x.commonArgs[selKey{source, off, rt.Size(), rt}]
if w == nil {
w = x.newArgToMemOrRegs(source, w, off, i, rt, pos)
suffix := x.pathTo(source.Type, rt, off)
@@ -699,7 +705,7 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
if t.IsPtrShaped() {
// Preserve the original store type. This ensures pointer type
// properties aren't discarded (e.g, notinheap).
- if rt.Width != t.Width || len(pa.Registers) != 1 || i != loadRegOffset {
+ if rt.Size() != t.Size() || len(pa.Registers) != 1 || i != loadRegOffset {
b.Func.Fatalf("incompatible store type %v and %v, i=%d", t, rt, i)
}
rt = t
@@ -730,7 +736,7 @@ func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t
}
return mem
case types.TINT64, types.TUINT64:
- if t.Width == x.regSize {
+ if t.Size() == x.regSize {
break
}
tHi, tLo := x.intPairTypes(t.Kind())
@@ -804,7 +810,7 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value,
}
return mem
case types.TINT64, types.TUINT64:
- if t.Width == x.regSize {
+ if t.Size() == x.regSize {
break
}
tHi, tLo := x.intPairTypes(t.Kind())
@@ -830,13 +836,13 @@ func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value,
// pos and b locate the store instruction, source is the "base" of the value input,
// mem is the input mem, t is the type in question, and offArg and offStore are the offsets from the respective bases.
func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suffix string, source, mem *Value, t *types.Type, argOffset, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("storeOneArg(%s; %s; %s; aO=%d; sO=%d; lrO=%d; %s)\n", source.LongString(), mem.String(), t.String(), argOffset, storeOffset, loadRegOffset, storeRc.String())
}
- w := x.commonArgs[selKey{source, argOffset, t.Width, t}]
+ w := x.commonArgs[selKey{source, argOffset, t.Size(), t}]
if w == nil {
w = x.newArgToMemOrRegs(source, w, argOffset, loadRegOffset, t, pos)
x.splitSlotsIntoNames(locs, suffix, argOffset, t, w)
@@ -846,7 +852,7 @@ func storeOneArg(x *expandState, pos src.XPos, b *Block, locs []*LocalSlot, suff
// storeOneLoad creates a decomposed (one step) load that is then stored.
func storeOneLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t *types.Type, offArg, offStore int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
- from := x.offsetFrom(b, source.Args[0], offArg, types.NewPtr(t))
+ from := x.offsetFrom(source.Block, source.Args[0], offArg, types.NewPtr(t))
w := source.Block.NewValue2(source.Pos, OpLoad, t, from, mem)
return x.storeArgOrLoad(pos, b, w, mem, t, offStore, loadRegOffset, storeRc)
}
@@ -871,7 +877,7 @@ func storeTwoLoad(x *expandState, pos src.XPos, b *Block, source, mem *Value, t1
// stores of non-aggregate types. It recursively walks up a chain of selectors until it reaches a Load or an Arg.
// If it does not reach a Load or an Arg, nothing happens; this allows a little freedom in phase ordering.
func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("storeArgOrLoad(%s; %s; %s; %d; %s)\n", source.LongString(), mem.String(), t.String(), storeOffset, storeRc.String())
@@ -917,7 +923,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
case OpComplexMake:
tPart := x.typs.Float32
- wPart := t.Width / 2
+ wPart := t.Size() / 2
if wPart == 8 {
tPart = x.typs.Float64
}
@@ -946,22 +952,23 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
switch t.Kind() {
case types.TARRAY:
elt := t.Elem()
- if source.Type != t && t.NumElem() == 1 && elt.Width == t.Width && t.Width == x.regSize {
+ if source.Type != t && t.NumElem() == 1 && elt.Size() == t.Size() && t.Size() == x.regSize {
t = removeTrivialWrapperTypes(t)
// it could be a leaf type, but the "leaf" could be complex64 (for example)
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.Width, loadRegOffset, storeRc.at(t, 0))
+ mem = x.storeArgOrLoad(pos, b, sel, mem, elt, storeOffset+i*elt.Size(), loadRegOffset, storeRc.at(t, 0))
loadRegOffset += eltRO
pos = pos.WithNotStmt()
}
return mem
case types.TSTRUCT:
- if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Width == t.Width && t.Width == x.regSize {
+ if source.Type != t && t.NumFields() == 1 && t.Field(0).Type.Size() == t.Size() && t.Size() == x.regSize {
// This peculiar test deals with accesses to immediate interface data.
// It works okay because everything is the same size.
// Example code that triggers this can be found in go/constant/value.go, function ToComplex
@@ -985,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)
@@ -995,7 +1003,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
return mem
case types.TINT64, types.TUINT64:
- if t.Width == x.regSize {
+ if t.Size() == x.regSize {
break
}
tHi, tLo := x.intPairTypes(t.Kind())
@@ -1054,7 +1062,7 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
dst := x.offsetFrom(b, storeRc.storeDest, storeOffset, types.NewPtr(t))
s = b.NewValue3A(pos, OpStore, types.TypeMem, t, dst, source, mem)
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("-->storeArg returns %s, storeRc=%s\n", s.LongString(), storeRc.String())
}
return s
@@ -1065,18 +1073,23 @@ func (x *expandState) storeArgOrLoad(pos src.XPos, b *Block, source, mem *Value,
// to account for any parameter stores required.
// Any of the old Args that have their use count fall to zero are marked OpInvalid.
func (x *expandState) rewriteArgs(v *Value, firstArg int) {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("rewriteArgs(%s; %d)\n", v.LongString(), firstArg)
}
// Thread the stores on the memory arg
aux := v.Aux.(*AuxCall)
- pos := v.Pos.WithNotStmt()
m0 := v.MemoryArg()
mem := m0
newArgs := []*Value{}
oldArgs := []*Value{}
+ sp := x.sp
+ if v.Op == OpTailLECall {
+ // For tail call, we unwind the frame before the call so we'll use the caller's
+ // SP.
+ sp = x.f.Entry.NewValue0(src.NoXPos, OpGetCallerSP, x.typs.Uintptr)
+ }
for i, a := range v.Args[firstArg : len(v.Args)-1] { // skip leading non-parameter SSA Args and trailing mem SSA Arg.
oldArgs = append(oldArgs, a)
auxI := int64(i)
@@ -1087,9 +1100,20 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
if a.MemoryArg() != m0 {
x.f.Fatalf("Op...LECall and OpDereference have mismatched mem, %s and %s", v.LongString(), a.LongString())
}
+ if v.Op == OpTailLECall {
+ // It's common for a tail call passing the same arguments (e.g. method wrapper),
+ // so this would be a self copy. Detect this and optimize it out.
+ a0 := a.Args[0]
+ if a0.Op == OpLocalAddr {
+ n := a0.Aux.(*ir.Name)
+ if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+ continue
+ }
+ }
+ }
// "Dereference" of addressed (probably not-SSA-eligible) value becomes Move
// TODO(register args) this will be more complicated with registers in the picture.
- mem = x.rewriteDereference(v.Block, x.sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, pos)
+ mem = x.rewriteDereference(v.Block, sp, a, mem, aOffset, aux.SizeOfArg(auxI), aType, a.Pos)
} else {
var rc registerCursor
var result *[]*Value
@@ -1099,11 +1123,19 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
} else {
aOffset = aux.OffsetOfArg(auxI)
}
- if x.debug {
+ if v.Op == OpTailLECall && a.Op == OpArg && a.AuxInt == 0 {
+ // It's common for a tail call passing the same arguments (e.g. method wrapper),
+ // so this would be a self copy. Detect this and optimize it out.
+ n := a.Aux.(*ir.Name)
+ if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+ continue
+ }
+ }
+ if x.debug > 1 {
x.Printf("...storeArg %s, %v, %d\n", a.LongString(), aType, aOffset)
}
- rc.init(aRegs, aux.abiInfo, result, x.sp)
- mem = x.storeArgOrLoad(pos, v.Block, a, mem, aType, aOffset, 0, rc)
+ rc.init(aRegs, aux.abiInfo, result, sp)
+ mem = x.storeArgOrLoad(a.Pos, v.Block, a, mem, aType, aOffset, 0, rc)
}
}
var preArgStore [2]*Value
@@ -1114,16 +1146,31 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
v.AddArg(mem)
for _, a := range oldArgs {
if a.Uses == 0 {
- if x.debug {
- x.Printf("...marking %v unused\n", a.LongString())
- }
- a.invalidateRecursively()
+ x.invalidateRecursively(a)
}
}
return
}
+func (x *expandState) invalidateRecursively(a *Value) {
+ var s string
+ if x.debug > 0 {
+ plus := " "
+ if a.Pos.IsStmt() == src.PosIsStmt {
+ plus = " +"
+ }
+ s = a.String() + plus + a.Pos.LineNumber() + " " + a.LongString()
+ if x.debug > 1 {
+ x.Printf("...marking %v unused\n", s)
+ }
+ }
+ lost := a.invalidateRecursively()
+ if x.debug&1 != 0 && lost { // For odd values of x.debug, do this.
+ x.Printf("Lost statement marker in %s on former %s\n", base.Ctxt.Pkgpath+"."+x.f.Name, s)
+ }
+}
+
// expandCalls converts LE (Late Expansion) calls that act like they receive value args into a lower-level form
// that is more oriented to a platform's ABI. The SelectN operations that extract results are rewritten into
// more appropriate forms, and any StructMake or ArrayMake inputs are decomposed until non-struct values are
@@ -1142,7 +1189,7 @@ func expandCalls(f *Func) {
x := &expandState{
f: f,
abi1: f.ABI1,
- debug: f.pass.debug > 0,
+ debug: f.pass.debug,
canSSAType: f.fe.CanSSA,
regSize: f.Config.RegSize,
sp: sp,
@@ -1164,7 +1211,7 @@ func expandCalls(f *Func) {
x.loRo, x.hiRo = 0, 1
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("\nexpandsCalls(%s)\n", f.Name)
}
@@ -1187,7 +1234,7 @@ func expandCalls(f *Func) {
for _, v := range b.Values {
firstArg := 0
switch v.Op {
- case OpStaticLECall:
+ case OpStaticLECall, OpTailLECall:
case OpInterLECall:
firstArg = 1
case OpClosureLECall:
@@ -1204,9 +1251,8 @@ func expandCalls(f *Func) {
m0 := v.MemoryArg()
mem := m0
aux := f.OwnAux
- pos := v.Pos.WithNotStmt()
allResults := []*Value{}
- if x.debug {
+ if x.debug > 1 {
x.Printf("multiValueExit rewriting %s\n", v.LongString())
}
var oldArgs []*Value
@@ -1227,7 +1273,7 @@ func expandCalls(f *Func) {
}
continue
}
- mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, pos)
+ mem = x.rewriteDereference(v.Block, auxBase, a, mem, auxOffset, auxSize, auxType, a.Pos)
} else {
if a.Op == OpLoad && a.Args[0].Op == OpLocalAddr {
addr := a.Args[0] // This is a self-move. // TODO(register args) do what here for registers?
@@ -1251,13 +1297,13 @@ func expandCalls(f *Func) {
b.SetControl(v)
for _, a := range oldArgs {
if a.Uses == 0 {
- if x.debug {
+ if x.debug > 1 {
x.Printf("...marking %v unused\n", a.LongString())
}
- a.invalidateRecursively()
+ x.invalidateRecursively(a)
}
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("...multiValueExit new result %s\n", v.LongString())
}
x.indent(-3)
@@ -1311,7 +1357,7 @@ func expandCalls(f *Func) {
switch w.Op {
case OpStructSelect, OpArraySelect, OpSelectN, OpArg:
val2Preds[w] += 1
- if x.debug {
+ if x.debug > 1 {
x.Printf("v2p[%s] = %d\n", w.LongString(), val2Preds[w])
}
}
@@ -1320,7 +1366,7 @@ func expandCalls(f *Func) {
case OpSelectN:
if _, ok := val2Preds[v]; !ok {
val2Preds[v] = 0
- if x.debug {
+ if x.debug > 1 {
x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
}
}
@@ -1331,7 +1377,7 @@ func expandCalls(f *Func) {
}
if _, ok := val2Preds[v]; !ok {
val2Preds[v] = 0
- if x.debug {
+ if x.debug > 1 {
x.Printf("v2p[%s] = %d\n", v.LongString(), val2Preds[v])
}
}
@@ -1416,7 +1462,7 @@ func expandCalls(f *Func) {
if typ.IsMemory() {
continue // handled elsewhere, not an indexable result
}
- size := typ.Width
+ size := typ.Size()
offset := int64(0)
switch v.Op {
case OpStructSelect:
@@ -1445,7 +1491,7 @@ func expandCalls(f *Func) {
if dupe == nil {
x.commonSelectors[sk] = v
} else if x.sdom.IsAncestorEq(dupe.Block, v.Block) {
- if x.debug {
+ if x.debug > 1 {
x.Printf("Duplicate, make %s copy of %s\n", v, dupe)
}
v.copyOf(dupe)
@@ -1461,12 +1507,12 @@ func expandCalls(f *Func) {
// Rewrite selectors.
for i, v := range allOrdered {
- if x.debug {
+ if x.debug > 1 {
b := v.Block
x.Printf("allOrdered[%d] = b%d, %s, uses=%d\n", i, b.ID, v.LongString(), v.Uses)
}
if v.Uses == 0 {
- v.invalidateRecursively()
+ x.invalidateRecursively(v)
continue
}
if v.Op == OpCopy {
@@ -1506,6 +1552,10 @@ func expandCalls(f *Func) {
v.Op = OpStaticCall
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
v.Type = types.NewResults(append(rts, types.TypeMem))
+ case OpTailLECall:
+ v.Op = OpTailCall
+ rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
+ v.Type = types.NewResults(append(rts, types.TypeMem))
case OpClosureLECall:
v.Op = OpClosureCall
rts := abi.RegisterTypes(v.Aux.(*AuxCall).abiInfo.OutParams())
@@ -1528,7 +1578,7 @@ func expandCalls(f *Func) {
case OpArgIntReg:
i := v.AuxInt
if w := IArg[i]; w != nil {
- if w.Type.Width != v.Type.Width {
+ if w.Type.Size() != v.Type.Size() {
f.Fatalf("incompatible OpArgIntReg [%d]: %s and %s", i, v.LongString(), w.LongString())
}
if w.Type.IsUnsafePtr() && !v.Type.IsUnsafePtr() {
@@ -1543,7 +1593,7 @@ func expandCalls(f *Func) {
case OpArgFloatReg:
i := v.AuxInt
if w := FArg[i]; w != nil {
- if w.Type.Width != v.Type.Width {
+ if w.Type.Size() != v.Type.Size() {
f.Fatalf("incompatible OpArgFloatReg [%d]: %v and %v", i, v, w)
}
v.copyOf(w)
@@ -1577,7 +1627,7 @@ func expandCalls(f *Func) {
v.SetArg(i, aa)
for a.Uses == 0 {
b := a.Args[0]
- a.invalidateRecursively()
+ x.invalidateRecursively(a)
a = b
}
}
@@ -1613,7 +1663,7 @@ func expandCalls(f *Func) {
// rewriteArgToMemOrRegs converts OpArg v in-place into the register version of v,
// if that is appropriate.
func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("rewriteArgToMemOrRegs(%s)\n", v.LongString())
@@ -1628,9 +1678,9 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
}
case 1:
t := v.Type
- key := selKey{v, 0, t.Width, t}
+ key := selKey{v, 0, t.Size(), t}
w := x.commonArgs[key]
- if w != nil {
+ if w != nil && w.Uses != 0 { // do not reuse dead value
v.copyOf(w)
break
}
@@ -1644,7 +1694,7 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
default:
panic(badVal("Saw unexpanded OpArg", v))
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("-->%s\n", v.LongString())
}
return v
@@ -1654,16 +1704,22 @@ func (x *expandState) rewriteArgToMemOrRegs(v *Value) *Value {
// or rewrites it into a copy of the appropriate OpArgXXX. The actual OpArgXXX is determined by combining baseArg (an OpArg)
// with offset, regOffset, and t to determine which portion of it to reference (either all or a part, in memory or in registers).
func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64, regOffset Abi1RO, t *types.Type, pos src.XPos) *Value {
- if x.debug {
+ if x.debug > 1 {
x.indent(3)
defer x.indent(-3)
x.Printf("newArgToMemOrRegs(base=%s; toReplace=%s; t=%s; memOff=%d; regOff=%d)\n", baseArg.String(), toReplace.LongString(), t.String(), offset, regOffset)
}
- key := selKey{baseArg, offset, t.Width, t}
+ key := selKey{baseArg, offset, t.Size(), t}
w := x.commonArgs[key]
- if w != nil {
+ if w != nil && w.Uses != 0 { // do not reuse dead value
if toReplace != nil {
toReplace.copyOf(w)
+ if x.debug > 1 {
+ x.Printf("...replace %s\n", toReplace.LongString())
+ }
+ }
+ if x.debug > 1 {
+ x.Printf("-->%s\n", w.LongString())
}
return w
}
@@ -1690,7 +1746,7 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
if toReplace != nil {
toReplace.copyOf(w)
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("-->%s\n", w.LongString())
}
return w
@@ -1721,7 +1777,7 @@ func (x *expandState) newArgToMemOrRegs(baseArg, toReplace *Value, offset int64,
if toReplace != nil {
toReplace.copyOf(w)
}
- if x.debug {
+ if x.debug > 1 {
x.Printf("-->%s\n", w.LongString())
}
return w
diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
index 8ed8a0c4a6e3b725f5f621b0ea8855a3a6b9bac7..c4e87ec7d0f9e8c74d5ff10703f27f09b5e5fa01 100644
--- a/src/cmd/compile/internal/ssa/export_test.go
+++ b/src/cmd/compile/internal/ssa/export_test.go
@@ -5,14 +5,16 @@
package ssa
import (
+ "testing"
+
"cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/arm64"
"cmd/internal/obj/s390x"
"cmd/internal/obj/x86"
"cmd/internal/src"
- "testing"
)
var CheckFunc = checkFunc
@@ -39,7 +41,7 @@ func testConfigArch(tb testing.TB, arch string) *Conf {
tb.Fatal("testTypes is 64-bit only")
}
c := &Conf{
- config: NewConfig(arch, testTypes, ctxt, true),
+ config: NewConfig(arch, testTypes, ctxt, true, false),
tb: tb,
}
return c
@@ -104,33 +106,12 @@ func (d TestFrontend) MyImportPath() string {
var testTypes Types
func init() {
- // Initialize just enough of the universe and the types package to make our tests function.
- // TODO(josharian): move universe initialization to the types package,
- // so this test setup can share it.
-
- for _, typ := range [...]struct {
- width int64
- et types.Kind
- }{
- {1, types.TINT8},
- {1, types.TUINT8},
- {1, types.TBOOL},
- {2, types.TINT16},
- {2, types.TUINT16},
- {4, types.TINT32},
- {4, types.TUINT32},
- {4, types.TFLOAT32},
- {4, types.TFLOAT64},
- {8, types.TUINT64},
- {8, types.TINT64},
- {8, types.TINT},
- {8, types.TUINTPTR},
- } {
- t := types.New(typ.et)
- t.Width = typ.width
- t.Align = uint8(typ.width)
- types.Types[typ.et] = t
- }
+ // TODO(mdempsky): Push into types.InitUniverse or typecheck.InitUniverse.
+ types.PtrSize = 8
+ types.RegSize = 8
+ types.MaxWidth = 1 << 50
+
+ typecheck.InitUniverse()
testTypes.SetTypPtrs()
}
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index fac876c23ebc2eede4fabfc5a085a962bb99f11b..7728a395e05cb666e242631d1e219570623cc3b2 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -43,7 +43,7 @@ type Func struct {
logfiles map[string]writeSyncer
HTMLWriter *HTMLWriter // html writer, for debugging
DebugTest bool // default true unless $GOSSAHASH != ""; as a debugging aid, make new code conditional on this and use GOSSAHASH to binary search for failing cases
- PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false.
+ PrintOrHtmlSSA bool // true if GOSSAFUNC matches, true even if fe.Log() (spew phase results to stdout) is false. There's an odd dependence on this in debug.go for method logf.
ruleMatches map[string]int // number of times countRule was called during compilation for any given string
ABI0 *abi.ABIConfig // A copy, for no-sync access
ABI1 *abi.ABIConfig // A copy, for no-sync access
diff --git a/src/cmd/compile/internal/ssa/fuse_branchredirect.go b/src/cmd/compile/internal/ssa/fuse_branchredirect.go
index 1b8b307bcac7a213880b25ca64f71eff41639dc7..751dca7468bc748af777e83d3565511e002473d6 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/386.rules b/src/cmd/compile/internal/ssa/gen/386.rules
index 199b73c42f436fcfc88e422d196ffd8904780787..7bdebedafef81f6a1e1f3821e1a5fc1d59322365 100644
--- a/src/cmd/compile/internal/ssa/gen/386.rules
+++ b/src/cmd/compile/internal/ssa/gen/386.rules
@@ -317,6 +317,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// Miscellaneous
(IsNonNil p) => (SETNE (TESTL p p))
diff --git a/src/cmd/compile/internal/ssa/gen/386Ops.go b/src/cmd/compile/internal/ssa/gen/386Ops.go
index c4b49fbb23067d4e52e5940a25e6351b7adc4060..f4c89b0bb3152926b03599f1e48bf87b78d8f6ba 100644
--- a/src/cmd/compile/internal/ssa/gen/386Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/386Ops.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -297,7 +298,7 @@ func init() {
// unary ops
{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0
- {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0
+ {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
{name: "BSFL", argLength: 1, reg: gp11, asm: "BSFL", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero
{name: "BSFW", argLength: 1, reg: gp11, asm: "BSFW", clobberFlags: true}, // arg0 # of low-order zeroes ; undef if zero
@@ -454,6 +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, 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 4cd00732fc3f5e5f8a4098f6a0b0261f01c9a4a8..47a6af003c9ecd9b6d9f376d608e646cdc8d0296 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -78,15 +78,21 @@
(OffPtr [off] ptr) => (ADDQ (MOVQconst [off]) ptr)
// Lowering other arithmetic
-(Ctz64 x) => (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x)))
-(Ctz32 x) => (Select0 (BSFQ (BTSQconst [32] x)))
+(Ctz64 x) && buildcfg.GOAMD64 >= 3 => (TZCNTQ x)
+(Ctz32 x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x)
+(Ctz64 x) && buildcfg.GOAMD64 < 3 => (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x)))
+(Ctz32 x) && buildcfg.GOAMD64 < 3 => (Select0 (BSFQ (BTSQconst [32] x)))
(Ctz16 x) => (BSFL (BTSLconst [16] x))
(Ctz8 x) => (BSFL (BTSLconst [ 8] x))
-(Ctz64NonZero x) => (Select0 (BSFQ x))
-(Ctz32NonZero ...) => (BSFL ...)
-(Ctz16NonZero ...) => (BSFL ...)
-(Ctz8NonZero ...) => (BSFL ...)
+(Ctz64NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTQ x)
+(Ctz32NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x)
+(Ctz16NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x)
+(Ctz8NonZero x) && buildcfg.GOAMD64 >= 3 => (TZCNTL x)
+(Ctz64NonZero x) && buildcfg.GOAMD64 < 3 => (Select0 (BSFQ x))
+(Ctz32NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x)
+(Ctz16NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x)
+(Ctz8NonZero x) && buildcfg.GOAMD64 < 3 => (BSFL x)
// BitLen64 of a 64 bit value x requires checking whether x == 0, since BSRQ is undefined when x == 0.
// However, for zero-extended values, we can cheat a bit, and calculate
@@ -362,26 +368,26 @@
// Adjust zeros to be a multiple of 16 bytes.
(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE =>
(Zero [s-s%16] (OffPtr destptr [s%16])
- (MOVOstorezero destptr mem))
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE =>
(Zero [s-s%16] (OffPtr destptr [s%16])
- (MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
(Zero [16] destptr mem) && config.useSSE =>
- (MOVOstorezero destptr mem)
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)
(Zero [32] destptr mem) && config.useSSE =>
- (MOVOstorezero (OffPtr destptr [16])
- (MOVOstorezero destptr mem))
+ (MOVOstoreconst [makeValAndOff(0,16)] destptr
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
(Zero [48] destptr mem) && config.useSSE =>
- (MOVOstorezero (OffPtr destptr [32])
- (MOVOstorezero (OffPtr destptr [16])
- (MOVOstorezero destptr mem)))
+ (MOVOstoreconst [makeValAndOff(0,32)] destptr
+ (MOVOstoreconst [makeValAndOff(0,16)] destptr
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)))
(Zero [64] destptr mem) && config.useSSE =>
- (MOVOstorezero (OffPtr destptr [48])
- (MOVOstorezero (OffPtr destptr [32])
- (MOVOstorezero (OffPtr destptr [16])
- (MOVOstorezero destptr mem))))
+ (MOVOstoreconst [makeValAndOff(0,48)] destptr
+ (MOVOstoreconst [makeValAndOff(0,32)] destptr
+ (MOVOstoreconst [makeValAndOff(0,16)] destptr
+ (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))))
// Medium zeroing uses a duff device.
(Zero [s] destptr mem)
@@ -408,6 +414,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// Lowering conditional moves
// If the condition is a SETxx, we can just run a CMOV from the comparison that was
@@ -460,12 +467,12 @@
(IsInBounds idx len) => (SETB (CMPQ idx len))
(IsSliceInBounds idx len) => (SETBE (CMPQ idx len))
(NilCheck ...) => (LoweredNilCheck ...)
-(GetG mem) && !(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal) => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register.
+(GetG mem) && v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal => (LoweredGetG mem) // only lower in old ABI. in new ABI we have a G register.
(GetClosurePtr ...) => (LoweredGetClosurePtr ...)
(GetCallerPC ...) => (LoweredGetCallerPC ...)
(GetCallerSP ...) => (LoweredGetCallerSP ...)
-(HasCPUFeature {s}) => (SETNE (CMPQconst [0] (LoweredHasCPUFeature {s})))
+(HasCPUFeature {s}) => (SETNE (CMPLconst [0] (LoweredHasCPUFeature {s})))
(Addr {sym} base) => (LEAQ {sym} base)
(LocalAddr {sym} base _) => (LEAQ {sym} base)
@@ -638,6 +645,7 @@
// Recognize bit clearing: a &^= 1< (BTR(Q|L) x y)
+(ANDN(Q|L) x (SHL(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y)
(ANDQconst [c] x) && isUint64PowerOfTwo(int64(^c)) && uint64(^c) >= 128
=> (BTRQconst [int8(log32(^c))] x)
(ANDLconst [c] x) && isUint32PowerOfTwo(int64(^c)) && uint64(^c) >= 128
@@ -1134,8 +1142,8 @@
(MOVBstoreconst [makeValAndOff(int32(int8(c)),off)] {sym} ptr mem)
// Fold address offsets into constant stores.
-(MOV(Q|L|W|B)storeconst [sc] {s} (ADDQconst [off] ptr) mem) && ValAndOff(sc).canAdd32(off) =>
- (MOV(Q|L|W|B)storeconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem)
+(MOV(Q|L|W|B|O)storeconst [sc] {s} (ADDQconst [off] ptr) mem) && ValAndOff(sc).canAdd32(off) =>
+ (MOV(Q|L|W|B|O)storeconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem)
// We need to fold LEAQ into the MOVx ops so that the live variable analysis knows
// what variables are being read/written by the ops.
@@ -1145,8 +1153,8 @@
(MOV(Q|L|W|B|SS|SD|O)store [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
&& is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) =>
(MOV(Q|L|W|B|SS|SD|O)store [off1+off2] {mergeSym(sym1,sym2)} base val mem)
-(MOV(Q|L|W|B)storeconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off) =>
- (MOV(Q|L|W|B)storeconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
+(MOV(Q|L|W|B|O)storeconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off) =>
+ (MOV(Q|L|W|B|O)storeconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
(SET(L|G|B|A|LE|GE|BE|AE|EQ|NE)store [off1] {sym1} (LEAQ [off2] {sym2} base) val mem)
&& is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) =>
(SET(L|G|B|A|LE|GE|BE|AE|EQ|NE)store [off1+off2] {mergeSym(sym1,sym2)} base val mem)
@@ -1897,14 +1905,22 @@
&& a.Off() + 4 == c.Off()
&& clobber(x)
=> (MOVQstore [a.Off()] {s} p (MOVQconst [a.Val64()&0xffffffff | c.Val64()<<32]) mem)
-(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem))
+(MOVQstoreconst [c] {s} p x:(MOVQstoreconst [a] {s} p mem))
+ && config.useSSE
+ && x.Uses == 1
+ && a.Off() + 8 == c.Off()
+ && a.Val() == 0
+ && c.Val() == 0
+ && clobber(x)
+ => (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p mem)
+(MOVQstoreconst [a] {s} p x:(MOVQstoreconst [c] {s} p mem))
&& config.useSSE
&& x.Uses == 1
- && c2.Off() + 8 == c.Off()
+ && a.Off() + 8 == c.Off()
+ && a.Val() == 0
&& c.Val() == 0
- && c2.Val() == 0
&& clobber(x)
- => (MOVOstorezero [c2.Off()] {s} p mem)
+ => (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p mem)
// Combine stores into larger (unaligned) stores. Little endian.
(MOVBstore [i] {s} p (SHR(W|L|Q)const [8] w) x:(MOVBstore [i-1] {s} p w mem))
@@ -2013,50 +2029,6 @@
&& clobber(x1, x2, mem2)
=> (MOVQstore [i-4] {s} p (MOVQload [j-4] {s2} p2 mem) mem)
-(MOVQload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVQload [off1+off2] {mergeSym(sym1,sym2)} base mem)
-(MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem)
-(MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
-(MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
-
-(MOVQstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
-(MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
-(MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
-(MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem) && canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) =>
- (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
-
-(MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) =>
- (MOVQstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
-(MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) =>
- (MOVLstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
-(MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) =>
- (MOVWstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
-(MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem) && canMergeSym(sym1, sym2) && sc.canAdd32(off) =>
- (MOVBstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
-
-(MOVQload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVQload [off1+off2] {sym} ptr mem)
-(MOVLload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVLload [off1+off2] {sym} ptr mem)
-(MOVWload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVWload [off1+off2] {sym} ptr mem)
-(MOVBload [off1] {sym} (ADDLconst [off2] ptr) mem) && is32Bit(int64(off1)+int64(off2)) => (MOVBload [off1+off2] {sym} ptr mem)
-(MOVQstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVQstore [off1+off2] {sym} ptr val mem)
-(MOVLstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVLstore [off1+off2] {sym} ptr val mem)
-(MOVWstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVWstore [off1+off2] {sym} ptr val mem)
-(MOVBstore [off1] {sym} (ADDLconst [off2] ptr) val mem) && is32Bit(int64(off1)+int64(off2)) => (MOVBstore [off1+off2] {sym} ptr val mem)
-(MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) =>
- (MOVQstoreconst [sc.addOffset32(off)] {s} ptr mem)
-(MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) =>
- (MOVLstoreconst [sc.addOffset32(off)] {s} ptr mem)
-(MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) =>
- (MOVWstoreconst [sc.addOffset32(off)] {s} ptr mem)
-(MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem) && sc.canAdd32(off) =>
- (MOVBstoreconst [sc.addOffset32(off)] {s} ptr mem)
-
// Merge load and op
// TODO: add indexed variants?
((ADD|SUB|AND|OR|XOR)Q x l:(MOVQload [off] {sym} ptr mem)) && canMergeLoadClobber(v, l, x) && clobber(l) => ((ADD|SUB|AND|OR|XOR)Qload x [off] {sym} ptr mem)
@@ -2235,3 +2207,41 @@
&& isInlinableMemmove(dst, src, sz, config)
&& clobber(call)
=> (Move [sz] dst src mem)
+
+// Prefetch instructions
+(PrefetchCache ...) => (PrefetchT0 ...)
+(PrefetchCacheStreamed ...) => (PrefetchNTA ...)
+
+// CPUID feature: BMI1.
+(AND(Q|L) x (NOT(Q|L) y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y)
+(AND(Q|L) x (NEG(Q|L) x)) && buildcfg.GOAMD64 >= 3 => (BLSI(Q|L) x)
+(XOR(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSMSK(Q|L) x)
+(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 67b3293903cd915f5ef2e3839b806fe0230370fd..a6906bec7c74eae3768d6577524ba3661beb2382 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -169,6 +169,8 @@ func init() {
fpstore = regInfo{inputs: []regMask{gpspsb, fp, 0}}
fpstoreidx = regInfo{inputs: []regMask{gpspsb, gpsp, fp, 0}}
+
+ prefreg = regInfo{inputs: []regMask{gpspsbg}}
)
var AMD64ops = []opData{
@@ -511,8 +513,8 @@ func init() {
{name: "NEGQ", argLength: 1, reg: gp11, asm: "NEGQ", resultInArg0: true, clobberFlags: true}, // -arg0
{name: "NEGL", argLength: 1, reg: gp11, asm: "NEGL", resultInArg0: true, clobberFlags: true}, // -arg0
- {name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true, clobberFlags: true}, // ^arg0
- {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true, clobberFlags: true}, // ^arg0
+ {name: "NOTQ", argLength: 1, reg: gp11, asm: "NOTQ", resultInArg0: true}, // ^arg0
+ {name: "NOTL", argLength: 1, reg: gp11, asm: "NOTL", resultInArg0: true}, // ^arg0
// BS{F,R}Q returns a tuple [result, flags]
// result is undefined if the input is zero.
@@ -679,20 +681,19 @@ func init() {
// Note: LEAx{1,2,4,8} must not have OpSB as either argument.
// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
- {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
- {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
- {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem
- {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem
- {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
- {name: "MOVOstorezero", argLength: 2, reg: regInfo{inputs: []regMask{gpspsb, 0}}, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of zero to arg0+auxint+aux. arg1=mem
+ {name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVBLZX", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVBQSXload", argLength: 2, reg: gpload, asm: "MOVBQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVWLZX", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVWQSXload", argLength: 2, reg: gpload, asm: "MOVWQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVLload", argLength: 2, reg: gpload, asm: "MOVL", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
+ {name: "MOVLQSXload", argLength: 2, reg: gpload, asm: "MOVLQSX", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // ditto, sign extend to int64
+ {name: "MOVQload", argLength: 2, reg: gpload, asm: "MOVQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem
+ {name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store byte in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVLstore", argLength: 3, reg: gpstore, asm: "MOVL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVQstore", argLength: 3, reg: gpstore, asm: "MOVQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ {name: "MOVOload", argLength: 2, reg: fpload, asm: "MOVUPS", aux: "SymOff", typ: "Int128", faultOnNilArg0: true, symEffect: "Read"}, // load 16 bytes from arg0+auxint+aux. arg1=mem
+ {name: "MOVOstore", argLength: 3, reg: fpstore, asm: "MOVUPS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
// indexed loads/stores
{name: "MOVBloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBLZX", scale: 1, aux: "SymOff", typ: "UInt8", symEffect: "Read"}, // load a byte from arg0+arg1+auxint+aux. arg2=mem
@@ -717,10 +718,11 @@ func init() {
// For storeconst ops, the AuxInt field encodes both
// the value to store and an address offset of the store.
// Cast AuxInt to a ValAndOff to extract Val and Off fields.
- {name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem
- {name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
- {name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
- {name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
+ {name: "MOVBstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVB", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem
+ {name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
+ {name: "MOVLstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVL", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
+ {name: "MOVQstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVQ", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
+ {name: "MOVOstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVUPS", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of ...
{name: "MOVBstoreconstidx1", argLength: 3, reg: gpstoreconstidx, commutative: true, asm: "MOVB", scale: 1, aux: "SymValAndOff", typ: "Mem", symEffect: "Write"}, // store low byte of ValAndOff(AuxInt).Val() to arg0+1*arg1+ValAndOff(AuxInt).Off()+aux. arg2=mem
{name: "MOVWstoreconstidx1", argLength: 3, reg: gpstoreconstidx, commutative: true, asm: "MOVW", scale: 1, aux: "SymValAndOff", typ: "Mem", symEffect: "Write"}, // store low 2 bytes of ... arg1 ...
@@ -763,6 +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, 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
@@ -900,6 +903,31 @@ func init() {
{name: "ANDLlock", argLength: 3, reg: gpstore, asm: "ANDL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) &= arg1
{name: "ORBlock", argLength: 3, reg: gpstore, asm: "ORB", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1
{name: "ORLlock", argLength: 3, reg: gpstore, asm: "ORL", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"}, // *(arg0+auxint+aux) |= arg1
+
+ // Prefetch instructions
+ // Do prefetch arg0 address. arg0=addr, arg1=memory. Instruction variant selects locality hint
+ {name: "PrefetchT0", argLength: 2, reg: prefreg, asm: "PREFETCHT0", hasSideEffects: true},
+ {name: "PrefetchNTA", argLength: 2, reg: prefreg, asm: "PREFETCHNTA", hasSideEffects: true},
+
+ // CPUID feature: BMI1.
+ {name: "ANDNQ", argLength: 2, reg: gp21, asm: "ANDNQ", clobberFlags: true}, // arg0 &^ arg1
+ {name: "ANDNL", argLength: 2, reg: gp21, asm: "ANDNL", clobberFlags: true}, // arg0 &^ arg1
+ {name: "BLSIQ", argLength: 1, reg: gp11, asm: "BLSIQ", clobberFlags: true}, // arg0 & -arg0
+ {name: "BLSIL", argLength: 1, reg: gp11, asm: "BLSIL", clobberFlags: true}, // arg0 & -arg0
+ {name: "BLSMSKQ", argLength: 1, reg: gp11, asm: "BLSMSKQ", clobberFlags: true}, // arg0 ^ (arg0 - 1)
+ {name: "BLSMSKL", argLength: 1, reg: gp11, asm: "BLSMSKL", clobberFlags: true}, // arg0 ^ (arg0 - 1)
+ {name: "BLSRQ", argLength: 1, reg: gp11, asm: "BLSRQ", clobberFlags: true}, // arg0 & (arg0 - 1)
+ {name: "BLSRL", argLength: 1, reg: gp11, asm: "BLSRL", clobberFlags: true}, // arg0 & (arg0 - 1)
+ // count the number of trailing zero bits, prefer TZCNTQ over BSFQ, as TZCNTQ(0)==64
+ // 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/ARM.rules b/src/cmd/compile/internal/ssa/gen/ARM.rules
index bcacbafe3a5f0299dd47e45962a46170bd9896d4..2bc58a3c47c82c0280414ca094f848ac61c07e02 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM.rules
@@ -351,6 +351,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// checks
(NilCheck ...) => (LoweredNilCheck ...)
@@ -497,9 +498,9 @@
(XOR x (MOVWconst [c])) => (XORconst [c] x)
(BIC x (MOVWconst [c])) => (BICconst [c] x)
-(SLL x (MOVWconst [c])) => (SLLconst x [c&31]) // Note: I don't think we ever generate bad constant shifts (i.e. c>=32)
-(SRL x (MOVWconst [c])) => (SRLconst x [c&31])
-(SRA x (MOVWconst [c])) => (SRAconst x [c&31])
+(SLL x (MOVWconst [c])) && 0 <= c && c < 32 => (SLLconst x [c])
+(SRL x (MOVWconst [c])) && 0 <= c && c < 32 => (SRLconst x [c])
+(SRA x (MOVWconst [c])) && 0 <= c && c < 32 => (SRAconst x [c])
(CMP x (MOVWconst [c])) => (CMPconst [c] x)
(CMP (MOVWconst [c]) x) => (InvertFlags (CMPconst [c] x))
@@ -507,6 +508,8 @@
(TST x (MOVWconst [c])) => (TSTconst [c] x)
(TEQ x (MOVWconst [c])) => (TEQconst [c] x)
+(SRR x (MOVWconst [c])) => (SRRconst x [c&31])
+
// Canonicalize the order of arguments to comparisons - helps with CSE.
(CMP x y) && canonLessThan(x,y) => (InvertFlags (CMP y x))
@@ -1072,60 +1075,60 @@
(CMNshiftRL x (MOVWconst [c]) [d]) => (CMNconst x [int32(uint32(c)>>uint64(d))])
(CMNshiftRA x (MOVWconst [c]) [d]) => (CMNconst x [c>>uint64(d)])
-(ADDshiftLLreg x y (MOVWconst [c])) => (ADDshiftLL x y [c])
-(ADDshiftRLreg x y (MOVWconst [c])) => (ADDshiftRL x y [c])
-(ADDshiftRAreg x y (MOVWconst [c])) => (ADDshiftRA x y [c])
-(ADCshiftLLreg x y (MOVWconst [c]) flags) => (ADCshiftLL x y [c] flags)
-(ADCshiftRLreg x y (MOVWconst [c]) flags) => (ADCshiftRL x y [c] flags)
-(ADCshiftRAreg x y (MOVWconst [c]) flags) => (ADCshiftRA x y [c] flags)
-(ADDSshiftLLreg x y (MOVWconst [c])) => (ADDSshiftLL x y [c])
-(ADDSshiftRLreg x y (MOVWconst [c])) => (ADDSshiftRL x y [c])
-(ADDSshiftRAreg x y (MOVWconst [c])) => (ADDSshiftRA x y [c])
-(SUBshiftLLreg x y (MOVWconst [c])) => (SUBshiftLL x y [c])
-(SUBshiftRLreg x y (MOVWconst [c])) => (SUBshiftRL x y [c])
-(SUBshiftRAreg x y (MOVWconst [c])) => (SUBshiftRA x y [c])
-(SBCshiftLLreg x y (MOVWconst [c]) flags) => (SBCshiftLL x y [c] flags)
-(SBCshiftRLreg x y (MOVWconst [c]) flags) => (SBCshiftRL x y [c] flags)
-(SBCshiftRAreg x y (MOVWconst [c]) flags) => (SBCshiftRA x y [c] flags)
-(SUBSshiftLLreg x y (MOVWconst [c])) => (SUBSshiftLL x y [c])
-(SUBSshiftRLreg x y (MOVWconst [c])) => (SUBSshiftRL x y [c])
-(SUBSshiftRAreg x y (MOVWconst [c])) => (SUBSshiftRA x y [c])
-(RSBshiftLLreg x y (MOVWconst [c])) => (RSBshiftLL x y [c])
-(RSBshiftRLreg x y (MOVWconst [c])) => (RSBshiftRL x y [c])
-(RSBshiftRAreg x y (MOVWconst [c])) => (RSBshiftRA x y [c])
-(RSCshiftLLreg x y (MOVWconst [c]) flags) => (RSCshiftLL x y [c] flags)
-(RSCshiftRLreg x y (MOVWconst [c]) flags) => (RSCshiftRL x y [c] flags)
-(RSCshiftRAreg x y (MOVWconst [c]) flags) => (RSCshiftRA x y [c] flags)
-(RSBSshiftLLreg x y (MOVWconst [c])) => (RSBSshiftLL x y [c])
-(RSBSshiftRLreg x y (MOVWconst [c])) => (RSBSshiftRL x y [c])
-(RSBSshiftRAreg x y (MOVWconst [c])) => (RSBSshiftRA x y [c])
-(ANDshiftLLreg x y (MOVWconst [c])) => (ANDshiftLL x y [c])
-(ANDshiftRLreg x y (MOVWconst [c])) => (ANDshiftRL x y [c])
-(ANDshiftRAreg x y (MOVWconst [c])) => (ANDshiftRA x y [c])
-(ORshiftLLreg x y (MOVWconst [c])) => (ORshiftLL x y [c])
-(ORshiftRLreg x y (MOVWconst [c])) => (ORshiftRL x y [c])
-(ORshiftRAreg x y (MOVWconst [c])) => (ORshiftRA x y [c])
-(XORshiftLLreg x y (MOVWconst [c])) => (XORshiftLL x y [c])
-(XORshiftRLreg x y (MOVWconst [c])) => (XORshiftRL x y [c])
-(XORshiftRAreg x y (MOVWconst [c])) => (XORshiftRA x y [c])
-(BICshiftLLreg x y (MOVWconst [c])) => (BICshiftLL x y [c])
-(BICshiftRLreg x y (MOVWconst [c])) => (BICshiftRL x y [c])
-(BICshiftRAreg x y (MOVWconst [c])) => (BICshiftRA x y [c])
-(MVNshiftLLreg x (MOVWconst [c])) => (MVNshiftLL x [c])
-(MVNshiftRLreg x (MOVWconst [c])) => (MVNshiftRL x [c])
-(MVNshiftRAreg x (MOVWconst [c])) => (MVNshiftRA x [c])
-(CMPshiftLLreg x y (MOVWconst [c])) => (CMPshiftLL x y [c])
-(CMPshiftRLreg x y (MOVWconst [c])) => (CMPshiftRL x y [c])
-(CMPshiftRAreg x y (MOVWconst [c])) => (CMPshiftRA x y [c])
-(TSTshiftLLreg x y (MOVWconst [c])) => (TSTshiftLL x y [c])
-(TSTshiftRLreg x y (MOVWconst [c])) => (TSTshiftRL x y [c])
-(TSTshiftRAreg x y (MOVWconst [c])) => (TSTshiftRA x y [c])
-(TEQshiftLLreg x y (MOVWconst [c])) => (TEQshiftLL x y [c])
-(TEQshiftRLreg x y (MOVWconst [c])) => (TEQshiftRL x y [c])
-(TEQshiftRAreg x y (MOVWconst [c])) => (TEQshiftRA x y [c])
-(CMNshiftLLreg x y (MOVWconst [c])) => (CMNshiftLL x y [c])
-(CMNshiftRLreg x y (MOVWconst [c])) => (CMNshiftRL x y [c])
-(CMNshiftRAreg x y (MOVWconst [c])) => (CMNshiftRA x y [c])
+(ADDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftLL x y [c])
+(ADDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRL x y [c])
+(ADDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDshiftRA x y [c])
+(ADCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftLL x y [c] flags)
+(ADCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRL x y [c] flags)
+(ADCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (ADCshiftRA x y [c] flags)
+(ADDSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftLL x y [c])
+(ADDSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRL x y [c])
+(ADDSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ADDSshiftRA x y [c])
+(SUBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftLL x y [c])
+(SUBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRL x y [c])
+(SUBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBshiftRA x y [c])
+(SBCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftLL x y [c] flags)
+(SBCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRL x y [c] flags)
+(SBCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (SBCshiftRA x y [c] flags)
+(SUBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftLL x y [c])
+(SUBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRL x y [c])
+(SUBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (SUBSshiftRA x y [c])
+(RSBshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftLL x y [c])
+(RSBshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRL x y [c])
+(RSBshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBshiftRA x y [c])
+(RSCshiftLLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftLL x y [c] flags)
+(RSCshiftRLreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRL x y [c] flags)
+(RSCshiftRAreg x y (MOVWconst [c]) flags) && 0 <= c && c < 32 => (RSCshiftRA x y [c] flags)
+(RSBSshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftLL x y [c])
+(RSBSshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRL x y [c])
+(RSBSshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (RSBSshiftRA x y [c])
+(ANDshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftLL x y [c])
+(ANDshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRL x y [c])
+(ANDshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ANDshiftRA x y [c])
+(ORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftLL x y [c])
+(ORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRL x y [c])
+(ORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (ORshiftRA x y [c])
+(XORshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftLL x y [c])
+(XORshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRL x y [c])
+(XORshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (XORshiftRA x y [c])
+(BICshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftLL x y [c])
+(BICshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRL x y [c])
+(BICshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (BICshiftRA x y [c])
+(MVNshiftLLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftLL x [c])
+(MVNshiftRLreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRL x [c])
+(MVNshiftRAreg x (MOVWconst [c])) && 0 <= c && c < 32 => (MVNshiftRA x [c])
+(CMPshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftLL x y [c])
+(CMPshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRL x y [c])
+(CMPshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMPshiftRA x y [c])
+(TSTshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftLL x y [c])
+(TSTshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRL x y [c])
+(TSTshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TSTshiftRA x y [c])
+(TEQshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftLL x y [c])
+(TEQshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRL x y [c])
+(TEQshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (TEQshiftRA x y [c])
+(CMNshiftLLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftLL x y [c])
+(CMNshiftRLreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRL x y [c])
+(CMNshiftRAreg x y (MOVWconst [c])) && 0 <= c && c < 32 => (CMNshiftRA x y [c])
// Generate rotates
(ADDshiftLL [c] (SRLconst x [32-c]) x) => (SRRconst [32-c] x)
@@ -1135,7 +1138,6 @@
( ORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x)
(XORshiftRL [c] (SLLconst x [32-c]) x) => (SRRconst [ c] x)
-(RotateLeft32 x (MOVWconst [c])) => (SRRconst [-c&31] x)
(RotateLeft16 x (MOVWconst [c])) => (Or16 (Lsh16x32 x (MOVWconst [c&15])) (Rsh16Ux32 x (MOVWconst [-c&15])))
(RotateLeft8 x (MOVWconst [c])) => (Or8 (Lsh8x32 x (MOVWconst [c&7])) (Rsh8Ux32 x (MOVWconst [-c&7])))
(RotateLeft32 x y) => (SRR x (RSBconst [0] y))
@@ -1237,24 +1239,24 @@
(AND x (MVN y)) => (BIC x y)
// simplification with *shift ops
-(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(RSBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVWconst [0])
-(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVWconst [0])
+(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(RSBshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(ANDshiftLL y:(SLLconst x [c]) x [c]) => y
+(ANDshiftRL y:(SRLconst x [c]) x [c]) => y
+(ANDshiftRA y:(SRAconst x [c]) x [c]) => y
+(ORshiftLL y:(SLLconst x [c]) x [c]) => y
+(ORshiftRL y:(SRLconst x [c]) x [c]) => y
+(ORshiftRA y:(SRAconst x [c]) x [c]) => y
+(XORshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(XORshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(XORshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftLL (SLLconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftRL (SRLconst x [c]) x [c]) => (MOVWconst [0])
+(BICshiftRA (SRAconst x [c]) x [c]) => (MOVWconst [0])
(AND x (MVNshiftLL y [c])) => (BICshiftLL x y [c])
(AND x (MVNshiftRL y [c])) => (BICshiftRL x y [c])
(AND x (MVNshiftRA y [c])) => (BICshiftRA x y [c])
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules
index 62699f290c2149e6308c4f0d3d82b9b20e3d7738..d34e1899db50909a1e0cabd212840c36400bef88 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules
@@ -503,6 +503,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// checks
(NilCheck ...) => (LoweredNilCheck ...)
@@ -567,6 +568,9 @@
// Write barrier.
(WB ...) => (LoweredWB ...)
+// Publication barrier (0xe is ST option)
+(PubBarrier mem) => (DMB [0xe] mem)
+
(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem)
@@ -1174,6 +1178,9 @@
(CMPW x (MOVDconst [c])) => (CMPWconst [int32(c)] x)
(CMPW (MOVDconst [c]) x) => (InvertFlags (CMPWconst [int32(c)] x))
+(ROR x (MOVDconst [c])) => (RORconst x [c&63])
+(RORW x (MOVDconst [c])) => (RORWconst x [c&31])
+
// Canonicalize the order of arguments to comparisons - helps with CSE.
((CMP|CMPW) x y) && canonLessThan(x,y) => (InvertFlags ((CMP|CMPW) y x))
@@ -1359,6 +1366,7 @@
(XOR x (MVN y)) => (EON x y)
(OR x (MVN y)) => (ORN x y)
(MVN (XOR x y)) => (EON x y)
+(NEG (NEG x)) => x
(CSEL [cc] (MOVDconst [-1]) (MOVDconst [0]) flag) => (CSETM [cc] flag)
(CSEL [cc] (MOVDconst [0]) (MOVDconst [-1]) flag) => (CSETM [arm64Negate(cc)] flag)
@@ -1596,6 +1604,7 @@
(MVN x:(SLLconst [c] y)) && clobberIfDead(x) => (MVNshiftLL [c] y)
(MVN x:(SRLconst [c] y)) && clobberIfDead(x) => (MVNshiftRL [c] y)
(MVN x:(SRAconst [c] y)) && clobberIfDead(x) => (MVNshiftRA [c] y)
+(MVN x:(RORconst [c] y)) && clobberIfDead(x) => (MVNshiftRO [c] y)
(ADD x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ADDshiftLL x0 y [c])
(ADD x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ADDshiftRL x0 y [c])
(ADD x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ADDshiftRA x0 y [c])
@@ -1605,21 +1614,27 @@
(AND x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ANDshiftLL x0 y [c])
(AND x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ANDshiftRL x0 y [c])
(AND x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ANDshiftRA x0 y [c])
+(AND x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ANDshiftRO x0 y [c])
(OR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORshiftLL x0 y [c]) // useful for combined load
(OR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORshiftRL x0 y [c])
(OR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORshiftRA x0 y [c])
+(OR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORshiftRO x0 y [c])
(XOR x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (XORshiftLL x0 y [c])
(XOR x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (XORshiftRL x0 y [c])
(XOR x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (XORshiftRA x0 y [c])
+(XOR x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (XORshiftRO x0 y [c])
(BIC x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (BICshiftLL x0 y [c])
(BIC x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (BICshiftRL x0 y [c])
(BIC x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (BICshiftRA x0 y [c])
+(BIC x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (BICshiftRO x0 y [c])
(ORN x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (ORNshiftLL x0 y [c])
(ORN x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (ORNshiftRL x0 y [c])
(ORN x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (ORNshiftRA x0 y [c])
+(ORN x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (ORNshiftRO x0 y [c])
(EON x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (EONshiftLL x0 y [c])
(EON x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (EONshiftRL x0 y [c])
(EON x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (EONshiftRA x0 y [c])
+(EON x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (EONshiftRO x0 y [c])
(CMP x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (CMPshiftLL x0 y [c])
(CMP x0:(SLLconst [c] y) x1) && clobberIfDead(x0) => (InvertFlags (CMPshiftLL x1 y [c]))
(CMP x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (CMPshiftRL x0 y [c])
@@ -1632,6 +1647,7 @@
(TST x0 x1:(SLLconst [c] y)) && clobberIfDead(x1) => (TSTshiftLL x0 y [c])
(TST x0 x1:(SRLconst [c] y)) && clobberIfDead(x1) => (TSTshiftRL x0 y [c])
(TST x0 x1:(SRAconst [c] y)) && clobberIfDead(x1) => (TSTshiftRA x0 y [c])
+(TST x0 x1:(RORconst [c] y)) && clobberIfDead(x1) => (TSTshiftRO x0 y [c])
// prefer *const ops to *shift ops
(ADDshiftLL (MOVDconst [c]) x [d]) => (ADDconst [c] (SLLconst x [d]))
@@ -1640,12 +1656,15 @@
(ANDshiftLL (MOVDconst [c]) x [d]) => (ANDconst [c] (SLLconst x [d]))
(ANDshiftRL (MOVDconst [c]) x [d]) => (ANDconst [c] (SRLconst x [d]))
(ANDshiftRA (MOVDconst [c]) x [d]) => (ANDconst [c] (SRAconst x [d]))
+(ANDshiftRO (MOVDconst [c]) x [d]) => (ANDconst [c] (RORconst x [d]))
(ORshiftLL (MOVDconst [c]) x [d]) => (ORconst [c] (SLLconst x [d]))
(ORshiftRL (MOVDconst [c]) x [d]) => (ORconst [c] (SRLconst x [d]))
(ORshiftRA (MOVDconst [c]) x [d]) => (ORconst [c] (SRAconst x [d]))
+(ORshiftRO (MOVDconst [c]) x [d]) => (ORconst [c] (RORconst x [d]))
(XORshiftLL (MOVDconst [c]) x [d]) => (XORconst [c] (SLLconst x [d]))
(XORshiftRL (MOVDconst [c]) x [d]) => (XORconst [c] (SRLconst x [d]))
(XORshiftRA (MOVDconst [c]) x [d]) => (XORconst [c] (SRAconst x [d]))
+(XORshiftRO (MOVDconst [c]) x [d]) => (XORconst [c] (RORconst x [d]))
(CMPshiftLL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SLLconst x [d])))
(CMPshiftRL (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRLconst x [d])))
(CMPshiftRA (MOVDconst [c]) x [d]) => (InvertFlags (CMPconst [c] (SRAconst x [d])))
@@ -1655,11 +1674,13 @@
(TSTshiftLL (MOVDconst [c]) x [d]) => (TSTconst [c] (SLLconst x [d]))
(TSTshiftRL (MOVDconst [c]) x [d]) => (TSTconst [c] (SRLconst x [d]))
(TSTshiftRA (MOVDconst [c]) x [d]) => (TSTconst [c] (SRAconst x [d]))
+(TSTshiftRO (MOVDconst [c]) x [d]) => (TSTconst [c] (RORconst x [d]))
// constant folding in *shift ops
(MVNshiftLL (MOVDconst [c]) [d]) => (MOVDconst [^int64(uint64(c)< (MOVDconst [^int64(uint64(c)>>uint64(d))])
(MVNshiftRA (MOVDconst [c]) [d]) => (MOVDconst [^(c>>uint64(d))])
+(MVNshiftRO (MOVDconst [c]) [d]) => (MOVDconst [^rotateRight64(c, d)])
(NEGshiftLL (MOVDconst [c]) [d]) => (MOVDconst [-int64(uint64(c)< (MOVDconst [-int64(uint64(c)>>uint64(d))])
(NEGshiftRA (MOVDconst [c]) [d]) => (MOVDconst [-(c>>uint64(d))])
@@ -1672,21 +1693,27 @@
(ANDshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [int64(uint64(c)< (ANDconst x [int64(uint64(c)>>uint64(d))])
(ANDshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [c>>uint64(d)])
+(ANDshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [rotateRight64(c, d)])
(ORshiftLL x (MOVDconst [c]) [d]) => (ORconst x [int64(uint64(c)< (ORconst x [int64(uint64(c)>>uint64(d))])
(ORshiftRA x (MOVDconst [c]) [d]) => (ORconst x [c>>uint64(d)])
+(ORshiftRO x (MOVDconst [c]) [d]) => (ORconst x [rotateRight64(c, d)])
(XORshiftLL x (MOVDconst [c]) [d]) => (XORconst x [int64(uint64(c)< (XORconst x [int64(uint64(c)>>uint64(d))])
(XORshiftRA x (MOVDconst [c]) [d]) => (XORconst x [c>>uint64(d)])
+(XORshiftRO x (MOVDconst [c]) [d]) => (XORconst x [rotateRight64(c, d)])
(BICshiftLL x (MOVDconst [c]) [d]) => (ANDconst x [^int64(uint64(c)< (ANDconst x [^int64(uint64(c)>>uint64(d))])
(BICshiftRA x (MOVDconst [c]) [d]) => (ANDconst x [^(c>>uint64(d))])
+(BICshiftRO x (MOVDconst [c]) [d]) => (ANDconst x [^rotateRight64(c, d)])
(ORNshiftLL x (MOVDconst [c]) [d]) => (ORconst x [^int64(uint64(c)< (ORconst x [^int64(uint64(c)>>uint64(d))])
(ORNshiftRA x (MOVDconst [c]) [d]) => (ORconst x [^(c>>uint64(d))])
+(ORNshiftRO x (MOVDconst [c]) [d]) => (ORconst x [^rotateRight64(c, d)])
(EONshiftLL x (MOVDconst [c]) [d]) => (XORconst x [^int64(uint64(c)< (XORconst x [^int64(uint64(c)>>uint64(d))])
(EONshiftRA x (MOVDconst [c]) [d]) => (XORconst x [^(c>>uint64(d))])
+(EONshiftRO x (MOVDconst [c]) [d]) => (XORconst x [^rotateRight64(c, d)])
(CMPshiftLL x (MOVDconst [c]) [d]) => (CMPconst x [int64(uint64(c)< (CMPconst x [int64(uint64(c)>>uint64(d))])
(CMPshiftRA x (MOVDconst [c]) [d]) => (CMPconst x [c>>uint64(d)])
@@ -1696,29 +1723,36 @@
(TSTshiftLL x (MOVDconst [c]) [d]) => (TSTconst x [int64(uint64(c)< (TSTconst x [int64(uint64(c)>>uint64(d))])
(TSTshiftRA x (MOVDconst [c]) [d]) => (TSTconst x [c>>uint64(d)])
+(TSTshiftRO x (MOVDconst [c]) [d]) => (TSTconst x [rotateRight64(c, d)])
// simplification with *shift ops
-(SUBshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(SUBshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(SUBshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(ANDshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ANDshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ANDshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(ORshiftLL x y:(SLLconst x [c]) [d]) && c==d => y
-(ORshiftRL x y:(SRLconst x [c]) [d]) && c==d => y
-(ORshiftRA x y:(SRAconst x [c]) [d]) && c==d => y
-(XORshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(XORshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(XORshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [0])
-(BICshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [0])
-(EONshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(EONshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(EONshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftLL x (SLLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftRL x (SRLconst x [c]) [d]) && c==d => (MOVDconst [-1])
-(ORNshiftRA x (SRAconst x [c]) [d]) && c==d => (MOVDconst [-1])
+(SUBshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(SUBshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(SUBshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(ANDshiftLL y:(SLLconst x [c]) x [c]) => y
+(ANDshiftRL y:(SRLconst x [c]) x [c]) => y
+(ANDshiftRA y:(SRAconst x [c]) x [c]) => y
+(ANDshiftRO y:(RORconst x [c]) x [c]) => y
+(ORshiftLL y:(SLLconst x [c]) x [c]) => y
+(ORshiftRL y:(SRLconst x [c]) x [c]) => y
+(ORshiftRA y:(SRAconst x [c]) x [c]) => y
+(ORshiftRO y:(RORconst x [c]) x [c]) => y
+(XORshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(XORshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [0])
+(BICshiftRO (RORconst x [c]) x [c]) => (MOVDconst [0])
+(EONshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1])
+(EONshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftLL (SLLconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRL (SRLconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRA (SRAconst x [c]) x [c]) => (MOVDconst [-1])
+(ORNshiftRO (RORconst x [c]) x [c]) => (MOVDconst [-1])
// Generate rotates with const shift
(ADDshiftLL [c] (SRLconst x [64-c]) x) => (RORconst [64-c] x)
@@ -1824,16 +1858,26 @@
// sbfiz
// (x << lc) >> rc
(SRAconst [rc] (SLLconst [lc] x)) && lc > rc => (SBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
+// int64(x << lc)
(MOVWreg (SLLconst [lc] x)) && lc < 32 => (SBFIZ [armBFAuxInt(lc, 32-lc)] x)
(MOVHreg (SLLconst [lc] x)) && lc < 16 => (SBFIZ [armBFAuxInt(lc, 16-lc)] x)
(MOVBreg (SLLconst [lc] x)) && lc < 8 => (SBFIZ [armBFAuxInt(lc, 8-lc)] x)
+// int64(x) << lc
+(SLLconst [lc] (MOVWreg x)) => (SBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
+(SLLconst [lc] (MOVHreg x)) => (SBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
+(SLLconst [lc] (MOVBreg x)) => (SBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
// sbfx
// (x << lc) >> rc
(SRAconst [rc] (SLLconst [lc] x)) && lc <= rc => (SBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+// int64(x) >> rc
(SRAconst [rc] (MOVWreg x)) && rc < 32 => (SBFX [armBFAuxInt(rc, 32-rc)] x)
(SRAconst [rc] (MOVHreg x)) && rc < 16 => (SBFX [armBFAuxInt(rc, 16-rc)] x)
(SRAconst [rc] (MOVBreg x)) && rc < 8 => (SBFX [armBFAuxInt(rc, 8-rc)] x)
+// merge sbfx and sign-extension into sbfx
+(MOVWreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (SBFX [bfc] x)
+(MOVHreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 16 => (SBFX [bfc] x)
+(MOVBreg (SBFX [bfc] x)) && bfc.getARM64BFwidth() <= 8 => (SBFX [bfc] x)
// sbfiz/sbfx combinations: merge shifts into bitfield ops
(SRAconst [sc] (SBFIZ [bfc] x)) && sc < bfc.getARM64BFlsb()
@@ -1843,42 +1887,48 @@
=> (SBFX [armBFAuxInt(sc-bfc.getARM64BFlsb(), bfc.getARM64BFlsb()+bfc.getARM64BFwidth()-sc)] x)
// ubfiz
+// (x << lc) >> rc
+(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
+// uint64(x) << lc
+(SLLconst [lc] (MOVWUreg x)) => (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
+(SLLconst [lc] (MOVHUreg x)) => (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
+(SLLconst [lc] (MOVBUreg x)) => (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
+// uint64(x << lc)
+(MOVWUreg (SLLconst [lc] x)) && lc < 32 => (UBFIZ [armBFAuxInt(lc, 32-lc)] x)
+(MOVHUreg (SLLconst [lc] x)) && lc < 16 => (UBFIZ [armBFAuxInt(lc, 16-lc)] x)
+(MOVBUreg (SLLconst [lc] x)) && lc < 8 => (UBFIZ [armBFAuxInt(lc, 8-lc)] x)
+
+// merge ANDconst into ubfiz
// (x & ac) << sc
(SLLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, 0)
=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
-(SLLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFIZ [armBFAuxInt(sc, 32)] x)
-(SLLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFIZ [armBFAuxInt(sc, 16)] x)
-(SLLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFIZ [armBFAuxInt(sc, 8)] x)
// (x << sc) & ac
(ANDconst [ac] (SLLconst [sc] x)) && isARM64BFMask(sc, ac, sc)
=> (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
-(MOVWUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, sc)
- => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
-(MOVHUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, sc)
- => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
-(MOVBUreg (SLLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, sc)
- => (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
-// (x << lc) >> rc
-(SRLconst [rc] (SLLconst [lc] x)) && lc > rc => (UBFIZ [armBFAuxInt(lc-rc, 64-lc)] x)
// ubfx
+// (x << lc) >> rc
+(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+// uint64(x) >> rc
+(SRLconst [rc] (MOVWUreg x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32-rc)] x)
+(SRLconst [rc] (MOVHUreg x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16-rc)] x)
+(SRLconst [rc] (MOVBUreg x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8-rc)] x)
+// uint64(x >> rc)
+(MOVWUreg (SRLconst [rc] x)) && rc < 32 => (UBFX [armBFAuxInt(rc, 32)] x)
+(MOVHUreg (SRLconst [rc] x)) && rc < 16 => (UBFX [armBFAuxInt(rc, 16)] x)
+(MOVBUreg (SRLconst [rc] x)) && rc < 8 => (UBFX [armBFAuxInt(rc, 8)] x)
+// merge ANDconst into ubfx
// (x >> sc) & ac
(ANDconst [ac] (SRLconst [sc] x)) && isARM64BFMask(sc, ac, 0)
=> (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
-(MOVWUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<32-1, 0) => (UBFX [armBFAuxInt(sc, 32)] x)
-(MOVHUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<16-1, 0) => (UBFX [armBFAuxInt(sc, 16)] x)
-(MOVBUreg (SRLconst [sc] x)) && isARM64BFMask(sc, 1<<8-1, 0) => (UBFX [armBFAuxInt(sc, 8)] x)
// (x & ac) >> sc
(SRLconst [sc] (ANDconst [ac] x)) && isARM64BFMask(sc, ac, sc)
=> (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
-(SRLconst [sc] (MOVWUreg x)) && isARM64BFMask(sc, 1<<32-1, sc)
- => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
-(SRLconst [sc] (MOVHUreg x)) && isARM64BFMask(sc, 1<<16-1, sc)
- => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
-(SRLconst [sc] (MOVBUreg x)) && isARM64BFMask(sc, 1<<8-1, sc)
- => (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
-// (x << lc) >> rc
-(SRLconst [rc] (SLLconst [lc] x)) && lc < rc => (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+
+// merge ubfx and zerso-extension into ubfx
+(MOVWUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 32 => (UBFX [bfc] x)
+(MOVHUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 16 => (UBFX [bfc] x)
+(MOVBUreg (UBFX [bfc] x)) && bfc.getARM64BFwidth() <= 8 => (UBFX [bfc] x)
// ubfiz/ubfx combinations: merge shifts into bitfield ops
(SRLconst [sc] (UBFX [bfc] x)) && sc < bfc.getARM64BFwidth()
@@ -2860,6 +2910,10 @@
(MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
(MOVDload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+// Prefetch instructions (aux is option: 0 - PLDL1KEEP; 1 - PLDL1STRM)
+(PrefetchCache addr mem) => (PRFM [0] addr mem)
+(PrefetchCacheStreamed addr mem) => (PRFM [1] addr mem)
+
// Arch-specific inlining for small or disjoint runtime.memmove
(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
&& sz >= 0
@@ -2868,3 +2922,14 @@
&& isInlinableMemmove(dst, src, sz, config)
&& clobber(s1, s2, s3, call)
=> (Move [sz] dst src mem)
+
+// Match post-lowering calls, register version.
+(SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem))
+ && sz >= 0
+ && isSameCall(sym, "runtime.memmove")
+ && call.Uses == 1
+ && isInlinableMemmove(dst, src, sz, config)
+ && clobber(call)
+ => (Move [sz] dst src mem)
+
+((REV|REVW) ((REV|REVW) p)) => p
diff --git a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
index 18a5666b40f0dfcce916f12efb228ee6b2ec1100..e052ce09f423bee903fe537da9655546d7a572c2 100644
--- a/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/ARM64Ops.go
@@ -175,6 +175,7 @@ func init() {
fpstore = regInfo{inputs: []regMask{gpspsbg, fp}}
fpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, fp}}
readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
+ prefreg = regInfo{inputs: []regMask{gpspsbg}}
)
ops := []opData{
// binary ops
@@ -301,6 +302,7 @@ func init() {
{name: "MVNshiftLL", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "MVNshiftRA", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+ {name: "MVNshiftRO", argLength: 1, reg: gp11, asm: "MVN", aux: "Int64"}, // ^(arg0 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
{name: "NEGshiftLL", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "NEGshiftRA", argLength: 1, reg: gp11, asm: "NEG", aux: "Int64"}, // -(arg0>>auxInt), signed shift, auxInt should be in the range 0 to 63.
@@ -313,21 +315,27 @@ func init() {
{name: "ANDshiftLL", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "ANDshiftRA", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+ {name: "ANDshiftRO", argLength: 2, reg: gp21, asm: "AND", aux: "Int64"}, // arg0 & (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
{name: "ORshiftLL", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
{name: "ORshiftRA", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
+ {name: "ORshiftRO", argLength: 2, reg: gp21, asm: "ORR", aux: "Int64"}, // arg0 | arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
{name: "XORshiftLL", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
{name: "XORshiftRA", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
+ {name: "XORshiftRO", argLength: 2, reg: gp21, asm: "EOR", aux: "Int64"}, // arg0 ^ arg1 ROR auxInt, signed shift, auxInt should be in the range 0 to 63.
{name: "BICshiftLL", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "BICshiftRA", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+ {name: "BICshiftRO", argLength: 2, reg: gp21, asm: "BIC", aux: "Int64"}, // arg0 &^ (arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
{name: "EONshiftLL", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "EONshiftRA", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+ {name: "EONshiftRO", argLength: 2, reg: gp21, asm: "EON", aux: "Int64"}, // arg0 ^ ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
{name: "ORNshiftLL", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1<>auxInt), unsigned shift, auxInt should be in the range 0 to 63.
{name: "ORNshiftRA", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1>>auxInt), signed shift, auxInt should be in the range 0 to 63.
+ {name: "ORNshiftRO", argLength: 2, reg: gp21, asm: "ORN", aux: "Int64"}, // arg0 | ^(arg1 ROR auxInt), signed shift, auxInt should be in the range 0 to 63.
{name: "CMPshiftLL", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1<>auxInt, unsigned shift, auxInt should be in the range 0 to 63.
{name: "CMPshiftRA", argLength: 2, reg: gp2flags, asm: "CMP", aux: "Int64", typ: "Flags"}, // arg0 compare to arg1>>auxInt, signed shift, auxInt should be in the range 0 to 63.
@@ -337,6 +345,7 @@ func init() {
{name: "TSTshiftLL", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1<>auxInt) compare to 0, unsigned shift, auxInt should be in the range 0 to 63.
{name: "TSTshiftRA", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1>>auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
+ {name: "TSTshiftRO", argLength: 2, reg: gp2flags, asm: "TST", aux: "Int64", typ: "Flags"}, // (arg0 & arg1 ROR auxInt) compare to 0, signed shift, auxInt should be in the range 0 to 63.
// bitfield ops
// for all bitfield ops lsb is auxInt>>8, width is auxInt&0xff
@@ -482,9 +491,10 @@ func init() {
{name: "CSETM", argLength: 1, reg: readflags, asm: "CSETM", aux: "CCop"}, // auxint(flags) ? -1 : 0
// 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: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R26"), 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
+ {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, 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
// pseudo-ops
{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // panic if arg0 is nil. arg1=mem.
@@ -729,6 +739,13 @@ func init() {
{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
+
+ // Prefetch instruction
+ // Do prefetch arg0 address with option aux. arg0=addr, arg1=memory, aux=option.
+ {name: "PRFM", argLength: 2, aux: "Int64", reg: prefreg, asm: "PRFM", hasSideEffects: true},
+
+ // Publication barrier
+ {name: "DMB", argLength: 1, aux: "Int64", asm: "DMB", hasSideEffects: true}, // Do data barrier. arg0=memory, aux=option.
}
blocks := []blockData{
@@ -759,15 +776,17 @@ func init() {
}
archs = append(archs, arch{
- name: "ARM64",
- pkg: "cmd/internal/obj/arm64",
- genfile: "../../arm64/ssa.go",
- ops: ops,
- blocks: blocks,
- regnames: regNamesARM64,
- gpregmask: gp,
- fpregmask: fp,
- framepointerreg: -1, // not used
- linkreg: int8(num["R30"]),
+ name: "ARM64",
+ pkg: "cmd/internal/obj/arm64",
+ genfile: "../../arm64/ssa.go",
+ ops: ops,
+ blocks: blocks,
+ regnames: regNamesARM64,
+ ParamIntRegNames: "R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15",
+ ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
+ gpregmask: gp,
+ fpregmask: fp,
+ framepointerreg: -1, // not used
+ linkreg: int8(num["R30"]),
})
}
diff --git a/src/cmd/compile/internal/ssa/gen/ARMOps.go b/src/cmd/compile/internal/ssa/gen/ARMOps.go
index d1f86039a36e5c98ab7d6681928d67ea50e66302..2f004205a55b357406a8f5cc32b42eeba653a0a8 100644
--- a/src/cmd/compile/internal/ssa/gen/ARMOps.go
+++ b/src/cmd/compile/internal/ssa/gen/ARMOps.go
@@ -228,14 +228,15 @@ func init() {
// shifts
{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"}, // arg0 << arg1, shift amount is mod 256
- {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt
+ {name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0 << auxInt, 0 <= auxInt < 32
{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"}, // arg0 >> arg1, unsigned, shift amount is mod 256
- {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned
+ {name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0 >> auxInt, unsigned, 0 <= auxInt < 32
{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"}, // arg0 >> arg1, signed, shift amount is mod 256
- {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed
+ {name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0 >> auxInt, signed, 0 <= auxInt < 32
{name: "SRR", argLength: 2, reg: gp21}, // arg0 right rotate by arg1 bits
- {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits
+ {name: "SRRconst", argLength: 1, reg: gp11, aux: "Int32"}, // arg0 right rotate by auxInt bits, 0 <= auxInt < 32
+ // auxInt for all of these satisfy 0 <= auxInt < 32
{name: "ADDshiftLL", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1<>auxInt, unsigned shift
{name: "ADDshiftRA", argLength: 2, reg: gp21, asm: "ADD", aux: "Int32"}, // arg0 + arg1>>auxInt, signed shift
@@ -431,6 +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, 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/MIPS.rules b/src/cmd/compile/internal/ssa/gen/MIPS.rules
index 4ac9668ea9cb566154a506ba1c3c0ecac7a144e7..639dda4b075492d3d8a499b71fa6616a2cbcb10c 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS.rules
+++ b/src/cmd/compile/internal/ssa/gen/MIPS.rules
@@ -334,6 +334,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// atomic intrinsics
(AtomicLoad(8|32) ...) => (LoweredAtomicLoad(8|32) ...)
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64.rules b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
index fd04a6c3a85c7ae768fa36d3a4a9bafb65b53012..292ff2fc7966092fac267f78a39a1d939e221c12 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS64.rules
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64.rules
@@ -379,6 +379,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// atomic intrinsics
(AtomicLoad(8|32|64) ...) => (LoweredAtomicLoad(8|32|64) ...)
diff --git a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
index 77f251c0d3f920ed3c1e43fefe781e5257b3748e..7b18c42ffb91e35c73e202a7e0b62f9dbb3b9754 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPS64Ops.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -275,6 +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, 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 b92e8cb9f1ee64b38db0a24a78cc2058ddb7b0da..523847badc9ce2d77db0c54757e81e50e2d706bd 100644
--- a/src/cmd/compile/internal/ssa/gen/MIPSOps.go
+++ b/src/cmd/compile/internal/ssa/gen/MIPSOps.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -257,6 +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, 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 ce4b324b5e10227bef0406b69f33b2bdef3652c9..c3f07a4e22cf3929a4af63e5a7482d70d7aafa60 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)
@@ -580,7 +582,7 @@
(Store {t} ptr val mem) && t.Size() == 8 && is64BitFloat(val.Type) => (FMOVDstore ptr val mem)
(Store {t} ptr val mem) && t.Size() == 8 && is32BitFloat(val.Type) => (FMOVDstore ptr val mem) // glitch from (Cvt32Fto64F x) => x -- type is wrong
(Store {t} ptr val mem) && t.Size() == 4 && is32BitFloat(val.Type) => (FMOVSstore ptr val mem)
-(Store {t} ptr val mem) && t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type)) => (MOVDstore ptr val mem)
+(Store {t} ptr val mem) && t.Size() == 8 && !is64BitFloat(val.Type) => (MOVDstore ptr val mem)
(Store {t} ptr val mem) && t.Size() == 4 && is32BitInt(val.Type) => (MOVWstore ptr val mem)
(Store {t} ptr val mem) && t.Size() == 2 => (MOVHstore ptr val mem)
(Store {t} ptr val mem) && t.Size() == 1 => (MOVBstore ptr val mem)
@@ -670,6 +672,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// Miscellaneous
(GetClosurePtr ...) => (LoweredGetClosurePtr ...)
@@ -847,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
@@ -1018,6 +1023,8 @@
(MOVWZreg x:(MOVWZloadidx _ _ _)) => x
(MOVWreg x:(MOVWload _ _)) => x
(MOVWreg x:(MOVWloadidx _ _ _)) => x
+(MOVBZreg x:(Select0 (LoweredAtomicLoad8 _ _))) => x
+(MOVWZreg x:(Select0 (LoweredAtomicLoad32 _ _))) => x
// don't extend if argument is already extended
(MOVBreg x:(Arg )) && is8BitInt(t) && isSigned(t) => x
@@ -1084,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)
@@ -1135,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)
@@ -1450,3 +1460,26 @@
&& i1 == i0+1 && i2 == i0+2 && i3 == i0+3 && i4 == i0+4 && i5 == i0+5 && i6 == i0+6 && i7 == i0+7
&& clobber(x0, x1, x2, x3, x4, x5, x6)
=> (MOVDBRstore (MOVDaddr [i0] {s} p) w mem)
+
+// Arch-specific inlining for small or disjoint runtime.memmove
+(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
+ && sz >= 0
+ && isSameCall(sym, "runtime.memmove")
+ && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
+ && isInlinableMemmove(dst, src, sz, config)
+ && clobber(s1, s2, s3, call)
+ => (Move [sz] dst src mem)
+
+// Match post-lowering calls, register version.
+(SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem))
+ && sz >= 0
+ && isSameCall(sym, "runtime.memmove")
+ && call.Uses == 1
+ && isInlinableMemmove(dst, src, sz, config)
+ && clobber(call)
+ => (Move [sz] dst src mem)
+
+// Prefetch instructions (aux is option: 0 - DCBT ; 8 - DCBT stream)
+(PrefetchCache ptr mem) => (DCBT ptr mem [0])
+(PrefetchCacheStreamed ptr mem) => (DCBT ptr mem [8])
+
diff --git a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
index f7198b90c32e48ce940e6b52530aee7dea47c4fb..59d8af1a9d3952fb1bfeee88af01200fc618cf6a 100644
--- a/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/PPC64Ops.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -148,6 +149,7 @@ func init() {
crgp21 = regInfo{inputs: []regMask{gp, gp}, outputs: []regMask{gp}}
gpload = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
gploadidx = regInfo{inputs: []regMask{gp | sp | sb, gp}, outputs: []regMask{gp}}
+ prefreg = regInfo{inputs: []regMask{gp | sp | sb}}
gpstore = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
gpstoreidx = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb, gp | sp | sb}}
gpstorezero = regInfo{inputs: []regMask{gp | sp | sb}} // ppc64.REGZERO is reserved zero value
@@ -335,6 +337,10 @@ func init() {
{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", typ: "Float64"},
{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", typ: "Float32"},
+ // Prefetch instruction
+ // Do prefetch of address generated with arg0 and arg1 with option aux. arg0=addr,arg1=memory, aux=option.
+ {name: "DCBT", argLength: 2, aux: "Int64", reg: prefreg, asm: "DCBT", hasSideEffects: true},
+
// Store bytes in the reverse endian order of the arch into arg0.
// These are indexed stores with no offset field in the instruction so the auxint fields are not used.
{name: "MOVDBRstore", argLength: 3, reg: gpstore, asm: "MOVDBR", aux: "Sym", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes reverse order
@@ -390,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
@@ -427,9 +433,10 @@ func init() {
{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
{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: "CALLclosure", argLength: 3, 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: 2, 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
+ {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, 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
// large or unaligned zeroing
// arg0 = address of memory to zero (in R3, changed as side effect)
@@ -703,15 +710,17 @@ func init() {
}
archs = append(archs, arch{
- name: "PPC64",
- pkg: "cmd/internal/obj/ppc64",
- genfile: "../../ppc64/ssa.go",
- ops: ops,
- blocks: blocks,
- regnames: regNamesPPC64,
- gpregmask: gp,
- fpregmask: fp,
- framepointerreg: int8(num["SP"]),
- linkreg: -1, // not used
+ name: "PPC64",
+ pkg: "cmd/internal/obj/ppc64",
+ genfile: "../../ppc64/ssa.go",
+ ops: ops,
+ blocks: blocks,
+ regnames: regNamesPPC64,
+ ParamIntRegNames: "R3 R4 R5 R6 R7 R8 R9 R10 R14 R15 R16 R17",
+ ParamFloatRegNames: "F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12",
+ gpregmask: gp,
+ fpregmask: fp,
+ framepointerreg: -1,
+ linkreg: -1, // not used
})
}
diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64.rules b/src/cmd/compile/internal/ssa/gen/RISCV64.rules
index 9cdd62edbe01c004f4b79951d38a4d5cd123a2a3..3379e1dac50cc36720367505e474cc4fba901e78 100644
--- a/src/cmd/compile/internal/ssa/gen/RISCV64.rules
+++ b/src/cmd/compile/internal/ssa/gen/RISCV64.rules
@@ -6,9 +6,7 @@
// * Use SLTI and SLTIU for comparisons to constants, instead of SLT/SLTU with constants in registers
// * Use the zero register instead of moving 0 into a register.
// * Add rules to avoid generating a temp bool value for (If (SLT[U] ...) ...).
-// * Optimize left and right shift by simplifying SLTIU, Neg, and ADD for constants.
// * Arrange for non-trivial Zero and Move lowerings to use aligned loads and stores.
-// * Eliminate zero immediate shifts, adds, etc.
// * Avoid using Neq32 for writeBarrier.enabled checks.
// Lowering arithmetic
@@ -29,6 +27,8 @@
(Sub64F ...) => (FSUBD ...)
(Mul64 ...) => (MUL ...)
+(Mul64uhilo ...) => (LoweredMuluhilo ...)
+(Mul64uover ...) => (LoweredMuluover ...)
(Mul32 ...) => (MULW ...)
(Mul16 x y) => (MULW (SignExt16to32 x) (SignExt16to32 y))
(Mul8 x y) => (MULW (SignExt8to32 x) (SignExt8to32 y))
@@ -94,6 +94,12 @@
(Sqrt ...) => (FSQRTD ...)
(Sqrt32 ...) => (FSQRTS ...)
+(Copysign ...) => (FSGNJD ...)
+
+(Abs ...) => (FABSD ...)
+
+(FMA ...) => (FMADDD ...)
+
// Sign and zero extension.
(SignExt8to16 ...) => (MOVBreg ...)
@@ -221,7 +227,7 @@
(Rsh64x32 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] (ZeroExt32to64 y)))))
(Rsh64x64 x y) => (SRA x (OR y (ADDI [-1] (SLTIU [64] y))))
-// rotates
+// Rotates.
(RotateLeft8 x (MOVDconst [c])) => (Or8 (Lsh8x64 x (MOVDconst [c&7])) (Rsh8Ux64 x (MOVDconst [-c&7])))
(RotateLeft16 x (MOVDconst [c])) => (Or16 (Lsh16x64 x (MOVDconst [c&15])) (Rsh16Ux64 x (MOVDconst [-c&15])))
(RotateLeft32 x (MOVDconst [c])) => (Or32 (Lsh32x64 x (MOVDconst [c&31])) (Rsh32Ux64 x (MOVDconst [-c&31])))
@@ -538,6 +544,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// Atomic Intrinsics
(AtomicLoad8 ...) => (LoweredAtomicLoad8 ...)
@@ -586,6 +593,10 @@
(BNEZ (SEQZ x) yes no) => (BEQZ x yes no)
(BNEZ (SNEZ x) yes no) => (BNEZ x yes no)
+// Absorb NEG into branch when possible.
+(BEQZ x:(NEG y) yes no) && x.Uses == 1 => (BEQZ y yes no)
+(BNEZ x:(NEG y) yes no) && x.Uses == 1 => (BNEZ y yes no)
+
// Convert BEQZ/BNEZ into more optimal branch conditions.
(BEQZ (SUB x y) yes no) => (BEQ x y yes no)
(BNEZ (SUB x y) yes no) => (BNE x y yes no)
@@ -594,11 +605,15 @@
(BEQZ (SLTU x y) yes no) => (BGEU x y yes no)
(BNEZ (SLTU x y) yes no) => (BLTU x y yes no)
-// Convert branch with zero to BEQZ/BNEZ.
+// Convert branch with zero to more optimal branch zero.
(BEQ (MOVDconst [0]) cond yes no) => (BEQZ cond yes no)
(BEQ cond (MOVDconst [0]) yes no) => (BEQZ cond yes no)
(BNE (MOVDconst [0]) cond yes no) => (BNEZ cond yes no)
(BNE cond (MOVDconst [0]) yes no) => (BNEZ cond yes no)
+(BLT (MOVDconst [0]) cond yes no) => (BGTZ cond yes no)
+(BLT cond (MOVDconst [0]) yes no) => (BLTZ cond yes no)
+(BGE (MOVDconst [0]) cond yes no) => (BLEZ cond yes no)
+(BGE cond (MOVDconst [0]) yes no) => (BGEZ cond yes no)
// Store zero
(MOVBstore [off] {sym} ptr (MOVDconst [0]) mem) => (MOVBstorezero [off] {sym} ptr mem)
@@ -690,16 +705,49 @@
(SUB x (MOVDconst [val])) && is32Bit(-val) => (ADDI [-val] x)
// Subtraction of zero.
-(SUB x (MOVDconst [0])) => x
-
-// Subtraction of zero with sign extension.
+(SUB x (MOVDconst [0])) => x
(SUBW x (MOVDconst [0])) => (ADDIW [0] x)
// Subtraction from zero.
-(SUB (MOVDconst [0]) x) => (NEG x)
-
-// Subtraction from zero with sign extension.
+(SUB (MOVDconst [0]) x) => (NEG x)
(SUBW (MOVDconst [0]) x) => (NEGW x)
-// Addition of zero.
+// Addition of zero or two constants.
(ADDI [0] x) => x
+(ADDI [x] (MOVDconst [y])) && is32Bit(x + y) => (MOVDconst [x + y])
+
+// ANDI with all zeros, all ones or two constants.
+(ANDI [0] x) => (MOVDconst [0])
+(ANDI [-1] x) => x
+(ANDI [x] (MOVDconst [y])) => (MOVDconst [x & y])
+
+// ORI with all zeroes, all ones or two constants.
+(ORI [0] x) => x
+(ORI [-1] x) => (MOVDconst [-1])
+(ORI [x] (MOVDconst [y])) => (MOVDconst [x | y])
+
+// Negation of a constant.
+(NEG (MOVDconst [x])) => (MOVDconst [-x])
+(NEGW (MOVDconst [x])) => (MOVDconst [int64(int32(-x))])
+
+// Shift of a constant.
+(SLLI [x] (MOVDconst [y])) && is32Bit(y << x) => (MOVDconst [y << x])
+(SRLI [x] (MOVDconst [y])) => (MOVDconst [int64(uint64(y) >> x)])
+(SRAI [x] (MOVDconst [y])) => (MOVDconst [int64(y) >> x])
+
+// SLTI/SLTIU with constants.
+(SLTI [x] (MOVDconst [y])) => (MOVDconst [b2i(int64(y) < int64(x))])
+(SLTIU [x] (MOVDconst [y])) => (MOVDconst [b2i(uint64(y) < uint64(x))])
+
+// Merge negation into fused multiply-add and multiply-subtract.
+//
+// Key:
+//
+// [+ -](x * y) [+ -] z.
+// _ N A S
+// D U
+// D B
+//
+// Note: multiplication commutativity handled by rule generator.
+(F(MADD|NMADD|MSUB|NMSUB)D neg:(FNEGD x) y z) && neg.Uses == 1 => (F(NMADD|MADD|NMSUB|MSUB)D x y z)
+(F(MADD|NMADD|MSUB|NMSUB)D x y neg:(FNEGD z)) && neg.Uses == 1 => (F(MSUB|NMSUB|MADD|NMADD)D x y z)
diff --git a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
index 0ac9c5f62ad176700958b5b648992cd3a3410e3e..076919773b47a3deb19862f5ebf74e6b5e79fa65 100644
--- a/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/RISCV64Ops.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -28,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
@@ -79,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
@@ -122,6 +124,7 @@ func init() {
gp01 = regInfo{outputs: []regMask{gpMask}}
gp11 = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}}
gp21 = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask}}
+ gp22 = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask, gpMask}}
gpload = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{gpMask}}
gp11sb = regInfo{inputs: []regMask{gpspsbMask}, outputs: []regMask{gpMask}}
gpxchg = regInfo{inputs: []regMask{gpspsbgMask, gpgMask}, outputs: []regMask{gpMask}}
@@ -130,6 +133,7 @@ func init() {
fp11 = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{fpMask}}
fp21 = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{fpMask}}
+ fp31 = regInfo{inputs: []regMask{fpMask, fpMask, fpMask}, outputs: []regMask{fpMask}}
gpfp = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{fpMask}}
fpgp = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{gpMask}}
fpstore = regInfo{inputs: []regMask{gpspsbMask, fpMask, 0}}
@@ -156,6 +160,9 @@ func init() {
{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, typ: "Int32"},
{name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int64"},
{name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt64"},
+ {name: "LoweredMuluhilo", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (hi, lo)
+ {name: "LoweredMuluover", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (64 bits of arg0*arg1, overflow)
+
{name: "DIV", argLength: 2, reg: gp21, asm: "DIV", typ: "Int64"}, // arg0 / arg1
{name: "DIVU", argLength: 2, reg: gp21, asm: "DIVU", typ: "UInt64"},
{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"},
@@ -234,9 +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: "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)
@@ -420,8 +428,14 @@ func init() {
{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", commutative: false, typ: "Float64"}, // arg0 - arg1
{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, typ: "Float64"}, // arg0 * arg1
{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", commutative: false, typ: "Float64"}, // arg0 / arg1
+ {name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", commutative: true, typ: "Float64"}, // (arg0 * arg1) + arg2
+ {name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD", commutative: true, typ: "Float64"}, // (arg0 * arg1) - arg2
+ {name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD", commutative: true, typ: "Float64"}, // -(arg0 * arg1) + arg2
+ {name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64"}, // -(arg0 * arg1) - arg2
{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"}, // sqrt(arg0)
{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"}, // -arg0
+ {name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", typ: "Float64"}, // abs(arg0)
+ {name: "FSGNJD", argLength: 2, reg: fp21, asm: "FSGNJD", typ: "Float64"}, // copy sign of arg1 to arg0
{name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"}, // reinterpret arg0 as float
{name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"}, // float64(low 32 bits of arg0)
{name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"}, // float64(arg0)
diff --git a/src/cmd/compile/internal/ssa/gen/S390X.rules b/src/cmd/compile/internal/ssa/gen/S390X.rules
index 88762f704589067c018bb110090397d7d51261de..b3928c6a1e6e4df84c241835c310dde974287b1d 100644
--- a/src/cmd/compile/internal/ssa/gen/S390X.rules
+++ b/src/cmd/compile/internal/ssa/gen/S390X.rules
@@ -434,6 +434,7 @@
(StaticCall ...) => (CALLstatic ...)
(ClosureCall ...) => (CALLclosure ...)
(InterCall ...) => (CALLinter ...)
+(TailCall ...) => (CALLtail ...)
// Miscellaneous
(IsNonNil p) => (LOCGR {s390x.NotEqual} (MOVDconst [0]) (MOVDconst [1]) (CMPconst p [0]))
diff --git a/src/cmd/compile/internal/ssa/gen/S390XOps.go b/src/cmd/compile/internal/ssa/gen/S390XOps.go
index 5b33ba710e9818799df21ca130049381ced03c5b..eef8a2557ca39d51e91cb5c5715d3b4d5678c0a2 100644
--- a/src/cmd/compile/internal/ssa/gen/S390XOps.go
+++ b/src/cmd/compile/internal/ssa/gen/S390XOps.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -479,6 +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, 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
@@ -507,8 +509,9 @@ func init() {
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
// It saves all GP registers if necessary,
- // but clobbers R14 (LR) because it's a call.
- {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
+ // but clobbers R14 (LR) because it's a call,
+ // and also clobbers R1 as the PLT stub does.
+ {name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14") | r1}, clobberFlags: true, aux: "Sym", symEffect: "None"},
// There are three of these functions so that they can have three different register inputs.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
diff --git a/src/cmd/compile/internal/ssa/gen/Wasm.rules b/src/cmd/compile/internal/ssa/gen/Wasm.rules
index 7ad3d1c72e1fb04db56f2b937410dd030de4fea7..9e683b116c1613423505e365cc7ffc3bfd071901 100644
--- a/src/cmd/compile/internal/ssa/gen/Wasm.rules
+++ b/src/cmd/compile/internal/ssa/gen/Wasm.rules
@@ -307,6 +307,7 @@
(StaticCall ...) => (LoweredStaticCall ...)
(ClosureCall ...) => (LoweredClosureCall ...)
(InterCall ...) => (LoweredInterCall ...)
+(TailCall ...) => (LoweredTailCall ...)
// Miscellaneous
(Convert ...) => (LoweredConvert ...)
diff --git a/src/cmd/compile/internal/ssa/gen/WasmOps.go b/src/cmd/compile/internal/ssa/gen/WasmOps.go
index c92878ca73b844a1a906829ba70b903e48391f90..edfba4ee99b774962a26cedf43994527ee939d11 100644
--- a/src/cmd/compile/internal/ssa/gen/WasmOps.go
+++ b/src/cmd/compile/internal/ssa/gen/WasmOps.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
@@ -123,6 +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, 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/dec64Ops.go b/src/cmd/compile/internal/ssa/gen/dec64Ops.go
index 8c5883bc5691348147d6845b20af96e961007100..78fcea885aa8b250304d0064e32ad0b03f52bc8e 100644
--- a/src/cmd/compile/internal/ssa/gen/dec64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/dec64Ops.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
diff --git a/src/cmd/compile/internal/ssa/gen/decOps.go b/src/cmd/compile/internal/ssa/gen/decOps.go
index b826481c9fbb2fdcb90b41742d66561f88a64014..d5cd79378c76a3041e007bc197c171f7272a4bda 100644
--- a/src/cmd/compile/internal/ssa/gen/decOps.go
+++ b/src/cmd/compile/internal/ssa/gen/decOps.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
package main
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index 5cbc70cf41d1c68c928c203c9fc77c53baa8882d..6dbe9b47d011fabd3c83e493b126a981efa45191 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -500,6 +500,9 @@
(Leq32 (Const32 [0]) (Rsh32Ux64 _ (Const64 [c]))) && c > 0 => (ConstBool [true])
(Leq64 (Const64 [0]) (Rsh64Ux64 _ (Const64 [c]))) && c > 0 => (ConstBool [true])
+(Less(64|32|16|8) (Const(64|32|16|8) [0]) x) && isNonNegative(x) => (Neq(64|32|16|8) (Const(64|32|16|8) [0]) x)
+(Less(64|32|16|8) x (Const(64|32|16|8) [1])) && isNonNegative(x) => (Eq(64|32|16|8) (Const(64|32|16|8) [0]) x)
+
// constant floating point comparisons
(Eq32F (Const32F [c]) (Const32F [d])) => (ConstBool [c == d])
(Eq64F (Const64F [c]) (Const64F [d])) => (ConstBool [c == d])
@@ -590,6 +593,10 @@
// simplifications often used for lengths. e.g. len(s[i:i+5])==5
(Sub(64|32|16|8) (Add(64|32|16|8) x y) x) => y
(Sub(64|32|16|8) (Add(64|32|16|8) x y) y) => x
+(Sub(64|32|16|8) (Sub(64|32|16|8) x y) x) => (Neg(64|32|16|8) y)
+(Sub(64|32|16|8) x (Add(64|32|16|8) x y)) => (Neg(64|32|16|8) y)
+(Add(64|32|16|8) x (Sub(64|32|16|8) y x)) => y
+(Add(64|32|16|8) x (Add(64|32|16|8) y (Sub(64|32|16|8) z x))) => (Add(64|32|16|8) y z)
// basic phi simplifications
(Phi (Const8 [c]) (Const8 [c])) => (Const8 [c])
@@ -2004,12 +2011,30 @@
=> (Invalid)
// for late-expanded calls, recognize memequal applied to a single constant byte
-// TODO figure out breakeven number of bytes for this optimization.
+// Support is limited by 1, 2, 4, 8 byte sizes
(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [1]) mem)
&& isSameCall(callAux, "runtime.memequal")
&& symIsRO(scon)
=> (MakeResult (Eq8 (Load sptr mem) (Const8 [int8(read8(scon,0))])) mem)
+(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [2]) mem)
+ && isSameCall(callAux, "runtime.memequal")
+ && symIsRO(scon)
+ && canLoadUnaligned(config)
+ => (MakeResult (Eq16 (Load sptr mem) (Const16 [int16(read16(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+
+(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [4]) mem)
+ && isSameCall(callAux, "runtime.memequal")
+ && symIsRO(scon)
+ && canLoadUnaligned(config)
+ => (MakeResult (Eq32 (Load sptr mem) (Const32 [int32(read32(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+
+(StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [8]) mem)
+ && isSameCall(callAux, "runtime.memequal")
+ && symIsRO(scon)
+ && canLoadUnaligned(config) && config.PtrSize == 8
+ => (MakeResult (Eq64 (Load sptr mem) (Const64 [int64(read64(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+
// Evaluate constant address comparisons.
(EqPtr x x) => (ConstBool [true])
(NeqPtr x x) => (ConstBool [false])
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 9f6664386c9d1749e51c4d6bd3a29c6fecd7d37d..4f133b1ff6a1b85de79dec35a4360d9cb2d2a231 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -106,7 +106,7 @@ var genericOps = []opData{
// For shifts, AxB means the shifted value has A bits and the shift amount has B bits.
// Shift amounts are considered unsigned.
- // If arg1 is known to be less than the number of bits in arg0,
+ // If arg1 is known to be nonnegative and less than the number of bits in arg0,
// then auxInt may be set to 1.
// This enables better code generation on some platforms.
{name: "Lsh8x8", argLength: 2, aux: "Bool"}, // arg0 << arg1
@@ -417,10 +417,12 @@ var genericOps = []opData{
{name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=code pointer, arg1=context ptr, arg2..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
{name: "StaticCall", argLength: -1, aux: "CallOff", call: true}, // call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
{name: "InterCall", argLength: -1, aux: "CallOff", call: true}, // interface call. arg0=code pointer, arg1..argN-1 are register inputs, argN=memory, auxint=arg size. Returns Result of register results, plus memory.
+ {name: "TailCall", argLength: -1, aux: "CallOff", call: true}, // tail call function aux.(*obj.LSym), arg0..argN-1 are register inputs, argN=memory. auxint=arg size. Returns Result of register results, plus memory.
{name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded closure call. arg0=code pointer, arg1=context ptr, arg2..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
{name: "StaticLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
{name: "InterLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded interface call. arg0=code pointer, arg1..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
+ {name: "TailLECall", argLength: -1, aux: "CallOff", call: true}, // late-expanded static tail call function aux.(*ssa.AuxCall.Fn). arg0..argN-1 are inputs, argN is mem. auxint = arg size. Result is tuple of result(s), plus mem.
// Conversions: signed extensions, zero (unsigned) extensions, truncations
{name: "SignExt8to16", argLength: 1, typ: "Int16"},
@@ -615,9 +617,16 @@ var genericOps = []opData{
{name: "AtomicOr8Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory.
{name: "AtomicOr32Variant", argLength: 3, typ: "Mem", hasSideEffects: true}, // *arg0 |= arg1. arg2=memory. Returns memory.
+ // Publication barrier
+ {name: "PubBarrier", argLength: 1, hasSideEffects: true}, // Do data barrier. arg0=memory.
+
// Clobber experiment op
{name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // write an invalid pointer value to the given pointer slot of a stack variable
{name: "ClobberReg", argLength: 0, typ: "Void"}, // clobber a register
+
+ // Prefetch instruction
+ {name: "PrefetchCache", argLength: 2, hasSideEffects: true}, // Do prefetch arg0 to cache. arg0=addr, arg1=memory.
+ {name: "PrefetchCacheStreamed", argLength: 2, hasSideEffects: true}, // Do non-temporal or streamed prefetch arg0 to cache. arg0=addr, arg1=memory.
}
// kind controls successors implicit exit
@@ -634,7 +643,7 @@ var genericBlocks = []blockData{
{name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1]
{name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type)
{name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result
- {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is memory result, jumps to b.Aux.(*gc.Sym)
+ {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call
{name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic
// transient block state used for dead code removal
diff --git a/src/cmd/compile/internal/ssa/gen/main.go b/src/cmd/compile/internal/ssa/gen/main.go
index 8e5997b25a447339b1bd80b1604a772e2ef82b16..2cf0a919fa6ebc2b07871249ce391439c44bb4e9 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/html.go b/src/cmd/compile/internal/ssa/html.go
index 4d191199fbafe1b3f5f61882a40aced0e836cf36..d9a78b396243beb32e912549f89a83ef72c692a5 100644
--- a/src/cmd/compile/internal/ssa/html.go
+++ b/src/cmd/compile/internal/ssa/html.go
@@ -903,15 +903,12 @@ func (w *HTMLWriter) WriteAST(phase string, buf *bytes.Buffer) {
if strings.HasPrefix(l, "buildssa") {
escaped = fmt.Sprintf("%v", l)
} else {
- // Parse the line number from the format l(123).
- idx := strings.Index(l, " l(")
- if idx != -1 {
- subl := l[idx+3:]
- idxEnd := strings.Index(subl, ")")
- if idxEnd != -1 {
- if _, err := strconv.Atoi(subl[:idxEnd]); err == nil {
- lineNo = subl[:idxEnd]
- }
+ // Parse the line number from the format file:line:col.
+ // See the implementation in ir/fmt.go:dumpNodeHeader.
+ sl := strings.Split(l, ":")
+ if len(sl) >= 3 {
+ if _, err := strconv.Atoi(sl[len(sl)-2]); err == nil {
+ lineNo = sl[len(sl)-2]
}
}
escaped = html.EscapeString(l)
@@ -1221,7 +1218,7 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
}
}
-func (p htmlFuncPrinter) endBlock(b *Block) {
+func (p htmlFuncPrinter) endBlock(b *Block, reachable bool) {
if len(b.Values) > 0 { // end list of values
io.WriteString(p.w, "")
io.WriteString(p.w, "")
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index 252c47cdebc8b6ab4a4523e4042c3d34724520be..b575febd72741158bab236eeea768e8ac2677874 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -91,8 +91,8 @@ func (t LocPair) String() string {
type LocResults []Location
func (t LocResults) String() string {
- s := "<"
- a := ""
+ s := ""
+ a := "<"
for _, r := range t {
a += s
s = ","
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index f09a08abcf752102944a3aba872105ac9ebe392d..a1835dcd3026ecb3f9ef137e38af0033831440cf 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
@@ -254,13 +259,13 @@ func (a *AuxCall) TypeOfArg(which int64) *types.Type {
// SizeOfResult returns the size of result which (indexed 0, 1, etc).
func (a *AuxCall) SizeOfResult(which int64) int64 {
- return a.TypeOfResult(which).Width
+ return a.TypeOfResult(which).Size()
}
// SizeOfArg returns the size of argument which (indexed 0, 1, etc).
// If the call is to a method, the receiver is the first argument (i.e., index 0)
func (a *AuxCall) SizeOfArg(which int64) int64 {
- return a.TypeOfArg(which).Width
+ return a.TypeOfArg(which).Size()
}
// NResults returns the number of results
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 1c37fbe0db4a49b7c7ff6d6a9668a6fdc745218a..81fe5d4c23cbc6d99054e54f7e2c95688a465063 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -515,6 +515,7 @@ const (
Op386DUFFZERO
Op386REPSTOSL
Op386CALLstatic
+ Op386CALLtail
Op386CALLclosure
Op386CALLinter
Op386DUFFCOPY
@@ -962,7 +963,6 @@ const (
OpAMD64MOVQstore
OpAMD64MOVOload
OpAMD64MOVOstore
- OpAMD64MOVOstorezero
OpAMD64MOVBloadidx1
OpAMD64MOVWloadidx1
OpAMD64MOVWloadidx2
@@ -983,6 +983,7 @@ const (
OpAMD64MOVWstoreconst
OpAMD64MOVLstoreconst
OpAMD64MOVQstoreconst
+ OpAMD64MOVOstoreconst
OpAMD64MOVBstoreconstidx1
OpAMD64MOVWstoreconstidx1
OpAMD64MOVWstoreconstidx2
@@ -993,6 +994,7 @@ const (
OpAMD64DUFFZERO
OpAMD64REPSTOSQ
OpAMD64CALLstatic
+ OpAMD64CALLtail
OpAMD64CALLclosure
OpAMD64CALLinter
OpAMD64DUFFCOPY
@@ -1029,6 +1031,22 @@ const (
OpAMD64ANDLlock
OpAMD64ORBlock
OpAMD64ORLlock
+ OpAMD64PrefetchT0
+ OpAMD64PrefetchNTA
+ OpAMD64ANDNQ
+ OpAMD64ANDNL
+ OpAMD64BLSIQ
+ OpAMD64BLSIL
+ OpAMD64BLSMSKQ
+ OpAMD64BLSMSKL
+ OpAMD64BLSRQ
+ OpAMD64BLSRL
+ OpAMD64TZCNTQ
+ OpAMD64TZCNTL
+ OpAMD64MOVBELload
+ OpAMD64MOVBELstore
+ OpAMD64MOVBEQload
+ OpAMD64MOVBEQstore
OpARMADD
OpARMADDconst
@@ -1267,6 +1285,7 @@ const (
OpARMCMOVWLSconst
OpARMSRAcond
OpARMCALLstatic
+ OpARMCALLtail
OpARMCALLclosure
OpARMCALLinter
OpARMLoweredNilCheck
@@ -1407,6 +1426,7 @@ const (
OpARM64MVNshiftLL
OpARM64MVNshiftRL
OpARM64MVNshiftRA
+ OpARM64MVNshiftRO
OpARM64NEGshiftLL
OpARM64NEGshiftRL
OpARM64NEGshiftRA
@@ -1419,21 +1439,27 @@ const (
OpARM64ANDshiftLL
OpARM64ANDshiftRL
OpARM64ANDshiftRA
+ OpARM64ANDshiftRO
OpARM64ORshiftLL
OpARM64ORshiftRL
OpARM64ORshiftRA
+ OpARM64ORshiftRO
OpARM64XORshiftLL
OpARM64XORshiftRL
OpARM64XORshiftRA
+ OpARM64XORshiftRO
OpARM64BICshiftLL
OpARM64BICshiftRL
OpARM64BICshiftRA
+ OpARM64BICshiftRO
OpARM64EONshiftLL
OpARM64EONshiftRL
OpARM64EONshiftRA
+ OpARM64EONshiftRO
OpARM64ORNshiftLL
OpARM64ORNshiftRL
OpARM64ORNshiftRA
+ OpARM64ORNshiftRO
OpARM64CMPshiftLL
OpARM64CMPshiftRL
OpARM64CMPshiftRA
@@ -1443,6 +1469,7 @@ const (
OpARM64TSTshiftLL
OpARM64TSTshiftRL
OpARM64TSTshiftRA
+ OpARM64TSTshiftRO
OpARM64BFI
OpARM64BFXIL
OpARM64SBFIZ
@@ -1550,6 +1577,7 @@ const (
OpARM64CSNEG
OpARM64CSETM
OpARM64CALLstatic
+ OpARM64CALLtail
OpARM64CALLclosure
OpARM64CALLinter
OpARM64LoweredNilCheck
@@ -1610,6 +1638,8 @@ const (
OpARM64LoweredPanicBoundsA
OpARM64LoweredPanicBoundsB
OpARM64LoweredPanicBoundsC
+ OpARM64PRFM
+ OpARM64DMB
OpMIPSADD
OpMIPSADDconst
@@ -1694,6 +1724,7 @@ const (
OpMIPSMOVFD
OpMIPSMOVDF
OpMIPSCALLstatic
+ OpMIPSCALLtail
OpMIPSCALLclosure
OpMIPSCALLinter
OpMIPSLoweredAtomicLoad8
@@ -1810,6 +1841,7 @@ const (
OpMIPS64MOVFD
OpMIPS64MOVDF
OpMIPS64CALLstatic
+ OpMIPS64CALLtail
OpMIPS64CALLclosure
OpMIPS64CALLinter
OpMIPS64DUFFZERO
@@ -1966,6 +1998,7 @@ const (
OpPPC64MOVDBRloadidx
OpPPC64FMOVDloadidx
OpPPC64FMOVSloadidx
+ OpPPC64DCBT
OpPPC64MOVDBRstore
OpPPC64MOVWBRstore
OpPPC64MOVHBRstore
@@ -2022,6 +2055,7 @@ const (
OpPPC64LoweredRound32F
OpPPC64LoweredRound64F
OpPPC64CALLstatic
+ OpPPC64CALLtail
OpPPC64CALLclosure
OpPPC64CALLinter
OpPPC64LoweredZero
@@ -2069,6 +2103,8 @@ const (
OpRISCV64MULW
OpRISCV64MULH
OpRISCV64MULHU
+ OpRISCV64LoweredMuluhilo
+ OpRISCV64LoweredMuluover
OpRISCV64DIV
OpRISCV64DIVU
OpRISCV64DIVW
@@ -2123,6 +2159,7 @@ const (
OpRISCV64SLTIU
OpRISCV64MOVconvert
OpRISCV64CALLstatic
+ OpRISCV64CALLtail
OpRISCV64CALLclosure
OpRISCV64CALLinter
OpRISCV64DUFFZERO
@@ -2172,8 +2209,14 @@ const (
OpRISCV64FSUBD
OpRISCV64FMULD
OpRISCV64FDIVD
+ OpRISCV64FMADDD
+ OpRISCV64FMSUBD
+ OpRISCV64FNMADDD
+ OpRISCV64FNMSUBD
OpRISCV64FSQRTD
OpRISCV64FNEGD
+ OpRISCV64FABSD
+ OpRISCV64FSGNJD
OpRISCV64FMVDX
OpRISCV64FCVTDW
OpRISCV64FCVTDL
@@ -2375,6 +2418,7 @@ const (
OpS390XMOVDstoreconst
OpS390XCLEAR
OpS390XCALLstatic
+ OpS390XCALLtail
OpS390XCALLclosure
OpS390XCALLinter
OpS390XInvertFlags
@@ -2428,6 +2472,7 @@ const (
OpS390XLoweredZero
OpWasmLoweredStaticCall
+ OpWasmLoweredTailCall
OpWasmLoweredClosureCall
OpWasmLoweredInterCall
OpWasmLoweredAddr
@@ -2774,9 +2819,11 @@ const (
OpClosureCall
OpStaticCall
OpInterCall
+ OpTailCall
OpClosureLECall
OpStaticLECall
OpInterLECall
+ OpTailLECall
OpSignExt8to16
OpSignExt8to32
OpSignExt8to64
@@ -2910,8 +2957,11 @@ const (
OpAtomicAnd32Variant
OpAtomicOr8Variant
OpAtomicOr32Variant
+ OpPubBarrier
OpClobber
OpClobberReg
+ OpPrefetchCache
+ OpPrefetchCacheStreamed
)
var opcodeTable = [...]opInfo{
@@ -4687,7 +4737,6 @@ var opcodeTable = [...]opInfo{
name: "NOTL",
argLen: 1,
resultInArg0: true,
- clobberFlags: true,
asm: x86.ANOTL,
reg: regInfo{
inputs: []inputInfo{
@@ -5893,6 +5942,17 @@ var opcodeTable = [...]opInfo{
clobbers: 65519, // AX CX DX BX BP SI DI X0 X1 X2 X3 X4 X5 X6 X7
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -10721,7 +10781,6 @@ var opcodeTable = [...]opInfo{
name: "NOTQ",
argLen: 1,
resultInArg0: true,
- clobberFlags: true,
asm: x86.ANOTQ,
reg: regInfo{
inputs: []inputInfo{
@@ -10736,7 +10795,6 @@ var opcodeTable = [...]opInfo{
name: "NOTL",
argLen: 1,
resultInArg0: true,
- clobberFlags: true,
asm: x86.ANOTL,
reg: regInfo{
inputs: []inputInfo{
@@ -12623,19 +12681,6 @@ var opcodeTable = [...]opInfo{
},
},
},
- {
- name: "MOVOstorezero",
- auxType: auxSymOff,
- argLen: 2,
- faultOnNilArg0: true,
- symEffect: SymWrite,
- asm: x86.AMOVUPS,
- reg: regInfo{
- inputs: []inputInfo{
- {0, 4295016447}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15 SB
- },
- },
- },
{
name: "MOVBloadidx1",
auxType: auxSymOff,
@@ -12952,6 +12997,19 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "MOVOstoreconst",
+ auxType: auxSymValAndOff,
+ argLen: 2,
+ faultOnNilArg0: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVUPS,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
{
name: "MOVBstoreconstidx1",
auxType: auxSymValAndOff,
@@ -13090,6 +13148,17 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -13553,6 +13622,230 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "PrefetchT0",
+ argLen: 2,
+ hasSideEffects: true,
+ asm: x86.APREFETCHT0,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "PrefetchNTA",
+ argLen: 2,
+ hasSideEffects: true,
+ asm: x86.APREFETCHNTA,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "ANDNQ",
+ argLen: 2,
+ clobberFlags: true,
+ asm: x86.AANDNQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "ANDNL",
+ argLen: 2,
+ clobberFlags: true,
+ asm: x86.AANDNL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSIQ",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSIQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSIL",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSIL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSMSKQ",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSMSKQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSMSKL",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSMSKL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSRQ",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSRQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "BLSRL",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ABLSRL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "TZCNTQ",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ATZCNTQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "TZCNTL",
+ argLen: 1,
+ clobberFlags: true,
+ asm: x86.ATZCNTL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ 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",
@@ -16904,6 +17197,17 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -18730,6 +19034,20 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "MVNshiftRO",
+ auxType: auxInt64,
+ argLen: 1,
+ asm: arm64.AMVN,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "NEGshiftLL",
auxType: auxInt64,
@@ -18907,6 +19225,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "ANDshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.AAND,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "ORshiftLL",
auxType: auxInt64,
@@ -18952,6 +19285,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "ORshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.AORR,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "XORshiftLL",
auxType: auxInt64,
@@ -18997,6 +19345,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "XORshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.AEOR,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "BICshiftLL",
auxType: auxInt64,
@@ -19042,6 +19405,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "BICshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.ABIC,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "EONshiftLL",
auxType: auxInt64,
@@ -19087,6 +19465,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "EONshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.AEON,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "ORNshiftLL",
auxType: auxInt64,
@@ -19132,6 +19525,21 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "ORNshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.AORN,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ outputs: []outputInfo{
+ {0, 670826495}, // 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 R30
+ },
+ },
+ },
{
name: "CMPshiftLL",
auxType: auxInt64,
@@ -19240,6 +19648,18 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "TSTshiftRO",
+ auxType: auxInt64,
+ argLen: 2,
+ asm: arm64.ATST,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 805044223}, // 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
+ {1, 805044223}, // 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
+ },
+ },
+ },
{
name: "BFI",
auxType: auxARM64BitField,
@@ -20664,17 +21084,28 @@ var opcodeTable = [...]opInfo{
{
name: "CALLstatic",
auxType: auxCallOff,
- argLen: 1,
+ argLen: -1,
clobberFlags: true,
call: 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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
- argLen: 3,
+ argLen: -1,
clobberFlags: true,
call: true,
reg: regInfo{
@@ -20688,7 +21119,7 @@ var opcodeTable = [...]opInfo{
{
name: "CALLinter",
auxType: auxCallOff,
- argLen: 2,
+ argLen: -1,
clobberFlags: true,
call: true,
reg: regInfo{
@@ -21445,6 +21876,26 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "PRFM",
+ auxType: auxInt64,
+ argLen: 2,
+ hasSideEffects: true,
+ asm: arm64.APRFM,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 9223372038733561855}, // 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 SP SB
+ },
+ },
+ },
+ {
+ name: "DMB",
+ auxType: auxInt64,
+ argLen: 1,
+ hasSideEffects: true,
+ asm: arm64.ADMB,
+ reg: regInfo{},
+ },
{
name: "ADD",
@@ -22592,6 +23043,17 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -24151,6 +24613,17 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -26310,6 +26783,18 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "DCBT",
+ auxType: auxInt64,
+ argLen: 2,
+ hasSideEffects: true,
+ asm: ppc64.ADCBT,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 1073733630}, // SP SB 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
+ },
+ },
+ },
{
name: "MOVDBRstore",
auxType: auxSym,
@@ -26972,9 +27457,20 @@ var opcodeTable = [...]opInfo{
{
name: "CALLstatic",
auxType: auxCallOff,
- argLen: 1,
+ argLen: -1,
+ clobberFlags: true,
+ call: 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
+ },
+ },
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
},
@@ -26982,7 +27478,7 @@ var opcodeTable = [...]opInfo{
{
name: "CALLclosure",
auxType: auxCallOff,
- argLen: 3,
+ argLen: -1,
clobberFlags: true,
call: true,
reg: regInfo{
@@ -26996,7 +27492,7 @@ var opcodeTable = [...]opInfo{
{
name: "CALLinter",
auxType: auxCallOff,
- argLen: 2,
+ argLen: -1,
clobberFlags: true,
call: true,
reg: regInfo{
@@ -27453,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
},
},
},
@@ -27468,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
},
},
},
@@ -27482,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
},
},
},
@@ -27495,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
},
},
},
@@ -27508,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
},
},
},
@@ -27521,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
},
},
},
@@ -27535,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
},
},
},
@@ -27550,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
},
},
},
@@ -27565,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
},
},
},
@@ -27580,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
},
},
},
@@ -27595,11 +28091,41 @@ 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
+ },
+ },
+ },
+ {
+ name: "LoweredMuluhilo",
+ argLen: 2,
+ resultNotInArgs: true,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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, 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
+ },
+ },
+ },
+ {
+ name: "LoweredMuluover",
+ argLen: 2,
+ resultNotInArgs: true,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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, 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
},
},
},
@@ -27609,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
},
},
},
@@ -27623,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
},
},
},
@@ -27637,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
},
},
},
@@ -27651,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
},
},
},
@@ -27665,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
},
},
},
@@ -27679,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
},
},
},
@@ -27693,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
},
},
},
@@ -27707,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
},
},
},
@@ -27724,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
},
},
},
@@ -27739,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
},
},
},
@@ -27752,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
},
},
},
@@ -27768,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
},
},
},
@@ -27784,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
},
},
},
@@ -27800,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
},
},
},
@@ -27816,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
},
},
},
@@ -27832,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
},
},
},
@@ -27848,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
},
},
},
@@ -27864,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
},
},
},
@@ -27878,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
},
},
},
@@ -27892,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
},
},
},
@@ -27906,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
},
},
},
@@ -27920,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
},
},
},
@@ -27933,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
},
},
},
@@ -27946,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
},
},
},
@@ -27959,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
},
},
},
@@ -27969,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
},
},
},
@@ -27982,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
},
},
},
@@ -27995,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
},
},
},
@@ -28008,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
},
},
},
@@ -28021,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
},
},
},
@@ -28034,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
},
},
},
@@ -28047,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
},
},
},
@@ -28060,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
},
},
},
@@ -28073,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
},
},
},
@@ -28087,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
},
},
},
@@ -28101,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
},
},
},
@@ -28116,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
},
},
},
@@ -28130,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
},
},
},
@@ -28144,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
},
},
},
@@ -28158,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
},
},
},
@@ -28173,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
},
},
},
@@ -28187,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
},
},
},
@@ -28202,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
},
},
},
@@ -28216,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
},
},
},
@@ -28231,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
},
},
},
@@ -28244,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
},
},
},
@@ -28257,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
},
},
},
@@ -28270,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
},
},
},
@@ -28283,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
},
},
},
@@ -28298,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
},
},
},
@@ -28311,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
},
},
},
@@ -28326,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
},
},
},
@@ -28339,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
},
},
},
@@ -28352,7 +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,
+ tailCall: true,
+ reg: regInfo{
+ 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
},
},
{
@@ -28363,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
},
},
{
@@ -28375,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
},
},
{
@@ -28414,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
},
@@ -28429,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
},
@@ -28440,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
},
},
},
@@ -28453,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
},
},
},
@@ -28466,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
},
},
},
@@ -28480,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
},
},
},
@@ -28492,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
},
},
},
@@ -28504,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
},
},
},
@@ -28517,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
},
},
},
@@ -28533,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
},
},
},
@@ -28550,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
},
},
},
@@ -28567,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
},
},
},
@@ -28584,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
},
},
},
@@ -28602,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
},
},
},
@@ -28619,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
},
},
},
@@ -28632,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
},
},
},
@@ -28644,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
},
},
},
@@ -28663,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
},
},
},
@@ -28673,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
},
},
},
@@ -28817,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
@@ -28830,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
@@ -28843,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
@@ -28859,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
},
},
},
@@ -28872,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
},
},
},
@@ -28885,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
@@ -28901,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
},
},
@@ -28917,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
},
},
},
@@ -28932,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
},
},
},
@@ -28946,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
},
},
},
@@ -28960,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
},
},
},
@@ -29022,6 +29558,70 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "FMADDD",
+ argLen: 3,
+ commutative: true,
+ asm: riscv.AFMADDD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {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
+ {2, 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, 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
+ },
+ },
+ },
+ {
+ name: "FMSUBD",
+ argLen: 3,
+ commutative: true,
+ asm: riscv.AFMSUBD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {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
+ {2, 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, 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
+ },
+ },
+ },
+ {
+ name: "FNMADDD",
+ argLen: 3,
+ commutative: true,
+ asm: riscv.AFNMADDD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {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
+ {2, 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, 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
+ },
+ },
+ },
+ {
+ name: "FNMSUBD",
+ argLen: 3,
+ commutative: true,
+ asm: riscv.AFNMSUBD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {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
+ {2, 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, 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
+ },
+ },
+ },
{
name: "FSQRTD",
argLen: 1,
@@ -29048,13 +29648,40 @@ var opcodeTable = [...]opInfo{
},
},
},
+ {
+ name: "FABSD",
+ argLen: 1,
+ asm: riscv.AFABSD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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, 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
+ },
+ },
+ },
+ {
+ name: "FSGNJD",
+ argLen: 2,
+ asm: riscv.AFSGNJD,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {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
+ {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, 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
+ },
+ },
+ },
{
name: "FMVDX",
argLen: 1,
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
@@ -29067,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
@@ -29080,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
@@ -29096,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
},
},
},
@@ -29109,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
},
},
},
@@ -29148,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
@@ -29164,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
},
},
@@ -29180,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
},
},
},
@@ -29195,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
},
},
},
@@ -29209,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
},
},
},
@@ -29223,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
},
},
},
@@ -32019,6 +32646,17 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ name: "CALLtail",
+ auxType: auxCallOff,
+ 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
+ },
+ },
{
name: "CALLclosure",
auxType: auxCallOff,
@@ -32141,7 +32779,7 @@ var opcodeTable = [...]opInfo{
{0, 4}, // R2
{1, 8}, // R3
},
- clobbers: 4294918144, // R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
+ clobbers: 4294918146, // R1 R14 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15
},
},
{
@@ -32689,6 +33327,16 @@ var opcodeTable = [...]opInfo{
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
},
},
+ {
+ 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
+ },
+ },
{
name: "LoweredClosureCall",
auxType: auxCallOff,
@@ -35465,6 +36113,13 @@ var opcodeTable = [...]opInfo{
call: true,
generic: true,
},
+ {
+ name: "TailCall",
+ auxType: auxCallOff,
+ argLen: -1,
+ call: true,
+ generic: true,
+ },
{
name: "ClosureLECall",
auxType: auxCallOff,
@@ -35486,6 +36141,13 @@ var opcodeTable = [...]opInfo{
call: true,
generic: true,
},
+ {
+ name: "TailLECall",
+ auxType: auxCallOff,
+ argLen: -1,
+ call: true,
+ generic: true,
+ },
{
name: "SignExt8to16",
argLen: 1,
@@ -36201,6 +36863,12 @@ var opcodeTable = [...]opInfo{
hasSideEffects: true,
generic: true,
},
+ {
+ name: "PubBarrier",
+ argLen: 1,
+ hasSideEffects: true,
+ generic: true,
+ },
{
name: "Clobber",
auxType: auxSymOff,
@@ -36213,6 +36881,18 @@ var opcodeTable = [...]opInfo{
argLen: 0,
generic: true,
},
+ {
+ name: "PrefetchCache",
+ argLen: 2,
+ hasSideEffects: true,
+ generic: true,
+ },
+ {
+ name: "PrefetchCacheStreamed",
+ argLen: 2,
+ hasSideEffects: true,
+ generic: true,
+ },
}
func (o Op) Asm() obj.As { return opcodeTable[o].asm }
@@ -36220,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 }
@@ -36400,8 +37081,8 @@ var registersARM64 = [...]Register{
{62, arm64.REG_F31, -1, "F31"},
{63, 0, -1, "SB"},
}
-var paramIntRegARM64 = []int8(nil)
-var paramFloatRegARM64 = []int8(nil)
+var paramIntRegARM64 = []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+var paramFloatRegARM64 = []int8{31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46}
var gpRegMaskARM64 = regMask(670826495)
var fpRegMaskARM64 = regMask(9223372034707292160)
var specialRegMaskARM64 = regMask(0)
@@ -36602,44 +37283,44 @@ var registersPPC64 = [...]Register{
{62, ppc64.REG_F30, -1, "F30"},
{63, ppc64.REG_F31, -1, "F31"},
}
-var paramIntRegPPC64 = []int8(nil)
-var paramFloatRegPPC64 = []int8(nil)
+var paramIntRegPPC64 = []int8{3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17}
+var paramFloatRegPPC64 = []int8{33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}
var gpRegMaskPPC64 = regMask(1073733624)
var fpRegMaskPPC64 = regMask(576460743713488896)
var specialRegMaskPPC64 = regMask(0)
-var framepointerRegPPC64 = int8(1)
+var framepointerRegPPC64 = int8(-1)
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"},
@@ -36677,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/print.go b/src/cmd/compile/internal/ssa/print.go
index d917183c70f5e8f23ff904a6421983e8a1770b25..96cd2c7c90a539677ec393f03708c49106a6372d 100644
--- a/src/cmd/compile/internal/ssa/print.go
+++ b/src/cmd/compile/internal/ssa/print.go
@@ -6,6 +6,7 @@ package ssa
import (
"bytes"
+ "cmd/internal/src"
"crypto/sha256"
"fmt"
"io"
@@ -17,22 +18,30 @@ func printFunc(f *Func) {
func hashFunc(f *Func) []byte {
h := sha256.New()
- p := stringFuncPrinter{w: h}
+ p := stringFuncPrinter{w: h, printDead: true}
fprintFunc(p, f)
return h.Sum(nil)
}
func (f *Func) String() string {
var buf bytes.Buffer
- p := stringFuncPrinter{w: &buf}
+ p := stringFuncPrinter{w: &buf, printDead: true}
fprintFunc(p, f)
return buf.String()
}
+// rewriteHash returns a hash of f suitable for detecting rewrite cycles.
+func (f *Func) rewriteHash() string {
+ h := sha256.New()
+ p := stringFuncPrinter{w: h, printDead: false}
+ fprintFunc(p, f)
+ return fmt.Sprintf("%x", h.Sum(nil))
+}
+
type funcPrinter interface {
header(f *Func)
startBlock(b *Block, reachable bool)
- endBlock(b *Block)
+ endBlock(b *Block, reachable bool)
value(v *Value, live bool)
startDepCycle()
endDepCycle()
@@ -40,7 +49,8 @@ type funcPrinter interface {
}
type stringFuncPrinter struct {
- w io.Writer
+ w io.Writer
+ printDead bool
}
func (p stringFuncPrinter) header(f *Func) {
@@ -50,6 +60,9 @@ func (p stringFuncPrinter) header(f *Func) {
}
func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
+ if !p.printDead && !reachable {
+ return
+ }
fmt.Fprintf(p.w, " b%d:", b.ID)
if len(b.Preds) > 0 {
io.WriteString(p.w, " <-")
@@ -64,14 +77,33 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
io.WriteString(p.w, "\n")
}
-func (p stringFuncPrinter) endBlock(b *Block) {
+func (p stringFuncPrinter) endBlock(b *Block, reachable bool) {
+ if !p.printDead && !reachable {
+ return
+ }
fmt.Fprintln(p.w, " "+b.LongString())
}
+func StmtString(p src.XPos) string {
+ linenumber := "(?) "
+ if p.IsKnown() {
+ pfx := ""
+ if p.IsStmt() == src.PosIsStmt {
+ pfx = "+"
+ }
+ if p.IsStmt() == src.PosNotStmt {
+ pfx = "-"
+ }
+ linenumber = fmt.Sprintf("(%s%d) ", pfx, p.Line())
+ }
+ return linenumber
+}
+
func (p stringFuncPrinter) value(v *Value, live bool) {
- fmt.Fprint(p.w, " ")
- //fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos))
- //fmt.Fprint(p.w, ": ")
+ if !p.printDead && !live {
+ return
+ }
+ fmt.Fprintf(p.w, " %s", StmtString(v.Pos))
fmt.Fprint(p.w, v.LongString())
if !live {
fmt.Fprint(p.w, " DEAD")
@@ -103,7 +135,7 @@ func fprintFunc(p funcPrinter, f *Func) {
p.value(v, live[v.ID])
printed[v.ID] = true
}
- p.endBlock(b)
+ p.endBlock(b, reachable[b.ID])
continue
}
@@ -151,7 +183,7 @@ func fprintFunc(p funcPrinter, f *Func) {
}
}
- p.endBlock(b)
+ p.endBlock(b, reachable[b.ID])
}
for _, name := range f.Names {
p.named(*name, f.NamedValues[*name])
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index 3b90b8769c2f1d8ffe484b0ab40d832aea650175..64792d0c80b241663f70ae336024fd773bc20a97 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
}
}
@@ -620,20 +621,22 @@ func (s *regAllocState) init(f *Func) {
}
if s.f.Config.ctxt.Flag_dynlink {
switch s.f.Config.arch {
- case "amd64":
- s.allocatable &^= 1 << 15 // R15
- case "arm":
- s.allocatable &^= 1 << 9 // R9
- case "ppc64le": // R2 already reserved.
- // nothing to do
- case "arm64":
- // nothing to do?
case "386":
// nothing to do.
// Note that for Flag_shared (position independent code)
// we do need to be careful, but that carefulness is hidden
// in the rewrite rules so we always have a free register
// available for global load/stores. See gen/386.rules (search for Flag_shared).
+ case "amd64":
+ s.allocatable &^= 1 << 15 // R15
+ case "arm":
+ s.allocatable &^= 1 << 9 // R9
+ case "arm64":
+ // 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
@@ -1865,23 +1868,6 @@ func (s *regAllocState) regalloc(f *Func) {
}
func (s *regAllocState) placeSpills() {
- f := s.f
-
- // Precompute some useful info.
- phiRegs := make([]regMask, f.NumBlocks())
- for _, b := range s.visitOrder {
- var m regMask
- for _, v := range b.Values {
- if v.Op != OpPhi {
- break
- }
- if r, ok := f.getHome(v.ID).(*Register); ok {
- m |= regMask(1) << uint(r.num)
- }
- }
- phiRegs[b.ID] = m
- }
-
mustBeFirst := func(op Op) bool {
return op.isLoweredGetClosurePtr() || op == OpPhi || op == OpArgIntReg || op == OpArgFloatReg
}
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index 375c4d5a5605f531b6b5ab761be276f41d72031b..9136c59e65494f8ee56341965a652f04bd1466ab 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -36,6 +36,8 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
if debug > 1 {
fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name)
}
+ var iters int
+ var states map[string]bool
for {
change := false
for _, b := range f.Blocks {
@@ -146,6 +148,30 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
if !change {
break
}
+ iters++
+ if iters > 1000 || debug >= 2 {
+ // We've done a suspiciously large number of rewrites (or we're in debug mode).
+ // As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer
+ // and the maximum value encountered during make.bash is 12.
+ // Start checking for cycles. (This is too expensive to do routinely.)
+ if states == nil {
+ states = make(map[string]bool)
+ }
+ h := f.rewriteHash()
+ if _, ok := states[h]; ok {
+ // We've found a cycle.
+ // To diagnose it, set debug to 2 and start again,
+ // so that we'll print all rules applied until we complete another cycle.
+ // If debug is already >= 2, we've already done that, so it's time to crash.
+ if debug < 2 {
+ debug = 2
+ states = make(map[string]bool)
+ } else {
+ f.Fatalf("rewrite cycle detected")
+ }
+ }
+ states[h] = true
+ }
}
// remove clobbered values
for _, b := range f.Blocks {
@@ -389,6 +415,11 @@ func isSameCall(sym interface{}, name string) bool {
return fn != nil && fn.String() == name
}
+// canLoadUnaligned reports if the achitecture supports unaligned load operations
+func canLoadUnaligned(c *Config) bool {
+ return c.ctxt.Arch.Alignment == 1
+}
+
// nlz returns the number of leading zeros.
func nlz64(x int64) int { return bits.LeadingZeros64(uint64(x)) }
func nlz32(x int32) int { return bits.LeadingZeros32(uint32(x)) }
@@ -745,27 +776,21 @@ func uaddOvf(a, b int64) bool {
return uint64(a)+uint64(b) < uint64(a)
}
-// de-virtualize an InterCall
-// 'sym' is the symbol for the itab
-func devirt(v *Value, aux Aux, sym Sym, offset int64) *AuxCall {
- f := v.Block.Func
- n, ok := sym.(*obj.LSym)
- if !ok {
+// loadLSymOffset simulates reading a word at an offset into a
+// read-only symbol's runtime memory. If it would read a pointer to
+// another symbol, that symbol is returned. Otherwise, it returns nil.
+func loadLSymOffset(lsym *obj.LSym, offset int64) *obj.LSym {
+ if lsym.Type != objabi.SRODATA {
return nil
}
- lsym := f.fe.DerefItab(n, offset)
- if f.pass.debug > 0 {
- if lsym != nil {
- f.Warnl(v.Pos, "de-virtualizing call")
- } else {
- f.Warnl(v.Pos, "couldn't de-virtualize call")
+
+ for _, r := range lsym.R {
+ if int64(r.Off) == offset && r.Type&^objabi.R_WEAK == objabi.R_ADDR && r.Add == 0 {
+ return r.Sym
}
}
- if lsym == nil {
- return nil
- }
- va := aux.(*AuxCall)
- return StaticAuxCall(lsym, va.abiInfo)
+
+ return nil
}
// de-virtualize an InterLECall
@@ -776,18 +801,14 @@ func devirtLESym(v *Value, aux Aux, sym Sym, offset int64) *obj.LSym {
return nil
}
- f := v.Block.Func
- lsym := f.fe.DerefItab(n, offset)
- if f.pass.debug > 0 {
+ lsym := loadLSymOffset(n, offset)
+ if f := v.Block.Func; f.pass.debug > 0 {
if lsym != nil {
f.Warnl(v.Pos, "de-virtualizing call")
} else {
f.Warnl(v.Pos, "couldn't de-virtualize call")
}
}
- if lsym == nil {
- return nil
- }
return lsym
}
@@ -795,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
}
@@ -1263,7 +1288,7 @@ func zeroUpper32Bits(x *Value, depth int) bool {
OpAMD64SHLL, OpAMD64SHLLconst:
return true
case OpArg:
- return x.Type.Width == 4
+ return x.Type.Size() == 4
case OpPhi, OpSelect0, OpSelect1:
// Phis can use each-other as an arguments, instead of tracking visited values,
// just limit recursion depth.
@@ -1287,7 +1312,7 @@ func zeroUpper48Bits(x *Value, depth int) bool {
case OpAMD64MOVWQZX, OpAMD64MOVWload, OpAMD64MOVWloadidx1, OpAMD64MOVWloadidx2:
return true
case OpArg:
- return x.Type.Width == 2
+ return x.Type.Size() == 2
case OpPhi, OpSelect0, OpSelect1:
// Phis can use each-other as an arguments, instead of tracking visited values,
// just limit recursion depth.
@@ -1311,7 +1336,7 @@ func zeroUpper56Bits(x *Value, depth int) bool {
case OpAMD64MOVBQZX, OpAMD64MOVBload, OpAMD64MOVBloadidx1:
return true
case OpArg:
- return x.Type.Width == 1
+ return x.Type.Size() == 1
case OpPhi, OpSelect0, OpSelect1:
// Phis can use each-other as an arguments, instead of tracking visited values,
// just limit recursion depth.
@@ -1551,12 +1576,16 @@ func rotateLeft32(v, rotate int64) int64 {
return int64(bits.RotateLeft32(uint32(v), int(rotate)))
}
+func rotateRight64(v, rotate int64) int64 {
+ return int64(bits.RotateLeft64(uint64(v), int(-rotate)))
+}
+
// encodes the lsb and width for arm(64) bitfield ops into the expected auxInt format.
func armBFAuxInt(lsb, width int64) arm64BitField {
if lsb < 0 || lsb > 63 {
panic("ARM(64) bit field lsb constant out of range")
}
- if width < 1 || width > 64 {
+ if width < 1 || lsb+width > 64 {
panic("ARM(64) bit field width constant out of range")
}
return arm64BitField(width | lsb<<8)
diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go
index 1ec2d26f750f6b7998933fca8cbeb668d9188f1a..34f37867cf6149140eb883b4f67f37e26c1cb179 100644
--- a/src/cmd/compile/internal/ssa/rewrite386.go
+++ b/src/cmd/compile/internal/ssa/rewrite386.go
@@ -652,6 +652,9 @@ func rewriteValue386(v *Value) bool {
case OpSubPtr:
v.Op = Op386SUBL
return true
+ case OpTailCall:
+ v.Op = Op386CALLtail
+ return true
case OpTrunc16to8:
v.Op = OpCopy
return true
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index 5045ba7351f447ce13814b8ee6b3580cb9ed7ea6..0c789d6b496477c66c4213b220d1cd9d4a2e6708 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -54,6 +54,10 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64ANDLload(v)
case OpAMD64ANDLmodify:
return rewriteValueAMD64_OpAMD64ANDLmodify(v)
+ case OpAMD64ANDNL:
+ return rewriteValueAMD64_OpAMD64ANDNL(v)
+ case OpAMD64ANDNQ:
+ return rewriteValueAMD64_OpAMD64ANDNQ(v)
case OpAMD64ANDQ:
return rewriteValueAMD64_OpAMD64ANDQ(v)
case OpAMD64ANDQconst:
@@ -66,6 +70,10 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64ANDQmodify(v)
case OpAMD64BSFQ:
return rewriteValueAMD64_OpAMD64BSFQ(v)
+ case OpAMD64BSWAPL:
+ return rewriteValueAMD64_OpAMD64BSWAPL(v)
+ case OpAMD64BSWAPQ:
+ return rewriteValueAMD64_OpAMD64BSWAPQ(v)
case OpAMD64BTCLconst:
return rewriteValueAMD64_OpAMD64BTCLconst(v)
case OpAMD64BTCQconst:
@@ -214,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:
@@ -250,6 +262,8 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64MOVOload(v)
case OpAMD64MOVOstore:
return rewriteValueAMD64_OpAMD64MOVOstore(v)
+ case OpAMD64MOVOstoreconst:
+ return rewriteValueAMD64_OpAMD64MOVOstoreconst(v)
case OpAMD64MOVQatomicload:
return rewriteValueAMD64_OpAMD64MOVQatomicload(v)
case OpAMD64MOVQf2i:
@@ -641,13 +655,11 @@ func rewriteValueAMD64(v *Value) bool {
case OpCtz16:
return rewriteValueAMD64_OpCtz16(v)
case OpCtz16NonZero:
- v.Op = OpAMD64BSFL
- return true
+ return rewriteValueAMD64_OpCtz16NonZero(v)
case OpCtz32:
return rewriteValueAMD64_OpCtz32(v)
case OpCtz32NonZero:
- v.Op = OpAMD64BSFL
- return true
+ return rewriteValueAMD64_OpCtz32NonZero(v)
case OpCtz64:
return rewriteValueAMD64_OpCtz64(v)
case OpCtz64NonZero:
@@ -655,8 +667,7 @@ func rewriteValueAMD64(v *Value) bool {
case OpCtz8:
return rewriteValueAMD64_OpCtz8(v)
case OpCtz8NonZero:
- v.Op = OpAMD64BSFL
- return true
+ return rewriteValueAMD64_OpCtz8NonZero(v)
case OpCvt32Fto32:
v.Op = OpAMD64CVTTSS2SL
return true
@@ -950,6 +961,12 @@ func rewriteValueAMD64(v *Value) bool {
return true
case OpPopCount8:
return rewriteValueAMD64_OpPopCount8(v)
+ case OpPrefetchCache:
+ v.Op = OpAMD64PrefetchT0
+ return true
+ case OpPrefetchCacheStreamed:
+ v.Op = OpAMD64PrefetchNTA
+ return true
case OpRotateLeft16:
v.Op = OpAMD64ROLW
return true
@@ -1096,6 +1113,9 @@ func rewriteValueAMD64(v *Value) bool {
case OpSubPtr:
v.Op = OpAMD64SUBQ
return true
+ case OpTailCall:
+ v.Op = OpAMD64CALLtail
+ return true
case OpTrunc:
return rewriteValueAMD64_OpTrunc(v)
case OpTrunc16to8:
@@ -2749,6 +2769,55 @@ func rewriteValueAMD64_OpAMD64ANDL(v *Value) bool {
}
break
}
+ // match: (ANDL x (NOTL y))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (ANDNL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64NOTL {
+ continue
+ }
+ y := v_1.Args[0]
+ if !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64ANDNL)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (ANDL x (NEGL x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSIL x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSIL)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
+ // match: (ANDL x (ADDLconst [-1] x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSRL x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSRL)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueAMD64_OpAMD64ANDLconst(v *Value) bool {
@@ -3027,6 +3096,48 @@ func rewriteValueAMD64_OpAMD64ANDLmodify(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64ANDNL(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (ANDNL x (SHLL (MOVLconst [1]) y))
+ // result: (BTRL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64SHLL {
+ break
+ }
+ y := v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpAMD64BTRL)
+ v.AddArg2(x, y)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64ANDNQ(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (ANDNQ x (SHLQ (MOVQconst [1]) y))
+ // result: (BTRQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64SHLQ {
+ break
+ }
+ y := v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpAMD64BTRQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -3128,6 +3239,55 @@ func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool {
}
break
}
+ // match: (ANDQ x (NOTQ y))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (ANDNQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64NOTQ {
+ continue
+ }
+ y := v_1.Args[0]
+ if !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64ANDNQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (ANDQ x (NEGQ x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSIQ x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSIQ)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
+ // match: (ANDQ x (ADDQconst [-1] x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSRQ x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSRQ)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueAMD64_OpAMD64ANDQconst(v *Value) bool {
@@ -3455,6 +3615,108 @@ func rewriteValueAMD64_OpAMD64BSFQ(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64BSWAPL(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (BSWAPL (BSWAPL p))
+ // result: p
+ for {
+ if v_0.Op != OpAMD64BSWAPL {
+ break
+ }
+ p := v_0.Args[0]
+ 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 {
+ v_0 := v.Args[0]
+ // match: (BSWAPQ (BSWAPQ p))
+ // result: p
+ for {
+ if v_0.Op != OpAMD64BSWAPQ {
+ break
+ }
+ p := v_0.Args[0]
+ 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 {
v_0 := v.Args[0]
// match: (BTCLconst [c] (XORLconst [d] x))
@@ -9211,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
@@ -9631,49 +9939,6 @@ func rewriteValueAMD64_OpAMD64MOVBload(v *Value) bool {
v.AddArg2(base, mem)
return true
}
- // match: (MOVBload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVBload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(base, mem)
- return true
- }
- // match: (MOVBload [off1] {sym} (ADDLconst [off2] ptr) mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVBload [off1+off2] {sym} ptr mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVBload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg2(ptr, mem)
- return true
- }
// match: (MOVBload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVLconst [int32(read8(sym, int64(off)))])
@@ -10878,51 +11143,6 @@ func rewriteValueAMD64_OpAMD64MOVBstore(v *Value) bool {
v.AddArg3(p, v0, mem)
return true
}
- // match: (MOVBstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVBstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg3(base, val, mem)
- return true
- }
- // match: (MOVBstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVBstore [off1+off2] {sym} ptr val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVBstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, val, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool {
@@ -11021,49 +11241,6 @@ func rewriteValueAMD64_OpAMD64MOVBstoreconst(v *Value) bool {
v.AddArg2(p, mem)
return true
}
- // match: (MOVBstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
- // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off)
- // result: (MOVBstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- ptr := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVBstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(ptr, mem)
- return true
- }
- // match: (MOVBstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
- // cond: sc.canAdd32(off)
- // result: (MOVBstoreconst [sc.addOffset32(off)] {s} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- s := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVBstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(s)
- v.AddArg2(ptr, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value) bool {
@@ -11492,49 +11669,6 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value) bool {
v.AddArg2(base, mem)
return true
}
- // match: (MOVLload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVLload [off1+off2] {mergeSym(sym1,sym2)} base mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVLload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(base, mem)
- return true
- }
- // match: (MOVLload [off1] {sym} (ADDLconst [off2] ptr) mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVLload [off1+off2] {sym} ptr mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVLload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg2(ptr, mem)
- return true
- }
// match: (MOVLload [off] {sym} ptr (MOVSSstore [off] {sym} ptr val _))
// result: (MOVLf2i val)
for {
@@ -11836,51 +11970,6 @@ func rewriteValueAMD64_OpAMD64MOVLstore(v *Value) bool {
v.AddArg3(p, v0, mem)
return true
}
- // match: (MOVLstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVLstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVLstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg3(base, val, mem)
- return true
- }
- // match: (MOVLstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVLstore [off1+off2] {sym} ptr val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVLstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, val, mem)
- return true
- }
// match: (MOVLstore {sym} [off] ptr y:(ADDLload x [off] {sym} ptr mem) mem)
// cond: y.Uses==1 && clobber(y)
// result: (ADDLmodify [off] {sym} ptr x mem)
@@ -12260,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 {
@@ -12364,49 +12475,6 @@ func rewriteValueAMD64_OpAMD64MOVLstoreconst(v *Value) bool {
v.AddArg3(p, v0, mem)
return true
}
- // match: (MOVLstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
- // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off)
- // result: (MOVLstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- ptr := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVLstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(ptr, mem)
- return true
- }
- // match: (MOVLstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
- // cond: sc.canAdd32(off)
- // result: (MOVLstoreconst [sc.addOffset32(off)] {s} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- s := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVLstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(s)
- v.AddArg2(ptr, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64MOVOload(v *Value) bool {
@@ -12545,6 +12613,54 @@ func rewriteValueAMD64_OpAMD64MOVOstore(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64MOVOstoreconst(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (MOVOstoreconst [sc] {s} (ADDQconst [off] ptr) mem)
+ // cond: ValAndOff(sc).canAdd32(off)
+ // result: (MOVOstoreconst [ValAndOff(sc).addOffset32(off)] {s} ptr mem)
+ for {
+ sc := auxIntToValAndOff(v.AuxInt)
+ s := auxToSym(v.Aux)
+ if v_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ off := auxIntToInt32(v_0.AuxInt)
+ ptr := v_0.Args[0]
+ mem := v_1
+ if !(ValAndOff(sc).canAdd32(off)) {
+ break
+ }
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(ValAndOff(sc).addOffset32(off))
+ v.Aux = symToAux(s)
+ v.AddArg2(ptr, mem)
+ return true
+ }
+ // match: (MOVOstoreconst [sc] {sym1} (LEAQ [off] {sym2} ptr) mem)
+ // cond: canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off)
+ // result: (MOVOstoreconst [ValAndOff(sc).addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
+ for {
+ sc := auxIntToValAndOff(v.AuxInt)
+ sym1 := auxToSym(v.Aux)
+ if v_0.Op != OpAMD64LEAQ {
+ break
+ }
+ off := auxIntToInt32(v_0.AuxInt)
+ sym2 := auxToSym(v_0.Aux)
+ ptr := v_0.Args[0]
+ mem := v_1
+ if !(canMergeSym(sym1, sym2) && ValAndOff(sc).canAdd32(off)) {
+ break
+ }
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(ValAndOff(sc).addOffset32(off))
+ v.Aux = symToAux(mergeSym(sym1, sym2))
+ v.AddArg2(ptr, mem)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64MOVQatomicload(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -12713,49 +12829,6 @@ func rewriteValueAMD64_OpAMD64MOVQload(v *Value) bool {
v.AddArg2(base, mem)
return true
}
- // match: (MOVQload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVQload [off1+off2] {mergeSym(sym1,sym2)} base mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVQload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(base, mem)
- return true
- }
- // match: (MOVQload [off1] {sym} (ADDLconst [off2] ptr) mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVQload [off1+off2] {sym} ptr mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVQload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg2(ptr, mem)
- return true
- }
// match: (MOVQload [off] {sym} ptr (MOVSDstore [off] {sym} ptr val _))
// result: (MOVQf2i val)
for {
@@ -12858,51 +12931,6 @@ func rewriteValueAMD64_OpAMD64MOVQstore(v *Value) bool {
v.AddArg3(base, val, mem)
return true
}
- // match: (MOVQstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVQstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVQstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg3(base, val, mem)
- return true
- }
- // match: (MOVQstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVQstore [off1+off2] {sym} ptr val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVQstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, val, mem)
- return true
- }
// match: (MOVQstore {sym} [off] ptr y:(ADDQload x [off] {sym} ptr mem) mem)
// cond: y.Uses==1 && clobber(y)
// result: (ADDQmodify [off] {sym} ptr x mem)
@@ -13282,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 {
@@ -13332,9 +13382,9 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool {
v.AddArg2(ptr, mem)
return true
}
- // match: (MOVQstoreconst [c] {s} p x:(MOVQstoreconst [c2] {s} p mem))
- // cond: config.useSSE && x.Uses == 1 && c2.Off() + 8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x)
- // result: (MOVOstorezero [c2.Off()] {s} p mem)
+ // match: (MOVQstoreconst [c] {s} p x:(MOVQstoreconst [a] {s} p mem))
+ // cond: config.useSSE && x.Uses == 1 && a.Off() + 8 == c.Off() && a.Val() == 0 && c.Val() == 0 && clobber(x)
+ // result: (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p mem)
for {
c := auxIntToValAndOff(v.AuxInt)
s := auxToSym(v.Aux)
@@ -13343,61 +13393,43 @@ func rewriteValueAMD64_OpAMD64MOVQstoreconst(v *Value) bool {
if x.Op != OpAMD64MOVQstoreconst {
break
}
- c2 := auxIntToValAndOff(x.AuxInt)
+ a := auxIntToValAndOff(x.AuxInt)
if auxToSym(x.Aux) != s {
break
}
mem := x.Args[1]
- if p != x.Args[0] || !(config.useSSE && x.Uses == 1 && c2.Off()+8 == c.Off() && c.Val() == 0 && c2.Val() == 0 && clobber(x)) {
+ if p != x.Args[0] || !(config.useSSE && x.Uses == 1 && a.Off()+8 == c.Off() && a.Val() == 0 && c.Val() == 0 && clobber(x)) {
break
}
- v.reset(OpAMD64MOVOstorezero)
- v.AuxInt = int32ToAuxInt(c2.Off())
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, a.Off()))
v.Aux = symToAux(s)
v.AddArg2(p, mem)
return true
}
- // match: (MOVQstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
- // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off)
- // result: (MOVQstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
+ // match: (MOVQstoreconst [a] {s} p x:(MOVQstoreconst [c] {s} p mem))
+ // cond: config.useSSE && x.Uses == 1 && a.Off() + 8 == c.Off() && a.Val() == 0 && c.Val() == 0 && clobber(x)
+ // result: (MOVOstoreconst [makeValAndOff(0,a.Off())] {s} p mem)
for {
- sc := auxIntToValAndOff(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- ptr := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) {
+ a := auxIntToValAndOff(v.AuxInt)
+ s := auxToSym(v.Aux)
+ p := v_0
+ x := v_1
+ if x.Op != OpAMD64MOVQstoreconst {
break
}
- v.reset(OpAMD64MOVQstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(ptr, mem)
- return true
- }
- // match: (MOVQstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
- // cond: sc.canAdd32(off)
- // result: (MOVQstoreconst [sc.addOffset32(off)] {s} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- s := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
+ c := auxIntToValAndOff(x.AuxInt)
+ if auxToSym(x.Aux) != s {
break
}
- off := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(sc.canAdd32(off)) {
+ mem := x.Args[1]
+ if p != x.Args[0] || !(config.useSSE && x.Uses == 1 && a.Off()+8 == c.Off() && a.Val() == 0 && c.Val() == 0 && clobber(x)) {
break
}
- v.reset(OpAMD64MOVQstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, a.Off()))
v.Aux = symToAux(s)
- v.AddArg2(ptr, mem)
+ v.AddArg2(p, mem)
return true
}
return false
@@ -14018,49 +14050,6 @@ func rewriteValueAMD64_OpAMD64MOVWload(v *Value) bool {
v.AddArg2(base, mem)
return true
}
- // match: (MOVWload [off1] {sym1} (LEAL [off2] {sym2} base) mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVWload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(base, mem)
- return true
- }
- // match: (MOVWload [off1] {sym} (ADDLconst [off2] ptr) mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVWload [off1+off2] {sym} ptr mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVWload)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg2(ptr, mem)
- return true
- }
// match: (MOVWload [off] {sym} (SB) _)
// cond: symIsRO(sym)
// result: (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
@@ -14454,51 +14443,6 @@ func rewriteValueAMD64_OpAMD64MOVWstore(v *Value) bool {
v.AddArg3(p, v0, mem)
return true
}
- // match: (MOVWstore [off1] {sym1} (LEAL [off2] {sym2} base) val mem)
- // cond: canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))
- // result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- base := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVWstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg3(base, val, mem)
- return true
- }
- // match: (MOVWstore [off1] {sym} (ADDLconst [off2] ptr) val mem)
- // cond: is32Bit(int64(off1)+int64(off2))
- // result: (MOVWstore [off1+off2] {sym} ptr val mem)
- for {
- off1 := auxIntToInt32(v.AuxInt)
- sym := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off2 := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- val := v_1
- mem := v_2
- if !(is32Bit(int64(off1) + int64(off2))) {
- break
- }
- v.reset(OpAMD64MOVWstore)
- v.AuxInt = int32ToAuxInt(off1 + off2)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, val, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool {
@@ -14597,49 +14541,6 @@ func rewriteValueAMD64_OpAMD64MOVWstoreconst(v *Value) bool {
v.AddArg2(p, mem)
return true
}
- // match: (MOVWstoreconst [sc] {sym1} (LEAL [off] {sym2} ptr) mem)
- // cond: canMergeSym(sym1, sym2) && sc.canAdd32(off)
- // result: (MOVWstoreconst [sc.addOffset32(off)] {mergeSym(sym1, sym2)} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- sym1 := auxToSym(v.Aux)
- if v_0.Op != OpAMD64LEAL {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- sym2 := auxToSym(v_0.Aux)
- ptr := v_0.Args[0]
- mem := v_1
- if !(canMergeSym(sym1, sym2) && sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVWstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(mergeSym(sym1, sym2))
- v.AddArg2(ptr, mem)
- return true
- }
- // match: (MOVWstoreconst [sc] {s} (ADDLconst [off] ptr) mem)
- // cond: sc.canAdd32(off)
- // result: (MOVWstoreconst [sc.addOffset32(off)] {s} ptr mem)
- for {
- sc := auxIntToValAndOff(v.AuxInt)
- s := auxToSym(v.Aux)
- if v_0.Op != OpAMD64ADDLconst {
- break
- }
- off := auxIntToInt32(v_0.AuxInt)
- ptr := v_0.Args[0]
- mem := v_1
- if !(sc.canAdd32(off)) {
- break
- }
- v.reset(OpAMD64MOVWstoreconst)
- v.AuxInt = valAndOffToAuxInt(sc.addOffset32(off))
- v.Aux = symToAux(s)
- v.AddArg2(ptr, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64MULL(v *Value) bool {
@@ -18924,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 {
@@ -26915,6 +26891,21 @@ func rewriteValueAMD64_OpAMD64XORL(v *Value) bool {
}
break
}
+ // match: (XORL x (ADDLconst [-1] x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSMSKL x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSMSKL)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueAMD64_OpAMD64XORLconst(v *Value) bool {
@@ -27391,6 +27382,21 @@ func rewriteValueAMD64_OpAMD64XORQ(v *Value) bool {
}
break
}
+ // match: (XORQ x (ADDQconst [-1] x))
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (BLSMSKQ x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst || auxIntToInt32(v_1.AuxInt) != -1 || x != v_1.Args[0] || !(buildcfg.GOAMD64 >= 3) {
+ continue
+ }
+ v.reset(OpAMD64BLSMSKQ)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueAMD64_OpAMD64XORQconst(v *Value) bool {
@@ -28960,14 +28966,58 @@ func rewriteValueAMD64_OpCtz16(v *Value) bool {
return true
}
}
+func rewriteValueAMD64_OpCtz16NonZero(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (Ctz16NonZero x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTL)
+ v.AddArg(x)
+ return true
+ }
+ // match: (Ctz16NonZero x)
+ // cond: buildcfg.GOAMD64 < 3
+ // result: (BSFL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
+ v.reset(OpAMD64BSFL)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpCtz32(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (Ctz32 x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTL)
+ v.AddArg(x)
+ return true
+ }
+ // match: (Ctz32 x)
+ // cond: buildcfg.GOAMD64 < 3
// result: (Select0 (BSFQ (BTSQconst [32] x)))
for {
x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
v.reset(OpSelect0)
v0 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags))
v1 := b.NewValue0(v.Pos, OpAMD64BTSQconst, typ.UInt64)
@@ -28977,16 +29027,61 @@ func rewriteValueAMD64_OpCtz32(v *Value) bool {
v.AddArg(v0)
return true
}
+ return false
+}
+func rewriteValueAMD64_OpCtz32NonZero(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (Ctz32NonZero x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTL)
+ v.AddArg(x)
+ return true
+ }
+ // match: (Ctz32NonZero x)
+ // cond: buildcfg.GOAMD64 < 3
+ // result: (BSFL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
+ v.reset(OpAMD64BSFL)
+ v.AddArg(x)
+ return true
+ }
+ return false
}
func rewriteValueAMD64_OpCtz64(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
+ // match: (Ctz64 x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTQ x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTQ)
+ v.AddArg(x)
+ return true
+ }
// match: (Ctz64 x)
+ // cond: buildcfg.GOAMD64 < 3
// result: (CMOVQEQ (Select0 (BSFQ x)) (MOVQconst [64]) (Select1 (BSFQ x)))
for {
t := v.Type
x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
v.reset(OpAMD64CMOVQEQ)
v0 := b.NewValue0(v.Pos, OpSelect0, t)
v1 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags))
@@ -28999,21 +29094,39 @@ func rewriteValueAMD64_OpCtz64(v *Value) bool {
v.AddArg3(v0, v2, v3)
return true
}
+ return false
}
func rewriteValueAMD64_OpCtz64NonZero(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
typ := &b.Func.Config.Types
// match: (Ctz64NonZero x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTQ x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTQ)
+ v.AddArg(x)
+ return true
+ }
+ // match: (Ctz64NonZero x)
+ // cond: buildcfg.GOAMD64 < 3
// result: (Select0 (BSFQ x))
for {
x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
v.reset(OpSelect0)
v0 := b.NewValue0(v.Pos, OpAMD64BSFQ, types.NewTuple(typ.UInt64, types.TypeFlags))
v0.AddArg(x)
v.AddArg(v0)
return true
}
+ return false
}
func rewriteValueAMD64_OpCtz8(v *Value) bool {
v_0 := v.Args[0]
@@ -29031,6 +29144,34 @@ func rewriteValueAMD64_OpCtz8(v *Value) bool {
return true
}
}
+func rewriteValueAMD64_OpCtz8NonZero(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (Ctz8NonZero x)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (TZCNTL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64TZCNTL)
+ v.AddArg(x)
+ return true
+ }
+ // match: (Ctz8NonZero x)
+ // cond: buildcfg.GOAMD64 < 3
+ // result: (BSFL x)
+ for {
+ x := v_0
+ if !(buildcfg.GOAMD64 < 3) {
+ break
+ }
+ v.reset(OpAMD64BSFL)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpDiv16(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -29339,11 +29480,11 @@ func rewriteValueAMD64_OpFloor(v *Value) bool {
func rewriteValueAMD64_OpGetG(v *Value) bool {
v_0 := v.Args[0]
// match: (GetG mem)
- // cond: !(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal)
+ // cond: v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal
// result: (LoweredGetG mem)
for {
mem := v_0
- if !(!(buildcfg.Experiment.RegabiG && v.Block.Func.OwnAux.Fn.ABI() == obj.ABIInternal)) {
+ if !(v.Block.Func.OwnAux.Fn.ABI() != obj.ABIInternal) {
break
}
v.reset(OpAMD64LoweredGetG)
@@ -29356,11 +29497,11 @@ func rewriteValueAMD64_OpHasCPUFeature(v *Value) bool {
b := v.Block
typ := &b.Func.Config.Types
// match: (HasCPUFeature {s})
- // result: (SETNE (CMPQconst [0] (LoweredHasCPUFeature {s})))
+ // result: (SETNE (CMPLconst [0] (LoweredHasCPUFeature {s})))
for {
s := auxToSym(v.Aux)
v.reset(OpAMD64SETNE)
- v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags)
+ v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags)
v0.AuxInt = int32ToAuxInt(0)
v1 := b.NewValue0(v.Pos, OpAMD64LoweredHasCPUFeature, typ.UInt64)
v1.Aux = symToAux(s)
@@ -33459,7 +33600,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
}
// match: (Zero [s] destptr mem)
// cond: s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE
- // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstorezero destptr mem))
+ // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
for {
s := auxIntToInt64(v.AuxInt)
destptr := v_0
@@ -33472,14 +33613,15 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(s % 16)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
v1.AddArg2(destptr, mem)
v.AddArg2(v0, v1)
return true
}
// match: (Zero [s] destptr mem)
// cond: s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE
- // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVQstoreconst [makeValAndOff(0,0)] destptr mem))
+ // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
for {
s := auxIntToInt64(v.AuxInt)
destptr := v_0
@@ -33492,7 +33634,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
v0.AuxInt = int64ToAuxInt(s % 16)
v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVQstoreconst, types.TypeMem)
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
v1.AddArg2(destptr, mem)
v.AddArg2(v0, v1)
@@ -33500,7 +33642,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
}
// match: (Zero [16] destptr mem)
// cond: config.useSSE
- // result: (MOVOstorezero destptr mem)
+ // result: (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)
for {
if auxIntToInt64(v.AuxInt) != 16 {
break
@@ -33510,13 +33652,14 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstorezero)
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
v.AddArg2(destptr, mem)
return true
}
// match: (Zero [32] destptr mem)
// cond: config.useSSE
- // result: (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem))
+ // result: (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))
for {
if auxIntToInt64(v.AuxInt) != 32 {
break
@@ -33526,18 +33669,17 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstorezero)
- v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v0.AuxInt = int64ToAuxInt(16)
- v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v1.AddArg2(destptr, mem)
- v.AddArg2(v0, v1)
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
+ v0.AddArg2(destptr, mem)
+ v.AddArg2(destptr, v0)
return true
}
// match: (Zero [48] destptr mem)
// cond: config.useSSE
- // result: (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem)))
+ // result: (MOVOstoreconst [makeValAndOff(0,32)] destptr (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)))
for {
if auxIntToInt64(v.AuxInt) != 48 {
break
@@ -33547,23 +33689,20 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstorezero)
- v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v0.AuxInt = int64ToAuxInt(32)
- v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v2.AuxInt = int64ToAuxInt(16)
- v2.AddArg(destptr)
- v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v3.AddArg2(destptr, mem)
- v1.AddArg2(v2, v3)
- v.AddArg2(v0, v1)
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 32))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16))
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
+ v1.AddArg2(destptr, mem)
+ v0.AddArg2(destptr, v1)
+ v.AddArg2(destptr, v0)
return true
}
// match: (Zero [64] destptr mem)
// cond: config.useSSE
- // result: (MOVOstorezero (OffPtr destptr [48]) (MOVOstorezero (OffPtr destptr [32]) (MOVOstorezero (OffPtr destptr [16]) (MOVOstorezero destptr mem))))
+ // result: (MOVOstoreconst [makeValAndOff(0,48)] destptr (MOVOstoreconst [makeValAndOff(0,32)] destptr (MOVOstoreconst [makeValAndOff(0,16)] destptr (MOVOstoreconst [makeValAndOff(0,0)] destptr mem))))
for {
if auxIntToInt64(v.AuxInt) != 64 {
break
@@ -33573,23 +33712,18 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
if !(config.useSSE) {
break
}
- v.reset(OpAMD64MOVOstorezero)
- v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v0.AuxInt = int64ToAuxInt(48)
- v0.AddArg(destptr)
- v1 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v2 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v2.AuxInt = int64ToAuxInt(32)
- v2.AddArg(destptr)
- v3 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v4 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type)
- v4.AuxInt = int64ToAuxInt(16)
- v4.AddArg(destptr)
- v5 := b.NewValue0(v.Pos, OpAMD64MOVOstorezero, types.TypeMem)
- v5.AddArg2(destptr, mem)
- v3.AddArg2(v4, v5)
- v1.AddArg2(v2, v3)
- v.AddArg2(v0, v1)
+ v.reset(OpAMD64MOVOstoreconst)
+ v.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 48))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v0.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 32))
+ v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 16))
+ v2 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem)
+ v2.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0))
+ v2.AddArg2(destptr, mem)
+ v1.AddArg2(destptr, v2)
+ v0.AddArg2(destptr, v1)
+ v.AddArg2(destptr, v0)
return true
}
// match: (Zero [s] destptr mem)
diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go
index febb5566e338492cb6f42e30d013b454b579fa4a..496f9b4ae2ef117031e08f43f97c575c371721d1 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM.go
@@ -338,6 +338,8 @@ func rewriteValueARM(v *Value) bool {
return rewriteValueARM_OpARMSRL(v)
case OpARMSRLconst:
return rewriteValueARM_OpARMSRLconst(v)
+ case OpARMSRR:
+ return rewriteValueARM_OpARMSRR(v)
case OpARMSUB:
return rewriteValueARM_OpARMSUB(v)
case OpARMSUBD:
@@ -855,6 +857,9 @@ func rewriteValueARM(v *Value) bool {
case OpSubPtr:
v.Op = OpARMSUB
return true
+ case OpTailCall:
+ v.Op = OpARMCALLtail
+ return true
case OpTrunc16to8:
v.Op = OpCopy
return true
@@ -1119,6 +1124,7 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool {
return true
}
// match: (ADCshiftLLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (ADCshiftLL x y [c] flags)
for {
x := v_0
@@ -1128,6 +1134,9 @@ func rewriteValueARM_OpARMADCshiftLLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADCshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -1199,6 +1208,7 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool {
return true
}
// match: (ADCshiftRAreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (ADCshiftRA x y [c] flags)
for {
x := v_0
@@ -1208,6 +1218,9 @@ func rewriteValueARM_OpARMADCshiftRAreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADCshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -1279,6 +1292,7 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool {
return true
}
// match: (ADCshiftRLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (ADCshiftRL x y [c] flags)
for {
x := v_0
@@ -1288,6 +1302,9 @@ func rewriteValueARM_OpARMADCshiftRLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADCshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -1740,6 +1757,7 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool {
return true
}
// match: (ADDSshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDSshiftLL x y [c])
for {
x := v_0
@@ -1748,6 +1766,9 @@ func rewriteValueARM_OpARMADDSshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDSshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -1814,6 +1835,7 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool {
return true
}
// match: (ADDSshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDSshiftRA x y [c])
for {
x := v_0
@@ -1822,6 +1844,9 @@ func rewriteValueARM_OpARMADDSshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDSshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -1888,6 +1913,7 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool {
return true
}
// match: (ADDSshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDSshiftRL x y [c])
for {
x := v_0
@@ -1896,6 +1922,9 @@ func rewriteValueARM_OpARMADDSshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDSshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2124,6 +2153,7 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool {
return true
}
// match: (ADDshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDshiftLL x y [c])
for {
x := v_0
@@ -2132,6 +2162,9 @@ func rewriteValueARM_OpARMADDshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2198,6 +2231,7 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool {
return true
}
// match: (ADDshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDshiftRA x y [c])
for {
x := v_0
@@ -2206,6 +2240,9 @@ func rewriteValueARM_OpARMADDshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2288,6 +2325,7 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool {
return true
}
// match: (ADDshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ADDshiftRL x y [c])
for {
x := v_0
@@ -2296,6 +2334,9 @@ func rewriteValueARM_OpARMADDshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMADDshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2614,18 +2655,16 @@ func rewriteValueARM_OpARMANDshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftLL x y:(SLLconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftLL y:(SLLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2655,6 +2694,7 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool {
return true
}
// match: (ANDshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ANDshiftLL x y [c])
for {
x := v_0
@@ -2663,6 +2703,9 @@ func rewriteValueARM_OpARMANDshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMANDshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2705,18 +2748,16 @@ func rewriteValueARM_OpARMANDshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftRA x y:(SRAconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftRA y:(SRAconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2746,6 +2787,7 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool {
return true
}
// match: (ANDshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ANDshiftRA x y [c])
for {
x := v_0
@@ -2754,6 +2796,9 @@ func rewriteValueARM_OpARMANDshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMANDshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -2796,18 +2841,16 @@ func rewriteValueARM_OpARMANDshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftRL x y:(SRLconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftRL y:(SRLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2837,6 +2880,7 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool {
return true
}
// match: (ANDshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ANDshiftRL x y [c])
for {
x := v_0
@@ -2845,6 +2889,9 @@ func rewriteValueARM_OpARMANDshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMANDshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3091,17 +3138,15 @@ func rewriteValueARM_OpARMBICshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftLL (SLLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -3115,6 +3160,7 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (BICshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (BICshiftLL x y [c])
for {
x := v_0
@@ -3123,6 +3169,9 @@ func rewriteValueARM_OpARMBICshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMBICshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3147,17 +3196,15 @@ func rewriteValueARM_OpARMBICshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftRA (SRAconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -3171,6 +3218,7 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (BICshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (BICshiftRA x y [c])
for {
x := v_0
@@ -3179,6 +3227,9 @@ func rewriteValueARM_OpARMBICshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMBICshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3203,17 +3254,15 @@ func rewriteValueARM_OpARMBICshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftRL (SRLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -3227,6 +3276,7 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (BICshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (BICshiftRL x y [c])
for {
x := v_0
@@ -3235,6 +3285,9 @@ func rewriteValueARM_OpARMBICshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMBICshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3437,6 +3490,7 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool {
return true
}
// match: (CMNshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMNshiftLL x y [c])
for {
x := v_0
@@ -3445,6 +3499,9 @@ func rewriteValueARM_OpARMCMNshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMNshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3511,6 +3568,7 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool {
return true
}
// match: (CMNshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMNshiftRA x y [c])
for {
x := v_0
@@ -3519,6 +3577,9 @@ func rewriteValueARM_OpARMCMNshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMNshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -3585,6 +3646,7 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool {
return true
}
// match: (CMNshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMNshiftRL x y [c])
for {
x := v_0
@@ -3593,6 +3655,9 @@ func rewriteValueARM_OpARMCMNshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMNshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -4090,6 +4155,7 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool {
return true
}
// match: (CMPshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMPshiftLL x y [c])
for {
x := v_0
@@ -4098,6 +4164,9 @@ func rewriteValueARM_OpARMCMPshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMPshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -4168,6 +4237,7 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool {
return true
}
// match: (CMPshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMPshiftRA x y [c])
for {
x := v_0
@@ -4176,6 +4246,9 @@ func rewriteValueARM_OpARMCMPshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMPshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -4246,6 +4319,7 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool {
return true
}
// match: (CMPshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (CMPshiftRL x y [c])
for {
x := v_0
@@ -4254,6 +4328,9 @@ func rewriteValueARM_OpARMCMPshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMCMPshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -8101,6 +8178,7 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (MVNshiftLLreg x (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (MVNshiftLL x [c])
for {
x := v_0
@@ -8108,6 +8186,9 @@ func rewriteValueARM_OpARMMVNshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMMVNshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
@@ -8135,6 +8216,7 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (MVNshiftRAreg x (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (MVNshiftRA x [c])
for {
x := v_0
@@ -8142,6 +8224,9 @@ func rewriteValueARM_OpARMMVNshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMMVNshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
@@ -8169,6 +8254,7 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (MVNshiftRLreg x (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (MVNshiftRL x [c])
for {
x := v_0
@@ -8176,6 +8262,9 @@ func rewriteValueARM_OpARMMVNshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMMVNshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
@@ -8556,18 +8645,16 @@ func rewriteValueARM_OpARMORshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftLL x y:(SLLconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftLL y:(SLLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSLLconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -8597,6 +8684,7 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool {
return true
}
// match: (ORshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ORshiftLL x y [c])
for {
x := v_0
@@ -8605,6 +8693,9 @@ func rewriteValueARM_OpARMORshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMORshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -8647,18 +8738,16 @@ func rewriteValueARM_OpARMORshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftRA x y:(SRAconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftRA y:(SRAconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSRAconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -8688,6 +8777,7 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool {
return true
}
// match: (ORshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ORshiftRA x y [c])
for {
x := v_0
@@ -8696,6 +8786,9 @@ func rewriteValueARM_OpARMORshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMORshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -8754,18 +8847,16 @@ func rewriteValueARM_OpARMORshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftRL x y:(SRLconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftRL y:(SRLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ y := v_0
+ if y.Op != OpARMSRLconst || auxIntToInt32(y.AuxInt) != c {
break
}
- c := auxIntToInt32(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -8795,6 +8886,7 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool {
return true
}
// match: (ORshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (ORshiftRL x y [c])
for {
x := v_0
@@ -8803,6 +8895,9 @@ func rewriteValueARM_OpARMORshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMORshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9090,6 +9185,7 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool {
return true
}
// match: (RSBSshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBSshiftLL x y [c])
for {
x := v_0
@@ -9098,6 +9194,9 @@ func rewriteValueARM_OpARMRSBSshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBSshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9164,6 +9263,7 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool {
return true
}
// match: (RSBSshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBSshiftRA x y [c])
for {
x := v_0
@@ -9172,6 +9272,9 @@ func rewriteValueARM_OpARMRSBSshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBSshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9238,6 +9341,7 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool {
return true
}
// match: (RSBSshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBSshiftRL x y [c])
for {
x := v_0
@@ -9246,6 +9350,9 @@ func rewriteValueARM_OpARMRSBSshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBSshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9346,17 +9453,15 @@ func rewriteValueARM_OpARMRSBshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (RSBshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (RSBshiftLL (SLLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -9387,6 +9492,7 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool {
return true
}
// match: (RSBshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBshiftLL x y [c])
for {
x := v_0
@@ -9395,6 +9501,9 @@ func rewriteValueARM_OpARMRSBshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9437,17 +9546,15 @@ func rewriteValueARM_OpARMRSBshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (RSBshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (RSBshiftRA (SRAconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -9478,6 +9585,7 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool {
return true
}
// match: (RSBshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBshiftRA x y [c])
for {
x := v_0
@@ -9486,6 +9594,9 @@ func rewriteValueARM_OpARMRSBshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9528,17 +9639,15 @@ func rewriteValueARM_OpARMRSBshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (RSBshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (RSBshiftRL (SRLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -9569,6 +9678,7 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool {
return true
}
// match: (RSBshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (RSBshiftRL x y [c])
for {
x := v_0
@@ -9577,6 +9687,9 @@ func rewriteValueARM_OpARMRSBshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSBshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -9683,6 +9796,7 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool {
return true
}
// match: (RSCshiftLLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (RSCshiftLL x y [c] flags)
for {
x := v_0
@@ -9692,6 +9806,9 @@ func rewriteValueARM_OpARMRSCshiftLLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSCshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -9763,6 +9880,7 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool {
return true
}
// match: (RSCshiftRAreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (RSCshiftRA x y [c] flags)
for {
x := v_0
@@ -9772,6 +9890,9 @@ func rewriteValueARM_OpARMRSCshiftRAreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSCshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -9843,6 +9964,7 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool {
return true
}
// match: (RSCshiftRLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (RSCshiftRL x y [c] flags)
for {
x := v_0
@@ -9852,6 +9974,9 @@ func rewriteValueARM_OpARMRSCshiftRLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMRSCshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -10166,6 +10291,7 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool {
return true
}
// match: (SBCshiftLLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (SBCshiftLL x y [c] flags)
for {
x := v_0
@@ -10175,6 +10301,9 @@ func rewriteValueARM_OpARMSBCshiftLLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSBCshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -10246,6 +10375,7 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool {
return true
}
// match: (SBCshiftRAreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (SBCshiftRA x y [c] flags)
for {
x := v_0
@@ -10255,6 +10385,9 @@ func rewriteValueARM_OpARMSBCshiftRAreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSBCshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -10326,6 +10459,7 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool {
return true
}
// match: (SBCshiftRLreg x y (MOVWconst [c]) flags)
+ // cond: 0 <= c && c < 32
// result: (SBCshiftRL x y [c] flags)
for {
x := v_0
@@ -10335,6 +10469,9 @@ func rewriteValueARM_OpARMSBCshiftRLreg(v *Value) bool {
}
c := auxIntToInt32(v_2.AuxInt)
flags := v_3
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSBCshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg3(x, y, flags)
@@ -10346,15 +10483,19 @@ func rewriteValueARM_OpARMSLL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (SLL x (MOVWconst [c]))
- // result: (SLLconst x [c&31])
+ // cond: 0 <= c && c < 32
+ // result: (SLLconst x [c])
for {
x := v_0
if v_1.Op != OpARMMOVWconst {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSLLconst)
- v.AuxInt = int32ToAuxInt(c & 31)
+ v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
return true
}
@@ -10380,15 +10521,19 @@ func rewriteValueARM_OpARMSRA(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (SRA x (MOVWconst [c]))
- // result: (SRAconst x [c&31])
+ // cond: 0 <= c && c < 32
+ // result: (SRAconst x [c])
for {
x := v_0
if v_1.Op != OpARMMOVWconst {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSRAconst)
- v.AuxInt = int32ToAuxInt(c & 31)
+ v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
return true
}
@@ -10472,15 +10617,19 @@ func rewriteValueARM_OpARMSRL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (SRL x (MOVWconst [c]))
- // result: (SRLconst x [c&31])
+ // cond: 0 <= c && c < 32
+ // result: (SRLconst x [c])
for {
x := v_0
if v_1.Op != OpARMMOVWconst {
break
}
c := auxIntToInt32(v_1.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSRLconst)
- v.AuxInt = int32ToAuxInt(c & 31)
+ v.AuxInt = int32ToAuxInt(c)
v.AddArg(x)
return true
}
@@ -10520,6 +10669,24 @@ func rewriteValueARM_OpARMSRLconst(v *Value) bool {
}
return false
}
+func rewriteValueARM_OpARMSRR(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (SRR x (MOVWconst [c]))
+ // result: (SRRconst x [c&31])
+ for {
+ x := v_0
+ if v_1.Op != OpARMMOVWconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpARMSRRconst)
+ v.AuxInt = int32ToAuxInt(c & 31)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
func rewriteValueARM_OpARMSUB(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -11058,6 +11225,7 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool {
return true
}
// match: (SUBSshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBSshiftLL x y [c])
for {
x := v_0
@@ -11066,6 +11234,9 @@ func rewriteValueARM_OpARMSUBSshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBSshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11132,6 +11303,7 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool {
return true
}
// match: (SUBSshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBSshiftRA x y [c])
for {
x := v_0
@@ -11140,6 +11312,9 @@ func rewriteValueARM_OpARMSUBSshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBSshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11206,6 +11381,7 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool {
return true
}
// match: (SUBSshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBSshiftRL x y [c])
for {
x := v_0
@@ -11214,6 +11390,9 @@ func rewriteValueARM_OpARMSUBSshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBSshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11368,17 +11547,15 @@ func rewriteValueARM_OpARMSUBshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftLL (SLLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -11409,6 +11586,7 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool {
return true
}
// match: (SUBshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBshiftLL x y [c])
for {
x := v_0
@@ -11417,6 +11595,9 @@ func rewriteValueARM_OpARMSUBshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11459,17 +11640,15 @@ func rewriteValueARM_OpARMSUBshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftRA (SRAconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -11500,6 +11679,7 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool {
return true
}
// match: (SUBshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBshiftRA x y [c])
for {
x := v_0
@@ -11508,6 +11688,9 @@ func rewriteValueARM_OpARMSUBshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11550,17 +11733,15 @@ func rewriteValueARM_OpARMSUBshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftRL (SRLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -11591,6 +11772,7 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool {
return true
}
// match: (SUBshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (SUBshiftRL x y [c])
for {
x := v_0
@@ -11599,6 +11781,9 @@ func rewriteValueARM_OpARMSUBshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMSUBshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11801,6 +11986,7 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool {
return true
}
// match: (TEQshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TEQshiftLL x y [c])
for {
x := v_0
@@ -11809,6 +11995,9 @@ func rewriteValueARM_OpARMTEQshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTEQshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11875,6 +12064,7 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool {
return true
}
// match: (TEQshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TEQshiftRA x y [c])
for {
x := v_0
@@ -11883,6 +12073,9 @@ func rewriteValueARM_OpARMTEQshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTEQshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -11949,6 +12142,7 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool {
return true
}
// match: (TEQshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TEQshiftRL x y [c])
for {
x := v_0
@@ -11957,6 +12151,9 @@ func rewriteValueARM_OpARMTEQshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTEQshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12159,6 +12356,7 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool {
return true
}
// match: (TSTshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TSTshiftLL x y [c])
for {
x := v_0
@@ -12167,6 +12365,9 @@ func rewriteValueARM_OpARMTSTshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTSTshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12233,6 +12434,7 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool {
return true
}
// match: (TSTshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TSTshiftRA x y [c])
for {
x := v_0
@@ -12241,6 +12443,9 @@ func rewriteValueARM_OpARMTSTshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTSTshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12307,6 +12512,7 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool {
return true
}
// match: (TSTshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (TSTshiftRL x y [c])
for {
x := v_0
@@ -12315,6 +12521,9 @@ func rewriteValueARM_OpARMTSTshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMTSTshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12595,17 +12804,15 @@ func rewriteValueARM_OpARMXORshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftLL (SLLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSLLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSLLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -12636,6 +12843,7 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool {
return true
}
// match: (XORshiftLLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (XORshiftLL x y [c])
for {
x := v_0
@@ -12644,6 +12852,9 @@ func rewriteValueARM_OpARMXORshiftLLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMXORshiftLL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12686,17 +12897,15 @@ func rewriteValueARM_OpARMXORshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftRA (SRAconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRAconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRAconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -12727,6 +12936,7 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool {
return true
}
// match: (XORshiftRAreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (XORshiftRA x y [c])
for {
x := v_0
@@ -12735,6 +12945,9 @@ func rewriteValueARM_OpARMXORshiftRAreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMXORshiftRA)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -12793,17 +13006,15 @@ func rewriteValueARM_OpARMXORshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftRL (SRLconst x [c]) x [c])
// result: (MOVWconst [0])
for {
- d := auxIntToInt32(v.AuxInt)
- x := v_0
- if v_1.Op != OpARMSRLconst {
+ c := auxIntToInt32(v.AuxInt)
+ if v_0.Op != OpARMSRLconst || auxIntToInt32(v_0.AuxInt) != c {
break
}
- c := auxIntToInt32(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARMMOVWconst)
@@ -12834,6 +13045,7 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool {
return true
}
// match: (XORshiftRLreg x y (MOVWconst [c]))
+ // cond: 0 <= c && c < 32
// result: (XORshiftRL x y [c])
for {
x := v_0
@@ -12842,6 +13054,9 @@ func rewriteValueARM_OpARMXORshiftRLreg(v *Value) bool {
break
}
c := auxIntToInt32(v_2.AuxInt)
+ if !(0 <= c && c < 32) {
+ break
+ }
v.reset(OpARMXORshiftRL)
v.AuxInt = int32ToAuxInt(c)
v.AddArg2(x, y)
@@ -14901,19 +15116,6 @@ func rewriteValueARM_OpRotateLeft32(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
- // match: (RotateLeft32 x (MOVWconst [c]))
- // result: (SRRconst [-c&31] x)
- for {
- x := v_0
- if v_1.Op != OpARMMOVWconst {
- break
- }
- c := auxIntToInt32(v_1.AuxInt)
- v.reset(OpARMSRRconst)
- v.AuxInt = int32ToAuxInt(-c & 31)
- v.AddArg(x)
- return true
- }
// match: (RotateLeft32 x y)
// result: (SRR x (RSBconst [0] y))
for {
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index 3cdc4d36cb5a400fbb419aa5b896074bc5243d61..ad34855c30f6be4e70f6c4af52e5104c1f54abf8 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -29,6 +29,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64ANDshiftRA(v)
case OpARM64ANDshiftRL:
return rewriteValueARM64_OpARM64ANDshiftRL(v)
+ case OpARM64ANDshiftRO:
+ return rewriteValueARM64_OpARM64ANDshiftRO(v)
case OpARM64BIC:
return rewriteValueARM64_OpARM64BIC(v)
case OpARM64BICshiftLL:
@@ -37,6 +39,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64BICshiftRA(v)
case OpARM64BICshiftRL:
return rewriteValueARM64_OpARM64BICshiftRL(v)
+ case OpARM64BICshiftRO:
+ return rewriteValueARM64_OpARM64BICshiftRO(v)
case OpARM64CMN:
return rewriteValueARM64_OpARM64CMN(v)
case OpARM64CMNW:
@@ -89,6 +93,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64EONshiftRA(v)
case OpARM64EONshiftRL:
return rewriteValueARM64_OpARM64EONshiftRL(v)
+ case OpARM64EONshiftRO:
+ return rewriteValueARM64_OpARM64EONshiftRO(v)
case OpARM64Equal:
return rewriteValueARM64_OpARM64Equal(v)
case OpARM64FADDD:
@@ -295,6 +301,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64MVNshiftRA(v)
case OpARM64MVNshiftRL:
return rewriteValueARM64_OpARM64MVNshiftRL(v)
+ case OpARM64MVNshiftRO:
+ return rewriteValueARM64_OpARM64MVNshiftRO(v)
case OpARM64NEG:
return rewriteValueARM64_OpARM64NEG(v)
case OpARM64NEGshiftLL:
@@ -315,6 +323,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64ORNshiftRA(v)
case OpARM64ORNshiftRL:
return rewriteValueARM64_OpARM64ORNshiftRL(v)
+ case OpARM64ORNshiftRO:
+ return rewriteValueARM64_OpARM64ORNshiftRO(v)
case OpARM64ORconst:
return rewriteValueARM64_OpARM64ORconst(v)
case OpARM64ORshiftLL:
@@ -323,6 +333,16 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64ORshiftRA(v)
case OpARM64ORshiftRL:
return rewriteValueARM64_OpARM64ORshiftRL(v)
+ case OpARM64ORshiftRO:
+ return rewriteValueARM64_OpARM64ORshiftRO(v)
+ case OpARM64REV:
+ return rewriteValueARM64_OpARM64REV(v)
+ case OpARM64REVW:
+ return rewriteValueARM64_OpARM64REVW(v)
+ case OpARM64ROR:
+ return rewriteValueARM64_OpARM64ROR(v)
+ case OpARM64RORW:
+ return rewriteValueARM64_OpARM64RORW(v)
case OpARM64RORWconst:
return rewriteValueARM64_OpARM64RORWconst(v)
case OpARM64RORconst:
@@ -367,6 +387,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64TSTshiftRA(v)
case OpARM64TSTshiftRL:
return rewriteValueARM64_OpARM64TSTshiftRL(v)
+ case OpARM64TSTshiftRO:
+ return rewriteValueARM64_OpARM64TSTshiftRO(v)
case OpARM64UBFIZ:
return rewriteValueARM64_OpARM64UBFIZ(v)
case OpARM64UBFX:
@@ -389,6 +411,8 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpARM64XORshiftRA(v)
case OpARM64XORshiftRL:
return rewriteValueARM64_OpARM64XORshiftRL(v)
+ case OpARM64XORshiftRO:
+ return rewriteValueARM64_OpARM64XORshiftRO(v)
case OpAbs:
v.Op = OpARM64FABSD
return true
@@ -896,6 +920,12 @@ func rewriteValueARM64(v *Value) bool {
return rewriteValueARM64_OpPopCount32(v)
case OpPopCount64:
return rewriteValueARM64_OpPopCount64(v)
+ case OpPrefetchCache:
+ return rewriteValueARM64_OpPrefetchCache(v)
+ case OpPrefetchCacheStreamed:
+ return rewriteValueARM64_OpPrefetchCacheStreamed(v)
+ case OpPubBarrier:
+ return rewriteValueARM64_OpPubBarrier(v)
case OpRotateLeft16:
return rewriteValueARM64_OpRotateLeft16(v)
case OpRotateLeft32:
@@ -1038,6 +1068,9 @@ func rewriteValueARM64(v *Value) bool {
case OpSubPtr:
v.Op = OpARM64SUB
return true
+ case OpTailCall:
+ v.Op = OpARM64CALLtail
+ return true
case OpTrunc:
v.Op = OpARM64FRINTZD
return true
@@ -2103,6 +2136,28 @@ func rewriteValueARM64_OpARM64AND(v *Value) bool {
}
break
}
+ // match: (AND x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (ANDshiftRO x0 y [c])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ continue
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ continue
+ }
+ v.reset(OpARM64ANDshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueARM64_OpARM64ANDconst(v *Value) bool {
@@ -2265,18 +2320,16 @@ func rewriteValueARM64_OpARM64ANDshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftLL x y:(SLLconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftLL y:(SLLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2319,18 +2372,16 @@ func rewriteValueARM64_OpARM64ANDshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftRA x y:(SRAconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftRA y:(SRAconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2373,18 +2424,68 @@ func rewriteValueARM64_OpARM64ANDshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ANDshiftRL x y:(SRLconst x [c]) [d])
- // cond: c==d
+ // match: (ANDshiftRL y:(SRLconst x [c]) x [c])
// result: y
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c {
+ break
+ }
+ x := y.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.copyOf(y)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64ANDshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (ANDshiftRO (MOVDconst [c]) x [d])
+ // result: (ANDconst [c] (RORconst x [d]))
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_0.AuxInt)
+ x := v_1
+ v.reset(OpARM64ANDconst)
+ v.AuxInt = int64ToAuxInt(c)
+ v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+ v0.AuxInt = int64ToAuxInt(d)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (ANDshiftRO x (MOVDconst [c]) [d])
+ // result: (ANDconst x [rotateRight64(c, d)])
for {
d := auxIntToInt64(v.AuxInt)
x := v_0
- y := v_1
- if y.Op != OpARM64SRLconst {
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64ANDconst)
+ v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ // match: (ANDshiftRO y:(RORconst x [c]) x [c])
+ // result: y
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -2476,6 +2577,25 @@ func rewriteValueARM64_OpARM64BIC(v *Value) bool {
v.AddArg2(x0, y)
return true
}
+ // match: (BIC x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (BICshiftRO x0 y [c])
+ for {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ break
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ break
+ }
+ v.reset(OpARM64BICshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool {
@@ -2495,17 +2615,15 @@ func rewriteValueARM64_OpARM64BICshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftLL (SLLconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -2531,17 +2649,15 @@ func rewriteValueARM64_OpARM64BICshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftRA (SRAconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -2567,17 +2683,49 @@ func rewriteValueARM64_OpARM64BICshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (BICshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (BICshiftRL (SRLconst x [c]) x [c])
// result: (MOVDconst [0])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpARM64MOVDconst)
+ v.AuxInt = int64ToAuxInt(0)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64BICshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (BICshiftRO x (MOVDconst [c]) [d])
+ // result: (ANDconst x [^rotateRight64(c, d)])
for {
d := auxIntToInt64(v.AuxInt)
x := v_0
- if v_1.Op != OpARM64SRLconst {
+ if v_1.Op != OpARM64MOVDconst {
break
}
c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ v.reset(OpARM64ANDconst)
+ v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ // match: (BICshiftRO (RORconst x [c]) x [c])
+ // result: (MOVDconst [0])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -3926,6 +4074,25 @@ func rewriteValueARM64_OpARM64EON(v *Value) bool {
v.AddArg2(x0, y)
return true
}
+ // match: (EON x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (EONshiftRO x0 y [c])
+ for {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ break
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ break
+ }
+ v.reset(OpARM64EONshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool {
@@ -3945,17 +4112,15 @@ func rewriteValueARM64_OpARM64EONshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (EONshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (EONshiftLL (SLLconst x [c]) x [c])
// result: (MOVDconst [-1])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -3981,17 +4146,15 @@ func rewriteValueARM64_OpARM64EONshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (EONshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (EONshiftRA (SRAconst x [c]) x [c])
// result: (MOVDconst [-1])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -4017,17 +4180,49 @@ func rewriteValueARM64_OpARM64EONshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (EONshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (EONshiftRL (SRLconst x [c]) x [c])
// result: (MOVDconst [-1])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpARM64MOVDconst)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64EONshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (EONshiftRO x (MOVDconst [c]) [d])
+ // result: (XORconst x [^rotateRight64(c, d)])
for {
d := auxIntToInt64(v.AuxInt)
x := v_0
- if v_1.Op != OpARM64SRLconst {
+ if v_1.Op != OpARM64MOVDconst {
break
}
c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ v.reset(OpARM64XORconst)
+ v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ // match: (EONshiftRO (RORconst x [c]) x [c])
+ // result: (MOVDconst [-1])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -7153,37 +7348,54 @@ func rewriteValueARM64_OpARM64MOVBUreg(v *Value) bool {
v.AuxInt = int64ToAuxInt(0)
return true
}
- // match: (MOVBUreg (SLLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<8-1, sc)
- // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
+ // match: (MOVBUreg (SLLconst [lc] x))
+ // cond: lc < 8
+ // result: (UBFIZ [armBFAuxInt(lc, 8-lc)] x)
for {
if v_0.Op != OpARM64SLLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ lc := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<8-1, sc)) {
+ if !(lc < 8) {
break
}
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 8-lc))
v.AddArg(x)
return true
}
- // match: (MOVBUreg (SRLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<8-1, 0)
- // result: (UBFX [armBFAuxInt(sc, 8)] x)
+ // match: (MOVBUreg (SRLconst [rc] x))
+ // cond: rc < 8
+ // result: (UBFX [armBFAuxInt(rc, 8)] x)
for {
if v_0.Op != OpARM64SRLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ rc := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(rc < 8) {
+ break
+ }
+ v.reset(OpARM64UBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8))
+ v.AddArg(x)
+ return true
+ }
+ // match: (MOVBUreg (UBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 8
+ // result: (UBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64UBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<8-1, 0)) {
+ if !(bfc.getARM64BFwidth() <= 8) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8))
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
v.AddArg(x)
return true
}
@@ -7401,6 +7613,23 @@ func rewriteValueARM64_OpARM64MOVBreg(v *Value) bool {
v.AddArg(x)
return true
}
+ // match: (MOVBreg (SBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 8
+ // result: (SBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64SBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(bfc.getARM64BFwidth() <= 8) {
+ break
+ }
+ v.reset(OpARM64SBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64MOVBstore(v *Value) bool {
@@ -10665,37 +10894,54 @@ func rewriteValueARM64_OpARM64MOVHUreg(v *Value) bool {
v.AuxInt = int64ToAuxInt(0)
return true
}
- // match: (MOVHUreg (SLLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<16-1, sc)
- // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
+ // match: (MOVHUreg (SLLconst [lc] x))
+ // cond: lc < 16
+ // result: (UBFIZ [armBFAuxInt(lc, 16-lc)] x)
for {
if v_0.Op != OpARM64SLLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ lc := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<16-1, sc)) {
+ if !(lc < 16) {
break
}
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 16-lc))
v.AddArg(x)
return true
}
- // match: (MOVHUreg (SRLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<16-1, 0)
- // result: (UBFX [armBFAuxInt(sc, 16)] x)
+ // match: (MOVHUreg (SRLconst [rc] x))
+ // cond: rc < 16
+ // result: (UBFX [armBFAuxInt(rc, 16)] x)
for {
if v_0.Op != OpARM64SRLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ rc := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(rc < 16) {
+ break
+ }
+ v.reset(OpARM64UBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16))
+ v.AddArg(x)
+ return true
+ }
+ // match: (MOVHUreg (UBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 16
+ // result: (UBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64UBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<16-1, 0)) {
+ if !(bfc.getARM64BFwidth() <= 16) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16))
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
v.AddArg(x)
return true
}
@@ -11096,17 +11342,34 @@ func rewriteValueARM64_OpARM64MOVHreg(v *Value) bool {
v.AddArg(x)
return true
}
- return false
-}
-func rewriteValueARM64_OpARM64MOVHstore(v *Value) bool {
- v_2 := v.Args[2]
- v_1 := v.Args[1]
- v_0 := v.Args[0]
- b := v.Block
- config := b.Func.Config
- // match: (MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
- // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)
- // result: (MOVHstore [off1+int32(off2)] {sym} ptr val mem)
+ // match: (MOVHreg (SBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 16
+ // result: (SBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64SBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(bfc.getARM64BFwidth() <= 16) {
+ break
+ }
+ v.reset(OpARM64SBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64MOVHstore(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ config := b.Func.Config
+ // match: (MOVHstore [off1] {sym} (ADDconst [off2] ptr) val mem)
+ // cond: is32Bit(int64(off1)+off2) && (ptr.Op != OpSB || !config.ctxt.Flag_shared)
+ // result: (MOVHstore [off1+int32(off2)] {sym} ptr val mem)
for {
off1 := auxIntToInt32(v.AuxInt)
sym := auxToSym(v.Aux)
@@ -12777,37 +13040,54 @@ func rewriteValueARM64_OpARM64MOVWUreg(v *Value) bool {
v.AuxInt = int64ToAuxInt(0)
return true
}
- // match: (MOVWUreg (SLLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<32-1, sc)
- // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
+ // match: (MOVWUreg (SLLconst [lc] x))
+ // cond: lc < 32
+ // result: (UBFIZ [armBFAuxInt(lc, 32-lc)] x)
for {
if v_0.Op != OpARM64SLLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ lc := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<32-1, sc)) {
+ if !(lc < 32) {
break
}
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, 32-lc))
v.AddArg(x)
return true
}
- // match: (MOVWUreg (SRLconst [sc] x))
- // cond: isARM64BFMask(sc, 1<<32-1, 0)
- // result: (UBFX [armBFAuxInt(sc, 32)] x)
+ // match: (MOVWUreg (SRLconst [rc] x))
+ // cond: rc < 32
+ // result: (UBFX [armBFAuxInt(rc, 32)] x)
for {
if v_0.Op != OpARM64SRLconst {
break
}
- sc := auxIntToInt64(v_0.AuxInt)
+ rc := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(rc < 32) {
+ break
+ }
+ v.reset(OpARM64UBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32))
+ v.AddArg(x)
+ return true
+ }
+ // match: (MOVWUreg (UBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 32
+ // result: (UBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64UBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<32-1, 0)) {
+ if !(bfc.getARM64BFwidth() <= 32) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32))
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
v.AddArg(x)
return true
}
@@ -13266,6 +13546,23 @@ func rewriteValueARM64_OpARM64MOVWreg(v *Value) bool {
v.AddArg(x)
return true
}
+ // match: (MOVWreg (SBFX [bfc] x))
+ // cond: bfc.getARM64BFwidth() <= 32
+ // result: (SBFX [bfc] x)
+ for {
+ if v_0.Op != OpARM64SBFX {
+ break
+ }
+ bfc := auxIntToArm64BitField(v_0.AuxInt)
+ x := v_0.Args[0]
+ if !(bfc.getARM64BFwidth() <= 32) {
+ break
+ }
+ v.reset(OpARM64SBFX)
+ v.AuxInt = arm64BitFieldToAuxInt(bfc)
+ v.AddArg(x)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64MOVWstore(v *Value) bool {
@@ -15502,6 +15799,24 @@ func rewriteValueARM64_OpARM64MVN(v *Value) bool {
v.AddArg(y)
return true
}
+ // match: (MVN x:(RORconst [c] y))
+ // cond: clobberIfDead(x)
+ // result: (MVNshiftRO [c] y)
+ for {
+ x := v_0
+ if x.Op != OpARM64RORconst {
+ break
+ }
+ c := auxIntToInt64(x.AuxInt)
+ y := x.Args[0]
+ if !(clobberIfDead(x)) {
+ break
+ }
+ v.reset(OpARM64MVNshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg(y)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64MVNshiftLL(v *Value) bool {
@@ -15552,6 +15867,22 @@ func rewriteValueARM64_OpARM64MVNshiftRL(v *Value) bool {
}
return false
}
+func rewriteValueARM64_OpARM64MVNshiftRO(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (MVNshiftRO (MOVDconst [c]) [d])
+ // result: (MOVDconst [^rotateRight64(c, d)])
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpARM64MOVDconst)
+ v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+ return true
+ }
+ return false
+}
func rewriteValueARM64_OpARM64NEG(v *Value) bool {
v_0 := v.Args[0]
// match: (NEG (MUL x y))
@@ -15578,6 +15909,16 @@ func rewriteValueARM64_OpARM64NEG(v *Value) bool {
v.AddArg2(x, y)
return true
}
+ // match: (NEG (NEG x))
+ // result: x
+ for {
+ if v_0.Op != OpARM64NEG {
+ break
+ }
+ x := v_0.Args[0]
+ v.copyOf(x)
+ return true
+ }
// match: (NEG (MOVDconst [c]))
// result: (MOVDconst [-c])
for {
@@ -15831,6 +16172,28 @@ func rewriteValueARM64_OpARM64OR(v *Value) bool {
}
break
}
+ // match: (OR x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (ORshiftRO x0 y [c])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ continue
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ continue
+ }
+ v.reset(OpARM64ORshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
+ break
+ }
// match: (OR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y)))))
// cond: cc == OpARM64LessThanU
// result: (ROR x (NEG y))
@@ -17800,6 +18163,25 @@ func rewriteValueARM64_OpARM64ORN(v *Value) bool {
v.AddArg2(x0, y)
return true
}
+ // match: (ORN x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (ORNshiftRO x0 y [c])
+ for {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ break
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ break
+ }
+ v.reset(OpARM64ORNshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
return false
}
func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool {
@@ -17819,17 +18201,15 @@ func rewriteValueARM64_OpARM64ORNshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORNshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (ORNshiftLL (SLLconst x [c]) x [c])
// result: (MOVDconst [-1])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -17855,17 +18235,15 @@ func rewriteValueARM64_OpARM64ORNshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORNshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (ORNshiftRA (SRAconst x [c]) x [c])
// result: (MOVDconst [-1])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -17891,17 +18269,49 @@ func rewriteValueARM64_OpARM64ORNshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORNshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (ORNshiftRL (SRLconst x [c]) x [c])
// result: (MOVDconst [-1])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpARM64MOVDconst)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64ORNshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (ORNshiftRO x (MOVDconst [c]) [d])
+ // result: (ORconst x [^rotateRight64(c, d)])
for {
d := auxIntToInt64(v.AuxInt)
x := v_0
- if v_1.Op != OpARM64SRLconst {
+ if v_1.Op != OpARM64MOVDconst {
break
}
c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ v.reset(OpARM64ORconst)
+ v.AuxInt = int64ToAuxInt(^rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ // match: (ORNshiftRO (RORconst x [c]) x [c])
+ // result: (MOVDconst [-1])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -18014,18 +18424,16 @@ func rewriteValueARM64_OpARM64ORshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftLL x y:(SLLconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftLL y:(SLLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SLLconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -19694,18 +20102,16 @@ func rewriteValueARM64_OpARM64ORshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftRA x y:(SRAconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftRA y:(SRAconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SRAconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -19748,18 +20154,16 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (ORshiftRL x y:(SRLconst x [c]) [d])
- // cond: c==d
+ // match: (ORshiftRL y:(SRLconst x [c]) x [c])
// result: y
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- y := v_1
- if y.Op != OpARM64SRLconst {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64SRLconst || auxIntToInt64(y.AuxInt) != c {
break
}
- c := auxIntToInt64(y.AuxInt)
- if x != y.Args[0] || !(c == d) {
+ x := y.Args[0]
+ if x != v_1 {
break
}
v.copyOf(y)
@@ -19847,58 +20251,174 @@ func rewriteValueARM64_OpARM64ORshiftRL(v *Value) bool {
}
return false
}
-func rewriteValueARM64_OpARM64RORWconst(v *Value) bool {
+func rewriteValueARM64_OpARM64ORshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
v_0 := v.Args[0]
- // match: (RORWconst [c] (RORWconst [d] x))
- // result: (RORWconst [(c+d)&31] x)
+ b := v.Block
+ // match: (ORshiftRO (MOVDconst [c]) x [d])
+ // result: (ORconst [c] (RORconst x [d]))
for {
- c := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64RORWconst {
+ d := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVDconst {
break
}
- d := auxIntToInt64(v_0.AuxInt)
- x := v_0.Args[0]
- v.reset(OpARM64RORWconst)
- v.AuxInt = int64ToAuxInt((c + d) & 31)
+ c := auxIntToInt64(v_0.AuxInt)
+ x := v_1
+ v.reset(OpARM64ORconst)
+ v.AuxInt = int64ToAuxInt(c)
+ v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+ v0.AuxInt = int64ToAuxInt(d)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (ORshiftRO x (MOVDconst [c]) [d])
+ // result: (ORconst x [rotateRight64(c, d)])
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ x := v_0
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64ORconst)
+ v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
v.AddArg(x)
return true
}
+ // match: (ORshiftRO y:(RORconst x [c]) x [c])
+ // result: y
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ y := v_0
+ if y.Op != OpARM64RORconst || auxIntToInt64(y.AuxInt) != c {
+ break
+ }
+ x := y.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.copyOf(y)
+ return true
+ }
return false
}
-func rewriteValueARM64_OpARM64RORconst(v *Value) bool {
+func rewriteValueARM64_OpARM64REV(v *Value) bool {
v_0 := v.Args[0]
- // match: (RORconst [c] (RORconst [d] x))
- // result: (RORconst [(c+d)&63] x)
+ // match: (REV (REV p))
+ // result: p
for {
- c := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64RORconst {
+ if v_0.Op != OpARM64REV {
break
}
- d := auxIntToInt64(v_0.AuxInt)
- x := v_0.Args[0]
- v.reset(OpARM64RORconst)
- v.AuxInt = int64ToAuxInt((c + d) & 63)
- v.AddArg(x)
+ p := v_0.Args[0]
+ v.copyOf(p)
return true
}
return false
}
-func rewriteValueARM64_OpARM64SBCSflags(v *Value) bool {
- v_2 := v.Args[2]
- v_1 := v.Args[1]
+func rewriteValueARM64_OpARM64REVW(v *Value) bool {
v_0 := v.Args[0]
- b := v.Block
- typ := &b.Func.Config.Types
- // match: (SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo)))))
- // result: (SBCSflags x y bo)
+ // match: (REVW (REVW p))
+ // result: p
for {
- x := v_0
- y := v_1
- if v_2.Op != OpSelect1 || v_2.Type != types.TypeFlags {
+ if v_0.Op != OpARM64REVW {
break
}
- v_2_0 := v_2.Args[0]
- if v_2_0.Op != OpARM64NEGSflags {
+ p := v_0.Args[0]
+ v.copyOf(p)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64ROR(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (ROR x (MOVDconst [c]))
+ // result: (RORconst x [c&63])
+ for {
+ x := v_0
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64RORconst)
+ v.AuxInt = int64ToAuxInt(c & 63)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64RORW(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (RORW x (MOVDconst [c]))
+ // result: (RORWconst x [c&31])
+ for {
+ x := v_0
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64RORWconst)
+ v.AuxInt = int64ToAuxInt(c & 31)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64RORWconst(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (RORWconst [c] (RORWconst [d] x))
+ // result: (RORWconst [(c+d)&31] x)
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORWconst {
+ break
+ }
+ d := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ v.reset(OpARM64RORWconst)
+ v.AuxInt = int64ToAuxInt((c + d) & 31)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64RORconst(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (RORconst [c] (RORconst [d] x))
+ // result: (RORconst [(c+d)&63] x)
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORconst {
+ break
+ }
+ d := auxIntToInt64(v_0.AuxInt)
+ x := v_0.Args[0]
+ v.reset(OpARM64RORconst)
+ v.AuxInt = int64ToAuxInt((c + d) & 63)
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
+func rewriteValueARM64_OpARM64SBCSflags(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SBCSflags x y (Select1 (NEGSflags (NEG (NGCzerocarry bo)))))
+ // result: (SBCSflags x y bo)
+ for {
+ x := v_0
+ y := v_1
+ if v_2.Op != OpSelect1 || v_2.Type != types.TypeFlags {
+ break
+ }
+ v_2_0 := v_2.Args[0]
+ if v_2_0.Op != OpARM64NEGSflags {
break
}
v_2_0_0 := v_2_0.Args[0]
@@ -19985,72 +20505,99 @@ func rewriteValueARM64_OpARM64SLLconst(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SLLconst [sc] (ANDconst [ac] x))
- // cond: isARM64BFMask(sc, ac, 0)
- // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
+ // match: (SLLconst [lc] (MOVWreg x))
+ // result: (SBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
for {
- sc := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64ANDconst {
+ lc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVWreg {
break
}
- ac := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, ac, 0)) {
+ v.reset(OpARM64SBFIZ)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc)))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SLLconst [lc] (MOVHreg x))
+ // result: (SBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
+ for {
+ lc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVHreg {
break
}
- v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0)))
+ x := v_0.Args[0]
+ v.reset(OpARM64SBFIZ)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc)))
v.AddArg(x)
return true
}
- // match: (SLLconst [sc] (MOVWUreg x))
- // cond: isARM64BFMask(sc, 1<<32-1, 0)
- // result: (UBFIZ [armBFAuxInt(sc, 32)] x)
+ // match: (SLLconst [lc] (MOVBreg x))
+ // result: (SBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
for {
- sc := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64MOVWUreg {
+ lc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVBreg {
break
}
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<32-1, 0)) {
+ v.reset(OpARM64SBFIZ)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc)))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SLLconst [lc] (MOVWUreg x))
+ // result: (UBFIZ [armBFAuxInt(lc, min(32, 64-lc))] x)
+ for {
+ lc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVWUreg {
break
}
+ x := v_0.Args[0]
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 32))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(32, 64-lc)))
v.AddArg(x)
return true
}
- // match: (SLLconst [sc] (MOVHUreg x))
- // cond: isARM64BFMask(sc, 1<<16-1, 0)
- // result: (UBFIZ [armBFAuxInt(sc, 16)] x)
+ // match: (SLLconst [lc] (MOVHUreg x))
+ // result: (UBFIZ [armBFAuxInt(lc, min(16, 64-lc))] x)
for {
- sc := auxIntToInt64(v.AuxInt)
+ lc := auxIntToInt64(v.AuxInt)
if v_0.Op != OpARM64MOVHUreg {
break
}
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<16-1, 0)) {
+ v.reset(OpARM64UBFIZ)
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(16, 64-lc)))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SLLconst [lc] (MOVBUreg x))
+ // result: (UBFIZ [armBFAuxInt(lc, min(8, 64-lc))] x)
+ for {
+ lc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVBUreg {
break
}
+ x := v_0.Args[0]
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 16))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(lc, min(8, 64-lc)))
v.AddArg(x)
return true
}
- // match: (SLLconst [sc] (MOVBUreg x))
- // cond: isARM64BFMask(sc, 1<<8-1, 0)
- // result: (UBFIZ [armBFAuxInt(sc, 8)] x)
+ // match: (SLLconst [sc] (ANDconst [ac] x))
+ // cond: isARM64BFMask(sc, ac, 0)
+ // result: (UBFIZ [armBFAuxInt(sc, arm64BFWidth(ac, 0))] x)
for {
sc := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64MOVBUreg {
+ if v_0.Op != OpARM64ANDconst {
break
}
+ ac := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<8-1, 0)) {
+ if !(isARM64BFMask(sc, ac, 0)) {
break
}
v.reset(OpARM64UBFIZ)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, 8))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, 0)))
v.AddArg(x)
return true
}
@@ -20343,90 +20890,90 @@ func rewriteValueARM64_OpARM64SRLconst(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SRLconst [sc] (ANDconst [ac] x))
- // cond: isARM64BFMask(sc, ac, sc)
- // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
+ // match: (SRLconst [rc] (SLLconst [lc] x))
+ // cond: lc < rc
+ // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
for {
- sc := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64ANDconst {
+ rc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst {
break
}
- ac := auxIntToInt64(v_0.AuxInt)
+ lc := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(isARM64BFMask(sc, ac, sc)) {
+ if !(lc < rc) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc))
v.AddArg(x)
return true
}
- // match: (SRLconst [sc] (MOVWUreg x))
- // cond: isARM64BFMask(sc, 1<<32-1, sc)
- // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc))] x)
+ // match: (SRLconst [rc] (MOVWUreg x))
+ // cond: rc < 32
+ // result: (UBFX [armBFAuxInt(rc, 32-rc)] x)
for {
- sc := auxIntToInt64(v.AuxInt)
+ rc := auxIntToInt64(v.AuxInt)
if v_0.Op != OpARM64MOVWUreg {
break
}
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<32-1, sc)) {
+ if !(rc < 32) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<32-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 32-rc))
v.AddArg(x)
return true
}
- // match: (SRLconst [sc] (MOVHUreg x))
- // cond: isARM64BFMask(sc, 1<<16-1, sc)
- // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc))] x)
+ // match: (SRLconst [rc] (MOVHUreg x))
+ // cond: rc < 16
+ // result: (UBFX [armBFAuxInt(rc, 16-rc)] x)
for {
- sc := auxIntToInt64(v.AuxInt)
+ rc := auxIntToInt64(v.AuxInt)
if v_0.Op != OpARM64MOVHUreg {
break
}
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<16-1, sc)) {
+ if !(rc < 16) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<16-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 16-rc))
v.AddArg(x)
return true
}
- // match: (SRLconst [sc] (MOVBUreg x))
- // cond: isARM64BFMask(sc, 1<<8-1, sc)
- // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc))] x)
+ // match: (SRLconst [rc] (MOVBUreg x))
+ // cond: rc < 8
+ // result: (UBFX [armBFAuxInt(rc, 8-rc)] x)
for {
- sc := auxIntToInt64(v.AuxInt)
+ rc := auxIntToInt64(v.AuxInt)
if v_0.Op != OpARM64MOVBUreg {
break
}
x := v_0.Args[0]
- if !(isARM64BFMask(sc, 1<<8-1, sc)) {
+ if !(rc < 8) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(1<<8-1, sc)))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc, 8-rc))
v.AddArg(x)
return true
}
- // match: (SRLconst [rc] (SLLconst [lc] x))
- // cond: lc < rc
- // result: (UBFX [armBFAuxInt(rc-lc, 64-rc)] x)
+ // match: (SRLconst [sc] (ANDconst [ac] x))
+ // cond: isARM64BFMask(sc, ac, sc)
+ // result: (UBFX [armBFAuxInt(sc, arm64BFWidth(ac, sc))] x)
for {
- rc := auxIntToInt64(v.AuxInt)
- if v_0.Op != OpARM64SLLconst {
+ sc := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64ANDconst {
break
}
- lc := auxIntToInt64(v_0.AuxInt)
+ ac := auxIntToInt64(v_0.AuxInt)
x := v_0.Args[0]
- if !(lc < rc) {
+ if !(isARM64BFMask(sc, ac, sc)) {
break
}
v.reset(OpARM64UBFX)
- v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(rc-lc, 64-rc))
+ v.AuxInt = arm64BitFieldToAuxInt(armBFAuxInt(sc, arm64BFWidth(ac, sc)))
v.AddArg(x)
return true
}
@@ -20836,17 +21383,15 @@ func rewriteValueARM64_OpARM64SUBshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftLL (SLLconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -20872,17 +21417,15 @@ func rewriteValueARM64_OpARM64SUBshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftRA (SRAconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -20908,17 +21451,15 @@ func rewriteValueARM64_OpARM64SUBshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (SUBshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (SUBshiftRL (SRLconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -21012,6 +21553,28 @@ func rewriteValueARM64_OpARM64TST(v *Value) bool {
}
break
}
+ // match: (TST x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (TSTshiftRO x0 y [c])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ continue
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ continue
+ }
+ v.reset(OpARM64TSTshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
+ break
+ }
return false
}
func rewriteValueARM64_OpARM64TSTW(v *Value) bool {
@@ -21178,6 +21741,43 @@ func rewriteValueARM64_OpARM64TSTshiftRL(v *Value) bool {
}
return false
}
+func rewriteValueARM64_OpARM64TSTshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (TSTshiftRO (MOVDconst [c]) x [d])
+ // result: (TSTconst [c] (RORconst x [d]))
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_0.AuxInt)
+ x := v_1
+ v.reset(OpARM64TSTconst)
+ v.AuxInt = int64ToAuxInt(c)
+ v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+ v0.AuxInt = int64ToAuxInt(d)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (TSTshiftRO x (MOVDconst [c]) [d])
+ // result: (TSTconst x [rotateRight64(c, d)])
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ x := v_0
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64TSTconst)
+ v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ return false
+}
func rewriteValueARM64_OpARM64UBFIZ(v *Value) bool {
v_0 := v.Args[0]
// match: (UBFIZ [bfc] (SLLconst [sc] x))
@@ -21637,6 +22237,28 @@ func rewriteValueARM64_OpARM64XOR(v *Value) bool {
}
break
}
+ // match: (XOR x0 x1:(RORconst [c] y))
+ // cond: clobberIfDead(x1)
+ // result: (XORshiftRO x0 y [c])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x0 := v_0
+ x1 := v_1
+ if x1.Op != OpARM64RORconst {
+ continue
+ }
+ c := auxIntToInt64(x1.AuxInt)
+ y := x1.Args[0]
+ if !(clobberIfDead(x1)) {
+ continue
+ }
+ v.reset(OpARM64XORshiftRO)
+ v.AuxInt = int64ToAuxInt(c)
+ v.AddArg2(x0, y)
+ return true
+ }
+ break
+ }
// match: (XOR (SLL x (ANDconst [63] y)) (CSEL0 [cc] (SRL x (SUB (MOVDconst [64]) (ANDconst [63] y))) (CMPconst [64] (SUB (MOVDconst [64]) (ANDconst [63] y)))))
// cond: cc == OpARM64LessThanU
// result: (ROR x (NEG y))
@@ -22007,17 +22629,15 @@ func rewriteValueARM64_OpARM64XORshiftLL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftLL x (SLLconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftLL (SLLconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SLLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SLLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -22219,17 +22839,15 @@ func rewriteValueARM64_OpARM64XORshiftRA(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftRA x (SRAconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftRA (SRAconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRAconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRAconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -22273,17 +22891,15 @@ func rewriteValueARM64_OpARM64XORshiftRL(v *Value) bool {
v.AddArg(x)
return true
}
- // match: (XORshiftRL x (SRLconst x [c]) [d])
- // cond: c==d
+ // match: (XORshiftRL (SRLconst x [c]) x [c])
// result: (MOVDconst [0])
for {
- d := auxIntToInt64(v.AuxInt)
- x := v_0
- if v_1.Op != OpARM64SRLconst {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64SRLconst || auxIntToInt64(v_0.AuxInt) != c {
break
}
- c := auxIntToInt64(v_1.AuxInt)
- if x != v_1.Args[0] || !(c == d) {
+ x := v_0.Args[0]
+ if x != v_1 {
break
}
v.reset(OpARM64MOVDconst)
@@ -22326,6 +22942,58 @@ func rewriteValueARM64_OpARM64XORshiftRL(v *Value) bool {
}
return false
}
+func rewriteValueARM64_OpARM64XORshiftRO(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (XORshiftRO (MOVDconst [c]) x [d])
+ // result: (XORconst [c] (RORconst x [d]))
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_0.AuxInt)
+ x := v_1
+ v.reset(OpARM64XORconst)
+ v.AuxInt = int64ToAuxInt(c)
+ v0 := b.NewValue0(v.Pos, OpARM64RORconst, x.Type)
+ v0.AuxInt = int64ToAuxInt(d)
+ v0.AddArg(x)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (XORshiftRO x (MOVDconst [c]) [d])
+ // result: (XORconst x [rotateRight64(c, d)])
+ for {
+ d := auxIntToInt64(v.AuxInt)
+ x := v_0
+ if v_1.Op != OpARM64MOVDconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpARM64XORconst)
+ v.AuxInt = int64ToAuxInt(rotateRight64(c, d))
+ v.AddArg(x)
+ return true
+ }
+ // match: (XORshiftRO (RORconst x [c]) x [c])
+ // result: (MOVDconst [0])
+ for {
+ c := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpARM64RORconst || auxIntToInt64(v_0.AuxInt) != c {
+ break
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpARM64MOVDconst)
+ v.AuxInt = int64ToAuxInt(0)
+ return true
+ }
+ return false
+}
func rewriteValueARM64_OpAddr(v *Value) bool {
v_0 := v.Args[0]
// match: (Addr {sym} base)
@@ -24951,6 +25619,46 @@ func rewriteValueARM64_OpPopCount64(v *Value) bool {
return true
}
}
+func rewriteValueARM64_OpPrefetchCache(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (PrefetchCache addr mem)
+ // result: (PRFM [0] addr mem)
+ for {
+ addr := v_0
+ mem := v_1
+ v.reset(OpARM64PRFM)
+ v.AuxInt = int64ToAuxInt(0)
+ v.AddArg2(addr, mem)
+ return true
+ }
+}
+func rewriteValueARM64_OpPrefetchCacheStreamed(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (PrefetchCacheStreamed addr mem)
+ // result: (PRFM [1] addr mem)
+ for {
+ addr := v_0
+ mem := v_1
+ v.reset(OpARM64PRFM)
+ v.AuxInt = int64ToAuxInt(1)
+ v.AddArg2(addr, mem)
+ return true
+ }
+}
+func rewriteValueARM64_OpPubBarrier(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (PubBarrier mem)
+ // result: (DMB [0xe] mem)
+ for {
+ mem := v_0
+ v.reset(OpARM64DMB)
+ v.AuxInt = int64ToAuxInt(0xe)
+ v.AddArg(mem)
+ return true
+ }
+}
func rewriteValueARM64_OpRotateLeft16(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -25997,7 +26705,7 @@ func rewriteValueARM64_OpSelectN(v *Value) bool {
break
}
call := v_0
- if call.Op != OpARM64CALLstatic {
+ if call.Op != OpARM64CALLstatic || len(call.Args) != 1 {
break
}
sym := auxToCall(call.Aux)
@@ -26031,6 +26739,34 @@ func rewriteValueARM64_OpSelectN(v *Value) bool {
v.AddArg3(dst, src, mem)
return true
}
+ // match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem))
+ // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)
+ // result: (Move [sz] dst src mem)
+ for {
+ if auxIntToInt64(v.AuxInt) != 0 {
+ break
+ }
+ call := v_0
+ if call.Op != OpARM64CALLstatic || len(call.Args) != 4 {
+ break
+ }
+ sym := auxToCall(call.Aux)
+ mem := call.Args[3]
+ dst := call.Args[0]
+ src := call.Args[1]
+ call_2 := call.Args[2]
+ if call_2.Op != OpARM64MOVDconst {
+ break
+ }
+ sz := auxIntToInt64(call_2.AuxInt)
+ if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) {
+ break
+ }
+ v.reset(OpMove)
+ v.AuxInt = int64ToAuxInt(sz)
+ v.AddArg3(dst, src, mem)
+ return true
+ }
return false
}
func rewriteValueARM64_OpSlicemask(v *Value) bool {
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS.go b/src/cmd/compile/internal/ssa/rewriteMIPS.go
index 429369d631eb37bf910c4b6a845fbac06da68463..811ea9d9d326f239a20e3d2c1d06fd62a7f9c914 100644
--- a/src/cmd/compile/internal/ssa/rewriteMIPS.go
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS.go
@@ -544,6 +544,9 @@ func rewriteValueMIPS(v *Value) bool {
case OpSubPtr:
v.Op = OpMIPSSUB
return true
+ case OpTailCall:
+ v.Op = OpMIPSCALLtail
+ return true
case OpTrunc16to8:
v.Op = OpCopy
return true
diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
index 772d7b66efeebdf3bd3048c2d5285be80ea69b03..1fbd556b5cbcd7d32cb9ad6424d5c5d2a14580e7 100644
--- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go
+++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go
@@ -625,6 +625,9 @@ func rewriteValueMIPS64(v *Value) bool {
case OpSubPtr:
v.Op = OpMIPS64SUBV
return true
+ case OpTailCall:
+ v.Op = OpMIPS64CALLtail
+ return true
case OpTrunc16to8:
v.Op = OpCopy
return true
diff --git a/src/cmd/compile/internal/ssa/rewritePPC64.go b/src/cmd/compile/internal/ssa/rewritePPC64.go
index 96dee0bd21baf6d9f753dcbf615fee2797506fee..7592b4f50582405812ba82999ed599e8e3a43cce 100644
--- a/src/cmd/compile/internal/ssa/rewritePPC64.go
+++ b/src/cmd/compile/internal/ssa/rewritePPC64.go
@@ -639,6 +639,10 @@ func rewriteValuePPC64(v *Value) bool {
return true
case OpPopCount8:
return rewriteValuePPC64_OpPopCount8(v)
+ case OpPrefetchCache:
+ return rewriteValuePPC64_OpPrefetchCache(v)
+ case OpPrefetchCacheStreamed:
+ return rewriteValuePPC64_OpPrefetchCacheStreamed(v)
case OpRotateLeft16:
return rewriteValuePPC64_OpRotateLeft16(v)
case OpRotateLeft32:
@@ -720,6 +724,8 @@ func rewriteValuePPC64(v *Value) bool {
return rewriteValuePPC64_OpRsh8x64(v)
case OpRsh8x8:
return rewriteValuePPC64_OpRsh8x8(v)
+ case OpSelectN:
+ return rewriteValuePPC64_OpSelectN(v)
case OpSignExt16to32:
v.Op = OpPPC64MOVHreg
return true
@@ -772,6 +778,9 @@ func rewriteValuePPC64(v *Value) bool {
case OpSubPtr:
v.Op = OpPPC64SUB
return true
+ case OpTailCall:
+ v.Op = OpPPC64CALLtail
+ return true
case OpTrunc:
v.Op = OpPPC64FTRUNC
return true
@@ -1159,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
@@ -1184,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)
@@ -5901,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 {
@@ -7084,6 +7100,20 @@ func rewriteValuePPC64_OpPPC64MOVBZreg(v *Value) bool {
v.copyOf(x)
return true
}
+ // match: (MOVBZreg x:(Select0 (LoweredAtomicLoad8 _ _)))
+ // result: x
+ for {
+ x := v_0
+ if x.Op != OpSelect0 {
+ break
+ }
+ x_0 := x.Args[0]
+ if x_0.Op != OpPPC64LoweredAtomicLoad8 {
+ break
+ }
+ v.copyOf(x)
+ return true
+ }
// match: (MOVBZreg x:(Arg ))
// cond: is8BitInt(t) && !isSigned(t)
// result: x
@@ -10540,6 +10570,20 @@ func rewriteValuePPC64_OpPPC64MOVWZreg(v *Value) bool {
v.copyOf(x)
return true
}
+ // match: (MOVWZreg x:(Select0 (LoweredAtomicLoad32 _ _)))
+ // result: x
+ for {
+ x := v_0
+ if x.Op != OpSelect0 {
+ break
+ }
+ x_0 := x.Args[0]
+ if x_0.Op != OpPPC64LoweredAtomicLoad32 {
+ break
+ }
+ v.copyOf(x)
+ return true
+ }
// match: (MOVWZreg x:(Arg ))
// cond: (is8BitInt(t) || is16BitInt(t) || is32BitInt(t)) && !isSigned(t)
// result: x
@@ -11335,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 {
@@ -13875,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 {
@@ -13899,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 {
@@ -14000,6 +14122,34 @@ func rewriteValuePPC64_OpPopCount8(v *Value) bool {
return true
}
}
+func rewriteValuePPC64_OpPrefetchCache(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (PrefetchCache ptr mem)
+ // result: (DCBT ptr mem [0])
+ for {
+ ptr := v_0
+ mem := v_1
+ v.reset(OpPPC64DCBT)
+ v.AuxInt = int64ToAuxInt(0)
+ v.AddArg2(ptr, mem)
+ return true
+ }
+}
+func rewriteValuePPC64_OpPrefetchCacheStreamed(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (PrefetchCacheStreamed ptr mem)
+ // result: (DCBT ptr mem [8])
+ for {
+ ptr := v_0
+ mem := v_1
+ v.reset(OpPPC64DCBT)
+ v.AuxInt = int64ToAuxInt(8)
+ v.AddArg2(ptr, mem)
+ return true
+ }
+}
func rewriteValuePPC64_OpRotateLeft16(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -16436,6 +16586,82 @@ func rewriteValuePPC64_OpRsh8x8(v *Value) bool {
return true
}
}
+func rewriteValuePPC64_OpSelectN(v *Value) bool {
+ v_0 := v.Args[0]
+ b := v.Block
+ config := b.Func.Config
+ // match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
+ // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)
+ // result: (Move [sz] dst src mem)
+ for {
+ if auxIntToInt64(v.AuxInt) != 0 {
+ break
+ }
+ call := v_0
+ if call.Op != OpPPC64CALLstatic || len(call.Args) != 1 {
+ break
+ }
+ sym := auxToCall(call.Aux)
+ s1 := call.Args[0]
+ if s1.Op != OpPPC64MOVDstore {
+ break
+ }
+ _ = s1.Args[2]
+ s1_1 := s1.Args[1]
+ if s1_1.Op != OpPPC64MOVDconst {
+ break
+ }
+ sz := auxIntToInt64(s1_1.AuxInt)
+ s2 := s1.Args[2]
+ if s2.Op != OpPPC64MOVDstore {
+ break
+ }
+ _ = s2.Args[2]
+ src := s2.Args[1]
+ s3 := s2.Args[2]
+ if s3.Op != OpPPC64MOVDstore {
+ break
+ }
+ mem := s3.Args[2]
+ dst := s3.Args[1]
+ if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)) {
+ break
+ }
+ v.reset(OpMove)
+ v.AuxInt = int64ToAuxInt(sz)
+ v.AddArg3(dst, src, mem)
+ return true
+ }
+ // match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVDconst [sz]) mem))
+ // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)
+ // result: (Move [sz] dst src mem)
+ for {
+ if auxIntToInt64(v.AuxInt) != 0 {
+ break
+ }
+ call := v_0
+ if call.Op != OpPPC64CALLstatic || len(call.Args) != 4 {
+ break
+ }
+ sym := auxToCall(call.Aux)
+ mem := call.Args[3]
+ dst := call.Args[0]
+ src := call.Args[1]
+ call_2 := call.Args[2]
+ if call_2.Op != OpPPC64MOVDconst {
+ break
+ }
+ sz := auxIntToInt64(call_2.AuxInt)
+ if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) {
+ break
+ }
+ v.reset(OpMove)
+ v.AuxInt = int64ToAuxInt(sz)
+ v.AddArg3(dst, src, mem)
+ return true
+ }
+ return false
+}
func rewriteValuePPC64_OpSlicemask(v *Value) bool {
v_0 := v.Args[0]
b := v.Block
@@ -16502,14 +16728,14 @@ func rewriteValuePPC64_OpStore(v *Value) bool {
return true
}
// match: (Store {t} ptr val mem)
- // cond: t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type))
+ // cond: t.Size() == 8 && !is64BitFloat(val.Type)
// result: (MOVDstore ptr val mem)
for {
t := auxToType(v.Aux)
ptr := v_0
val := v_1
mem := v_2
- if !(t.Size() == 8 && (is64BitInt(val.Type) || isPtr(val.Type))) {
+ if !(t.Size() == 8 && !is64BitFloat(val.Type)) {
break
}
v.reset(OpPPC64MOVDstore)
diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
index 431fb1aaf66e0dd288704707d9ed909780533d4b..885bbaf4a1d0bf1cd3732264da50f23ed8deeaa0 100644
--- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go
+++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go
@@ -8,6 +8,9 @@ import "cmd/compile/internal/types"
func rewriteValueRISCV64(v *Value) bool {
switch v.Op {
+ case OpAbs:
+ v.Op = OpRISCV64FABSD
+ return true
case OpAdd16:
v.Op = OpRISCV64ADD
return true
@@ -134,6 +137,9 @@ func rewriteValueRISCV64(v *Value) bool {
case OpConvert:
v.Op = OpRISCV64MOVconvert
return true
+ case OpCopysign:
+ v.Op = OpRISCV64FSGNJD
+ return true
case OpCvt32Fto32:
v.Op = OpRISCV64FCVTWS
return true
@@ -209,6 +215,9 @@ func rewriteValueRISCV64(v *Value) bool {
return rewriteValueRISCV64_OpEqB(v)
case OpEqPtr:
return rewriteValueRISCV64_OpEqPtr(v)
+ case OpFMA:
+ v.Op = OpRISCV64FMADDD
+ return true
case OpGetCallerPC:
v.Op = OpRISCV64LoweredGetCallerPC
return true
@@ -356,6 +365,12 @@ func rewriteValueRISCV64(v *Value) bool {
case OpMul64F:
v.Op = OpRISCV64FMULD
return true
+ case OpMul64uhilo:
+ v.Op = OpRISCV64LoweredMuluhilo
+ return true
+ case OpMul64uover:
+ v.Op = OpRISCV64LoweredMuluover
+ return true
case OpMul8:
return rewriteValueRISCV64_OpMul8(v)
case OpNeg16:
@@ -426,6 +441,16 @@ func rewriteValueRISCV64(v *Value) bool {
return rewriteValueRISCV64_OpRISCV64ADDI(v)
case OpRISCV64AND:
return rewriteValueRISCV64_OpRISCV64AND(v)
+ case OpRISCV64ANDI:
+ return rewriteValueRISCV64_OpRISCV64ANDI(v)
+ case OpRISCV64FMADDD:
+ return rewriteValueRISCV64_OpRISCV64FMADDD(v)
+ case OpRISCV64FMSUBD:
+ return rewriteValueRISCV64_OpRISCV64FMSUBD(v)
+ case OpRISCV64FNMADDD:
+ return rewriteValueRISCV64_OpRISCV64FNMADDD(v)
+ case OpRISCV64FNMSUBD:
+ return rewriteValueRISCV64_OpRISCV64FNMSUBD(v)
case OpRISCV64MOVBUload:
return rewriteValueRISCV64_OpRISCV64MOVBUload(v)
case OpRISCV64MOVBUreg:
@@ -472,14 +497,30 @@ func rewriteValueRISCV64(v *Value) bool {
return rewriteValueRISCV64_OpRISCV64MOVWstore(v)
case OpRISCV64MOVWstorezero:
return rewriteValueRISCV64_OpRISCV64MOVWstorezero(v)
+ case OpRISCV64NEG:
+ return rewriteValueRISCV64_OpRISCV64NEG(v)
+ case OpRISCV64NEGW:
+ return rewriteValueRISCV64_OpRISCV64NEGW(v)
case OpRISCV64OR:
return rewriteValueRISCV64_OpRISCV64OR(v)
+ case OpRISCV64ORI:
+ return rewriteValueRISCV64_OpRISCV64ORI(v)
case OpRISCV64SLL:
return rewriteValueRISCV64_OpRISCV64SLL(v)
+ case OpRISCV64SLLI:
+ return rewriteValueRISCV64_OpRISCV64SLLI(v)
+ case OpRISCV64SLTI:
+ return rewriteValueRISCV64_OpRISCV64SLTI(v)
+ case OpRISCV64SLTIU:
+ return rewriteValueRISCV64_OpRISCV64SLTIU(v)
case OpRISCV64SRA:
return rewriteValueRISCV64_OpRISCV64SRA(v)
+ case OpRISCV64SRAI:
+ return rewriteValueRISCV64_OpRISCV64SRAI(v)
case OpRISCV64SRL:
return rewriteValueRISCV64_OpRISCV64SRL(v)
+ case OpRISCV64SRLI:
+ return rewriteValueRISCV64_OpRISCV64SRLI(v)
case OpRISCV64SUB:
return rewriteValueRISCV64_OpRISCV64SUB(v)
case OpRISCV64SUBW:
@@ -616,6 +657,9 @@ func rewriteValueRISCV64(v *Value) bool {
case OpSubPtr:
v.Op = OpRISCV64SUB
return true
+ case OpTailCall:
+ v.Op = OpRISCV64CALLtail
+ return true
case OpTrunc16to8:
v.Op = OpCopy
return true
@@ -2796,6 +2840,22 @@ func rewriteValueRISCV64_OpRISCV64ADDI(v *Value) bool {
v.copyOf(x)
return true
}
+ // match: (ADDI [x] (MOVDconst [y]))
+ // cond: is32Bit(x + y)
+ // result: (MOVDconst [x + y])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ if !(is32Bit(x + y)) {
+ break
+ }
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(x + y)
+ return true
+ }
return false
}
func rewriteValueRISCV64_OpRISCV64AND(v *Value) bool {
@@ -2823,6 +2883,222 @@ func rewriteValueRISCV64_OpRISCV64AND(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64ANDI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (ANDI [0] x)
+ // result: (MOVDconst [0])
+ for {
+ if auxIntToInt64(v.AuxInt) != 0 {
+ break
+ }
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(0)
+ return true
+ }
+ // match: (ANDI [-1] x)
+ // result: x
+ for {
+ if auxIntToInt64(v.AuxInt) != -1 {
+ break
+ }
+ x := v_0
+ v.copyOf(x)
+ return true
+ }
+ // match: (ANDI [x] (MOVDconst [y]))
+ // result: (MOVDconst [x & y])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(x & y)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64FMADDD(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (FMADDD neg:(FNEGD x) y z)
+ // cond: neg.Uses == 1
+ // result: (FNMADDD x y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ neg := v_0
+ if neg.Op != OpRISCV64FNEGD {
+ continue
+ }
+ x := neg.Args[0]
+ y := v_1
+ z := v_2
+ if !(neg.Uses == 1) {
+ continue
+ }
+ v.reset(OpRISCV64FNMADDD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ break
+ }
+ // match: (FMADDD x y neg:(FNEGD z))
+ // cond: neg.Uses == 1
+ // result: (FMSUBD x y z)
+ for {
+ x := v_0
+ y := v_1
+ neg := v_2
+ if neg.Op != OpRISCV64FNEGD {
+ break
+ }
+ z := neg.Args[0]
+ if !(neg.Uses == 1) {
+ break
+ }
+ v.reset(OpRISCV64FMSUBD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64FMSUBD(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (FMSUBD neg:(FNEGD x) y z)
+ // cond: neg.Uses == 1
+ // result: (FNMSUBD x y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ neg := v_0
+ if neg.Op != OpRISCV64FNEGD {
+ continue
+ }
+ x := neg.Args[0]
+ y := v_1
+ z := v_2
+ if !(neg.Uses == 1) {
+ continue
+ }
+ v.reset(OpRISCV64FNMSUBD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ break
+ }
+ // match: (FMSUBD x y neg:(FNEGD z))
+ // cond: neg.Uses == 1
+ // result: (FMADDD x y z)
+ for {
+ x := v_0
+ y := v_1
+ neg := v_2
+ if neg.Op != OpRISCV64FNEGD {
+ break
+ }
+ z := neg.Args[0]
+ if !(neg.Uses == 1) {
+ break
+ }
+ v.reset(OpRISCV64FMADDD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64FNMADDD(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (FNMADDD neg:(FNEGD x) y z)
+ // cond: neg.Uses == 1
+ // result: (FMADDD x y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ neg := v_0
+ if neg.Op != OpRISCV64FNEGD {
+ continue
+ }
+ x := neg.Args[0]
+ y := v_1
+ z := v_2
+ if !(neg.Uses == 1) {
+ continue
+ }
+ v.reset(OpRISCV64FMADDD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ break
+ }
+ // match: (FNMADDD x y neg:(FNEGD z))
+ // cond: neg.Uses == 1
+ // result: (FNMSUBD x y z)
+ for {
+ x := v_0
+ y := v_1
+ neg := v_2
+ if neg.Op != OpRISCV64FNEGD {
+ break
+ }
+ z := neg.Args[0]
+ if !(neg.Uses == 1) {
+ break
+ }
+ v.reset(OpRISCV64FNMSUBD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64FNMSUBD(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ // match: (FNMSUBD neg:(FNEGD x) y z)
+ // cond: neg.Uses == 1
+ // result: (FMSUBD x y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ neg := v_0
+ if neg.Op != OpRISCV64FNEGD {
+ continue
+ }
+ x := neg.Args[0]
+ y := v_1
+ z := v_2
+ if !(neg.Uses == 1) {
+ continue
+ }
+ v.reset(OpRISCV64FMSUBD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ break
+ }
+ // match: (FNMSUBD x y neg:(FNEGD z))
+ // cond: neg.Uses == 1
+ // result: (FNMADDD x y z)
+ for {
+ x := v_0
+ y := v_1
+ neg := v_2
+ if neg.Op != OpRISCV64FNEGD {
+ break
+ }
+ z := neg.Args[0]
+ if !(neg.Uses == 1) {
+ break
+ }
+ v.reset(OpRISCV64FNMADDD)
+ v.AddArg3(x, y, z)
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -4413,6 +4689,36 @@ func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64NEG(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (NEG (MOVDconst [x]))
+ // result: (MOVDconst [-x])
+ for {
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ x := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(-x)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64NEGW(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (NEGW (MOVDconst [x]))
+ // result: (MOVDconst [int64(int32(-x))])
+ for {
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ x := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(int64(int32(-x)))
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64OR(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -4438,6 +4744,42 @@ func rewriteValueRISCV64_OpRISCV64OR(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64ORI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (ORI [0] x)
+ // result: x
+ for {
+ if auxIntToInt64(v.AuxInt) != 0 {
+ break
+ }
+ x := v_0
+ v.copyOf(x)
+ return true
+ }
+ // match: (ORI [-1] x)
+ // result: (MOVDconst [-1])
+ for {
+ if auxIntToInt64(v.AuxInt) != -1 {
+ break
+ }
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ // match: (ORI [x] (MOVDconst [y]))
+ // result: (MOVDconst [x | y])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(x | y)
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64SLL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -4456,6 +4798,58 @@ func rewriteValueRISCV64_OpRISCV64SLL(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64SLLI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (SLLI [x] (MOVDconst [y]))
+ // cond: is32Bit(y << x)
+ // result: (MOVDconst [y << x])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ if !(is32Bit(y << x)) {
+ break
+ }
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(y << x)
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64SLTI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (SLTI [x] (MOVDconst [y]))
+ // result: (MOVDconst [b2i(int64(y) < int64(x))])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(b2i(int64(y) < int64(x)))
+ return true
+ }
+ return false
+}
+func rewriteValueRISCV64_OpRISCV64SLTIU(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (SLTIU [x] (MOVDconst [y]))
+ // result: (MOVDconst [b2i(uint64(y) < uint64(x))])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(b2i(uint64(y) < uint64(x)))
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -4474,6 +4868,22 @@ func rewriteValueRISCV64_OpRISCV64SRA(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64SRAI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (SRAI [x] (MOVDconst [y]))
+ // result: (MOVDconst [int64(y) >> x])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(int64(y) >> x)
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -4492,6 +4902,22 @@ func rewriteValueRISCV64_OpRISCV64SRL(v *Value) bool {
}
return false
}
+func rewriteValueRISCV64_OpRISCV64SRLI(v *Value) bool {
+ v_0 := v.Args[0]
+ // match: (SRLI [x] (MOVDconst [y]))
+ // result: (MOVDconst [int64(uint64(y) >> x)])
+ for {
+ x := auxIntToInt64(v.AuxInt)
+ if v_0.Op != OpRISCV64MOVDconst {
+ break
+ }
+ y := auxIntToInt64(v_0.AuxInt)
+ v.reset(OpRISCV64MOVDconst)
+ v.AuxInt = int64ToAuxInt(int64(uint64(y) >> x))
+ return true
+ }
+ return false
+}
func rewriteValueRISCV64_OpRISCV64SUB(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -6096,6 +6522,18 @@ func rewriteBlockRISCV64(b *Block) bool {
b.resetWithControl(BlockRISCV64BEQZ, x)
return true
}
+ // match: (BEQZ x:(NEG y) yes no)
+ // cond: x.Uses == 1
+ // result: (BEQZ y yes no)
+ for b.Controls[0].Op == OpRISCV64NEG {
+ x := b.Controls[0]
+ y := x.Args[0]
+ if !(x.Uses == 1) {
+ break
+ }
+ b.resetWithControl(BlockRISCV64BEQZ, y)
+ return true
+ }
// match: (BEQZ (SUB x y) yes no)
// result: (BEQ x y yes no)
for b.Controls[0].Op == OpRISCV64SUB {
@@ -6123,6 +6561,52 @@ func rewriteBlockRISCV64(b *Block) bool {
b.resetWithControl2(BlockRISCV64BGEU, x, y)
return true
}
+ case BlockRISCV64BGE:
+ // match: (BGE (MOVDconst [0]) cond yes no)
+ // result: (BLEZ cond yes no)
+ for b.Controls[0].Op == OpRISCV64MOVDconst {
+ v_0 := b.Controls[0]
+ if auxIntToInt64(v_0.AuxInt) != 0 {
+ break
+ }
+ cond := b.Controls[1]
+ b.resetWithControl(BlockRISCV64BLEZ, cond)
+ return true
+ }
+ // match: (BGE cond (MOVDconst [0]) yes no)
+ // result: (BGEZ cond yes no)
+ for b.Controls[1].Op == OpRISCV64MOVDconst {
+ cond := b.Controls[0]
+ v_1 := b.Controls[1]
+ if auxIntToInt64(v_1.AuxInt) != 0 {
+ break
+ }
+ b.resetWithControl(BlockRISCV64BGEZ, cond)
+ return true
+ }
+ case BlockRISCV64BLT:
+ // match: (BLT (MOVDconst [0]) cond yes no)
+ // result: (BGTZ cond yes no)
+ for b.Controls[0].Op == OpRISCV64MOVDconst {
+ v_0 := b.Controls[0]
+ if auxIntToInt64(v_0.AuxInt) != 0 {
+ break
+ }
+ cond := b.Controls[1]
+ b.resetWithControl(BlockRISCV64BGTZ, cond)
+ return true
+ }
+ // match: (BLT cond (MOVDconst [0]) yes no)
+ // result: (BLTZ cond yes no)
+ for b.Controls[1].Op == OpRISCV64MOVDconst {
+ cond := b.Controls[0]
+ v_1 := b.Controls[1]
+ if auxIntToInt64(v_1.AuxInt) != 0 {
+ break
+ }
+ b.resetWithControl(BlockRISCV64BLTZ, cond)
+ return true
+ }
case BlockRISCV64BNE:
// match: (BNE (MOVDconst [0]) cond yes no)
// result: (BNEZ cond yes no)
@@ -6163,6 +6647,18 @@ func rewriteBlockRISCV64(b *Block) bool {
b.resetWithControl(BlockRISCV64BNEZ, x)
return true
}
+ // match: (BNEZ x:(NEG y) yes no)
+ // cond: x.Uses == 1
+ // result: (BNEZ y yes no)
+ for b.Controls[0].Op == OpRISCV64NEG {
+ x := b.Controls[0]
+ y := x.Args[0]
+ if !(x.Uses == 1) {
+ break
+ }
+ b.resetWithControl(BlockRISCV64BNEZ, y)
+ return true
+ }
// match: (BNEZ (SUB x y) yes no)
// result: (BNE x y yes no)
for b.Controls[0].Op == OpRISCV64SUB {
diff --git a/src/cmd/compile/internal/ssa/rewriteS390X.go b/src/cmd/compile/internal/ssa/rewriteS390X.go
index 8b41d62c315bd25de6fa2adbdd553c17e1ca2a3e..0d6358614929c1adc323bd5eaadd7bb12c41a796 100644
--- a/src/cmd/compile/internal/ssa/rewriteS390X.go
+++ b/src/cmd/compile/internal/ssa/rewriteS390X.go
@@ -819,6 +819,9 @@ func rewriteValueS390X(v *Value) bool {
case OpSubPtr:
v.Op = OpS390XSUB
return true
+ case OpTailCall:
+ v.Op = OpS390XCALLtail
+ return true
case OpTrunc:
return rewriteValueS390X_OpTrunc(v)
case OpTrunc16to8:
diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go
index 5dab09f85b35780f7113c4cb5e89e70337c7ae49..defd40ddd195e6a233e6205fcb74896ce5b52550 100644
--- a/src/cmd/compile/internal/ssa/rewriteWasm.go
+++ b/src/cmd/compile/internal/ssa/rewriteWasm.go
@@ -556,6 +556,9 @@ func rewriteValueWasm(v *Value) bool {
case OpSubPtr:
v.Op = OpWasmI64Sub
return true
+ case OpTailCall:
+ v.Op = OpWasmLoweredTailCall
+ return true
case OpTrunc:
v.Op = OpWasmF64Trunc
return true
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 52258201ca105163e20197a200794c7604176ad2..fbf227562a92fc4e708aaa94746c6800d9a07918 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -533,6 +533,52 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool {
}
break
}
+ // match: (Add16 x (Sub16 y x))
+ // result: y
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpSub16 {
+ continue
+ }
+ _ = v_1.Args[1]
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ continue
+ }
+ v.copyOf(y)
+ return true
+ }
+ break
+ }
+ // match: (Add16 x (Add16 y (Sub16 z x)))
+ // result: (Add16 y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAdd16 {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ y := v_1_0
+ if v_1_1.Op != OpSub16 {
+ continue
+ }
+ _ = v_1_1.Args[1]
+ z := v_1_1.Args[0]
+ if x != v_1_1.Args[1] {
+ continue
+ }
+ v.reset(OpAdd16)
+ v.AddArg2(y, z)
+ return true
+ }
+ }
+ break
+ }
// match: (Add16 (Add16 i:(Const16 ) z) x)
// cond: (z.Op != OpConst16 && x.Op != OpConst16)
// result: (Add16 i (Add16 z x))
@@ -732,6 +778,52 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool {
}
break
}
+ // match: (Add32 x (Sub32 y x))
+ // result: y
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpSub32 {
+ continue
+ }
+ _ = v_1.Args[1]
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ continue
+ }
+ v.copyOf(y)
+ return true
+ }
+ break
+ }
+ // match: (Add32 x (Add32 y (Sub32 z x)))
+ // result: (Add32 y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAdd32 {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ y := v_1_0
+ if v_1_1.Op != OpSub32 {
+ continue
+ }
+ _ = v_1_1.Args[1]
+ z := v_1_1.Args[0]
+ if x != v_1_1.Args[1] {
+ continue
+ }
+ v.reset(OpAdd32)
+ v.AddArg2(y, z)
+ return true
+ }
+ }
+ break
+ }
// match: (Add32 (Add32 i:(Const32 ) z) x)
// cond: (z.Op != OpConst32 && x.Op != OpConst32)
// result: (Add32 i (Add32 z x))
@@ -958,6 +1050,52 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool {
}
break
}
+ // match: (Add64 x (Sub64 y x))
+ // result: y
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpSub64 {
+ continue
+ }
+ _ = v_1.Args[1]
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ continue
+ }
+ v.copyOf(y)
+ return true
+ }
+ break
+ }
+ // match: (Add64 x (Add64 y (Sub64 z x)))
+ // result: (Add64 y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAdd64 {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ y := v_1_0
+ if v_1_1.Op != OpSub64 {
+ continue
+ }
+ _ = v_1_1.Args[1]
+ z := v_1_1.Args[0]
+ if x != v_1_1.Args[1] {
+ continue
+ }
+ v.reset(OpAdd64)
+ v.AddArg2(y, z)
+ return true
+ }
+ }
+ break
+ }
// match: (Add64 (Add64 i:(Const64 ) z) x)
// cond: (z.Op != OpConst64 && x.Op != OpConst64)
// result: (Add64 i (Add64 z x))
@@ -1184,6 +1322,52 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool {
}
break
}
+ // match: (Add8 x (Sub8 y x))
+ // result: y
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpSub8 {
+ continue
+ }
+ _ = v_1.Args[1]
+ y := v_1.Args[0]
+ if x != v_1.Args[1] {
+ continue
+ }
+ v.copyOf(y)
+ return true
+ }
+ break
+ }
+ // match: (Add8 x (Add8 y (Sub8 z x)))
+ // result: (Add8 y z)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpAdd8 {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ y := v_1_0
+ if v_1_1.Op != OpSub8 {
+ continue
+ }
+ _ = v_1_1.Args[1]
+ z := v_1_1.Args[0]
+ if x != v_1_1.Args[1] {
+ continue
+ }
+ v.reset(OpAdd8)
+ v.AddArg2(y, z)
+ return true
+ }
+ }
+ break
+ }
// match: (Add8 (Add8 i:(Const8 ) z) x)
// cond: (z.Op != OpConst8 && x.Op != OpConst8)
// result: (Add8 i (Add8 z x))
@@ -9899,6 +10083,7 @@ func rewriteValuegeneric_OpLeq8U(v *Value) bool {
func rewriteValuegeneric_OpLess16(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
+ b := v.Block
// match: (Less16 (Const16 [c]) (Const16 [d]))
// result: (ConstBool [c < d])
for {
@@ -9914,6 +10099,45 @@ func rewriteValuegeneric_OpLess16(v *Value) bool {
v.AuxInt = boolToAuxInt(c < d)
return true
}
+ // match: (Less16 (Const16 [0]) x)
+ // cond: isNonNegative(x)
+ // result: (Neq16 (Const16 [0]) x)
+ for {
+ if v_0.Op != OpConst16 {
+ break
+ }
+ t := v_0.Type
+ if auxIntToInt16(v_0.AuxInt) != 0 {
+ break
+ }
+ x := v_1
+ if !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpNeq16)
+ v0 := b.NewValue0(v.Pos, OpConst16, t)
+ v0.AuxInt = int16ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
+ // match: (Less16 x (Const16 [1]))
+ // cond: isNonNegative(x)
+ // result: (Eq16 (Const16 [0]) x)
+ for {
+ x := v_0
+ if v_1.Op != OpConst16 {
+ break
+ }
+ t := v_1.Type
+ if auxIntToInt16(v_1.AuxInt) != 1 || !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpEq16)
+ v0 := b.NewValue0(v.Pos, OpConst16, t)
+ v0.AuxInt = int16ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpLess16U(v *Value) bool {
@@ -9949,6 +10173,7 @@ func rewriteValuegeneric_OpLess16U(v *Value) bool {
func rewriteValuegeneric_OpLess32(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
+ b := v.Block
// match: (Less32 (Const32 [c]) (Const32 [d]))
// result: (ConstBool [c < d])
for {
@@ -9964,6 +10189,45 @@ func rewriteValuegeneric_OpLess32(v *Value) bool {
v.AuxInt = boolToAuxInt(c < d)
return true
}
+ // match: (Less32 (Const32 [0]) x)
+ // cond: isNonNegative(x)
+ // result: (Neq32 (Const32 [0]) x)
+ for {
+ if v_0.Op != OpConst32 {
+ break
+ }
+ t := v_0.Type
+ if auxIntToInt32(v_0.AuxInt) != 0 {
+ break
+ }
+ x := v_1
+ if !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpNeq32)
+ v0 := b.NewValue0(v.Pos, OpConst32, t)
+ v0.AuxInt = int32ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
+ // match: (Less32 x (Const32 [1]))
+ // cond: isNonNegative(x)
+ // result: (Eq32 (Const32 [0]) x)
+ for {
+ x := v_0
+ if v_1.Op != OpConst32 {
+ break
+ }
+ t := v_1.Type
+ if auxIntToInt32(v_1.AuxInt) != 1 || !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpEq32)
+ v0 := b.NewValue0(v.Pos, OpConst32, t)
+ v0.AuxInt = int32ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpLess32F(v *Value) bool {
@@ -10019,6 +10283,7 @@ func rewriteValuegeneric_OpLess32U(v *Value) bool {
func rewriteValuegeneric_OpLess64(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
+ b := v.Block
// match: (Less64 (Const64 [c]) (Const64 [d]))
// result: (ConstBool [c < d])
for {
@@ -10034,6 +10299,45 @@ func rewriteValuegeneric_OpLess64(v *Value) bool {
v.AuxInt = boolToAuxInt(c < d)
return true
}
+ // match: (Less64 (Const64 [0]) x)
+ // cond: isNonNegative(x)
+ // result: (Neq64 (Const64 [0]) x)
+ for {
+ if v_0.Op != OpConst64 {
+ break
+ }
+ t := v_0.Type
+ if auxIntToInt64(v_0.AuxInt) != 0 {
+ break
+ }
+ x := v_1
+ if !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpNeq64)
+ v0 := b.NewValue0(v.Pos, OpConst64, t)
+ v0.AuxInt = int64ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
+ // match: (Less64 x (Const64 [1]))
+ // cond: isNonNegative(x)
+ // result: (Eq64 (Const64 [0]) x)
+ for {
+ x := v_0
+ if v_1.Op != OpConst64 {
+ break
+ }
+ t := v_1.Type
+ if auxIntToInt64(v_1.AuxInt) != 1 || !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpEq64)
+ v0 := b.NewValue0(v.Pos, OpConst64, t)
+ v0.AuxInt = int64ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpLess64F(v *Value) bool {
@@ -10089,6 +10393,7 @@ func rewriteValuegeneric_OpLess64U(v *Value) bool {
func rewriteValuegeneric_OpLess8(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
+ b := v.Block
// match: (Less8 (Const8 [c]) (Const8 [d]))
// result: (ConstBool [c < d])
for {
@@ -10104,6 +10409,45 @@ func rewriteValuegeneric_OpLess8(v *Value) bool {
v.AuxInt = boolToAuxInt(c < d)
return true
}
+ // match: (Less8 (Const8 [0]) x)
+ // cond: isNonNegative(x)
+ // result: (Neq8 (Const8 [0]) x)
+ for {
+ if v_0.Op != OpConst8 {
+ break
+ }
+ t := v_0.Type
+ if auxIntToInt8(v_0.AuxInt) != 0 {
+ break
+ }
+ x := v_1
+ if !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpNeq8)
+ v0 := b.NewValue0(v.Pos, OpConst8, t)
+ v0.AuxInt = int8ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
+ // match: (Less8 x (Const8 [1]))
+ // cond: isNonNegative(x)
+ // result: (Eq8 (Const8 [0]) x)
+ for {
+ x := v_0
+ if v_1.Op != OpConst8 {
+ break
+ }
+ t := v_1.Type
+ if auxIntToInt8(v_1.AuxInt) != 1 || !(isNonNegative(x)) {
+ break
+ }
+ v.reset(OpEq8)
+ v0 := b.NewValue0(v.Pos, OpConst8, t)
+ v0.AuxInt = int8ToAuxInt(0)
+ v.AddArg2(v0, x)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpLess8U(v *Value) bool {
@@ -21402,6 +21746,7 @@ func rewriteValuegeneric_OpSqrt(v *Value) bool {
}
func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
b := v.Block
+ config := b.Func.Config
typ := &b.Func.Config.Types
// match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [1]) mem)
// cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon)
@@ -21436,6 +21781,105 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
v.AddArg2(v0, mem)
return true
}
+ // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [2]) mem)
+ // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)
+ // result: (MakeResult (Eq16 (Load sptr mem) (Const16 [int16(read16(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+ for {
+ if len(v.Args) != 4 {
+ break
+ }
+ callAux := auxToCall(v.Aux)
+ mem := v.Args[3]
+ sptr := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAddr {
+ break
+ }
+ scon := auxToSym(v_1.Aux)
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpSB {
+ break
+ }
+ v_2 := v.Args[2]
+ if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 2 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)) {
+ break
+ }
+ v.reset(OpMakeResult)
+ v0 := b.NewValue0(v.Pos, OpEq16, typ.Bool)
+ v1 := b.NewValue0(v.Pos, OpLoad, typ.Int16)
+ v1.AddArg2(sptr, mem)
+ v2 := b.NewValue0(v.Pos, OpConst16, typ.Int16)
+ v2.AuxInt = int16ToAuxInt(int16(read16(scon, 0, config.ctxt.Arch.ByteOrder)))
+ v0.AddArg2(v1, v2)
+ v.AddArg2(v0, mem)
+ return true
+ }
+ // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [4]) mem)
+ // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)
+ // result: (MakeResult (Eq32 (Load sptr mem) (Const32 [int32(read32(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+ for {
+ if len(v.Args) != 4 {
+ break
+ }
+ callAux := auxToCall(v.Aux)
+ mem := v.Args[3]
+ sptr := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAddr {
+ break
+ }
+ scon := auxToSym(v_1.Aux)
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpSB {
+ break
+ }
+ v_2 := v.Args[2]
+ if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 4 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config)) {
+ break
+ }
+ v.reset(OpMakeResult)
+ v0 := b.NewValue0(v.Pos, OpEq32, typ.Bool)
+ v1 := b.NewValue0(v.Pos, OpLoad, typ.Int32)
+ v1.AddArg2(sptr, mem)
+ v2 := b.NewValue0(v.Pos, OpConst32, typ.Int32)
+ v2.AuxInt = int32ToAuxInt(int32(read32(scon, 0, config.ctxt.Arch.ByteOrder)))
+ v0.AddArg2(v1, v2)
+ v.AddArg2(v0, mem)
+ return true
+ }
+ // match: (StaticLECall {callAux} sptr (Addr {scon} (SB)) (Const64 [8]) mem)
+ // cond: isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) && config.PtrSize == 8
+ // result: (MakeResult (Eq64 (Load sptr mem) (Const64 [int64(read64(scon,0,config.ctxt.Arch.ByteOrder))])) mem)
+ for {
+ if len(v.Args) != 4 {
+ break
+ }
+ callAux := auxToCall(v.Aux)
+ mem := v.Args[3]
+ sptr := v.Args[0]
+ v_1 := v.Args[1]
+ if v_1.Op != OpAddr {
+ break
+ }
+ scon := auxToSym(v_1.Aux)
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpSB {
+ break
+ }
+ v_2 := v.Args[2]
+ if v_2.Op != OpConst64 || auxIntToInt64(v_2.AuxInt) != 8 || !(isSameCall(callAux, "runtime.memequal") && symIsRO(scon) && canLoadUnaligned(config) && config.PtrSize == 8) {
+ break
+ }
+ v.reset(OpMakeResult)
+ v0 := b.NewValue0(v.Pos, OpEq64, typ.Bool)
+ v1 := b.NewValue0(v.Pos, OpLoad, typ.Int64)
+ v1.AddArg2(sptr, mem)
+ v2 := b.NewValue0(v.Pos, OpConst64, typ.Int64)
+ v2.AuxInt = int64ToAuxInt(int64(read64(scon, 0, config.ctxt.Arch.ByteOrder)))
+ v0.AddArg2(v1, v2)
+ v.AddArg2(v0, mem)
+ return true
+ }
return false
}
func rewriteValuegeneric_OpStore(v *Value) bool {
@@ -22590,6 +23034,42 @@ func rewriteValuegeneric_OpSub16(v *Value) bool {
}
break
}
+ // match: (Sub16 (Sub16 x y) x)
+ // result: (Neg16 y)
+ for {
+ if v_0.Op != OpSub16 {
+ break
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpNeg16)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Sub16 x (Add16 x y))
+ // result: (Neg16 y)
+ for {
+ x := v_0
+ if v_1.Op != OpAdd16 {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if x != v_1_0 {
+ continue
+ }
+ y := v_1_1
+ v.reset(OpNeg16)
+ v.AddArg(y)
+ return true
+ }
+ break
+ }
// match: (Sub16 x (Sub16 i:(Const16 ) z))
// cond: (z.Op != OpConst16 && x.Op != OpConst16)
// result: (Sub16 (Add16 x z) i)
@@ -22869,6 +23349,42 @@ func rewriteValuegeneric_OpSub32(v *Value) bool {
}
break
}
+ // match: (Sub32 (Sub32 x y) x)
+ // result: (Neg32 y)
+ for {
+ if v_0.Op != OpSub32 {
+ break
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpNeg32)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Sub32 x (Add32 x y))
+ // result: (Neg32 y)
+ for {
+ x := v_0
+ if v_1.Op != OpAdd32 {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if x != v_1_0 {
+ continue
+ }
+ y := v_1_1
+ v.reset(OpNeg32)
+ v.AddArg(y)
+ return true
+ }
+ break
+ }
// match: (Sub32 x (Sub32 i:(Const32 ) z))
// cond: (z.Op != OpConst32 && x.Op != OpConst32)
// result: (Sub32 (Add32 x z) i)
@@ -23172,6 +23688,42 @@ func rewriteValuegeneric_OpSub64(v *Value) bool {
}
break
}
+ // match: (Sub64 (Sub64 x y) x)
+ // result: (Neg64 y)
+ for {
+ if v_0.Op != OpSub64 {
+ break
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpNeg64)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Sub64 x (Add64 x y))
+ // result: (Neg64 y)
+ for {
+ x := v_0
+ if v_1.Op != OpAdd64 {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if x != v_1_0 {
+ continue
+ }
+ y := v_1_1
+ v.reset(OpNeg64)
+ v.AddArg(y)
+ return true
+ }
+ break
+ }
// match: (Sub64 x (Sub64 i:(Const64 ) z))
// cond: (z.Op != OpConst64 && x.Op != OpConst64)
// result: (Sub64 (Add64 x z) i)
@@ -23475,6 +24027,42 @@ func rewriteValuegeneric_OpSub8(v *Value) bool {
}
break
}
+ // match: (Sub8 (Sub8 x y) x)
+ // result: (Neg8 y)
+ for {
+ if v_0.Op != OpSub8 {
+ break
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if x != v_1 {
+ break
+ }
+ v.reset(OpNeg8)
+ v.AddArg(y)
+ return true
+ }
+ // match: (Sub8 x (Add8 x y))
+ // result: (Neg8 y)
+ for {
+ x := v_0
+ if v_1.Op != OpAdd8 {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if x != v_1_0 {
+ continue
+ }
+ y := v_1_1
+ v.reset(OpNeg8)
+ v.AddArg(y)
+ return true
+ }
+ break
+ }
// match: (Sub8 x (Sub8 i:(Const8 ) z))
// cond: (z.Op != OpConst8 && x.Op != OpConst8)
// result: (Sub8 (Add8 x z) i)
diff --git a/src/cmd/compile/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go
index 4e3e5e75e358d7f1d75a0a5c2888efdfe8fe398a..c5130b2ee50b99157fd86c04c245d926cebf03be 100644
--- a/src/cmd/compile/internal/ssa/schedule.go
+++ b/src/cmd/compile/internal/ssa/schedule.go
@@ -220,7 +220,7 @@ func schedule(f *Func) {
// unless they are phi values (which must be first).
// OpArg also goes first -- if it is stack it register allocates
// to a LoadReg, if it is register it is from the beginning anyway.
- if c.Op == OpPhi || c.Op == OpArg {
+ if score[c.ID] == ScorePhi || score[c.ID] == ScoreArg {
continue
}
score[c.ID] = ScoreControl
diff --git a/src/cmd/compile/internal/ssa/shortcircuit.go b/src/cmd/compile/internal/ssa/shortcircuit.go
index 29abf3c591f902748ef2affd7869d8636e053d12..c0b9eacf4174d8bbb80784d7fb026a87e0bc9835 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/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go
index a8a8f836294b9790c4864f9674aa4dad2b94fe7c..351f824a9f579a2f8d4675e0be4d6d2a256631f2 100644
--- a/src/cmd/compile/internal/ssa/softfloat.go
+++ b/src/cmd/compile/internal/ssa/softfloat.go
@@ -63,6 +63,7 @@ func softfloat(f *Func) {
v.Aux = f.Config.Types.UInt32
case 8:
v.Aux = f.Config.Types.UInt64
+ newInt64 = true
default:
v.Fatalf("bad float type with size %d", size)
}
diff --git a/src/cmd/compile/internal/ssa/stmtlines_test.go b/src/cmd/compile/internal/ssa/stmtlines_test.go
index a510d0b3d0607c268d76b444790ed7744bd1a802..088f9802e6484f849667b54f2edea84863f8cbdb 100644
--- a/src/cmd/compile/internal/ssa/stmtlines_test.go
+++ b/src/cmd/compile/internal/ssa/stmtlines_test.go
@@ -2,6 +2,7 @@ package ssa_test
import (
cmddwarf "cmd/internal/dwarf"
+ "cmd/internal/quoted"
"debug/dwarf"
"debug/elf"
"debug/macho"
@@ -57,7 +58,11 @@ func TestStmtLines(t *testing.T) {
if extld == "" {
extld = "gcc"
}
- enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extld)
+ extldArgs, err := quoted.Split(extld)
+ if err != nil {
+ t.Fatal(err)
+ }
+ enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs)
if err != nil {
t.Fatal(err)
}
@@ -84,6 +89,9 @@ func TestStmtLines(t *testing.T) {
if pkgname == "runtime" {
continue
}
+ if pkgname == "crypto/elliptic/internal/fiat" {
+ continue // golang.org/issue/49372
+ }
if e.Val(dwarf.AttrStmtList) == nil {
continue
}
diff --git a/src/cmd/compile/internal/ssa/testdata/convertline.go b/src/cmd/compile/internal/ssa/testdata/convertline.go
new file mode 100644
index 0000000000000000000000000000000000000000..08f3ae8a35be3fcbaaf9574a08ad665287e80aa1
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/convertline.go
@@ -0,0 +1,16 @@
+package main
+
+import "fmt"
+
+func F[T any](n T) {
+ fmt.Printf("called\n")
+}
+
+func G[T any](n T) {
+ F(n)
+ fmt.Printf("after\n")
+}
+
+func main() {
+ G(3)
+}
diff --git a/src/cmd/compile/internal/ssa/testdata/inline-dump.go b/src/cmd/compile/internal/ssa/testdata/inline-dump.go
new file mode 100644
index 0000000000000000000000000000000000000000..97893b6f212f986b9931fd5f678759b6e0ded95d
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/inline-dump.go
@@ -0,0 +1,17 @@
+package foo
+
+func f(m, n int) int {
+ a := g(n)
+ b := g(m)
+ return a + b
+}
+
+func g(x int) int {
+ y := h(x + 1)
+ z := h(x - 1)
+ return y + z
+}
+
+func h(x int) int {
+ return x * x
+}
diff --git a/src/cmd/compile/internal/ssa/testdata/pushback.go b/src/cmd/compile/internal/ssa/testdata/pushback.go
new file mode 100644
index 0000000000000000000000000000000000000000..754e6cbb2307876bb4bed202c968f64c88658acd
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/pushback.go
@@ -0,0 +1,30 @@
+package main
+
+type Node struct {
+ Circular bool
+}
+
+type ExtNode[V any] struct {
+ v V
+ Node
+}
+
+type List[V any] struct {
+ root *ExtNode[V]
+ len int
+}
+
+func (list *List[V]) PushBack(arg V) {
+ if list.len == 0 {
+ list.root = &ExtNode[V]{v: arg}
+ list.root.Circular = true
+ list.len++
+ return
+ }
+ list.len++
+}
+
+func main() {
+ var v List[int]
+ v.PushBack(1)
+}
diff --git a/src/cmd/compile/internal/ssa/testdata/sayhi.go b/src/cmd/compile/internal/ssa/testdata/sayhi.go
new file mode 100644
index 0000000000000000000000000000000000000000..680e1eb3a18f35ee0190a5673416cd292df5d72d
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/testdata/sayhi.go
@@ -0,0 +1,12 @@
+package foo
+
+import (
+ "fmt"
+ "sync"
+)
+
+func sayhi(n int, wg *sync.WaitGroup) {
+ fmt.Println("hi", n)
+ fmt.Println("hi", n)
+ wg.Done()
+}
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 630e4814b99f1b38c182d684bb2b6c16f30de704..7b411a46124130b6fea4d5e04d53bd931b21c031 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)
@@ -351,11 +345,13 @@ func (v *Value) reset(op Op) {
// invalidateRecursively marks a value as invalid (unused)
// and after decrementing reference counts on its Args,
// also recursively invalidates any of those whose use
-// count goes to zero.
+// count goes to zero. It returns whether any of the
+// invalidated values was marked with IsStmt.
//
// BEWARE of doing this *before* you've applied intended
// updates to SSA.
-func (v *Value) invalidateRecursively() {
+func (v *Value) invalidateRecursively() bool {
+ lostStmt := v.Pos.IsStmt() == src.PosIsStmt
if v.InCache {
v.Block.Func.unCache(v)
}
@@ -364,7 +360,8 @@ func (v *Value) invalidateRecursively() {
for _, a := range v.Args {
a.Uses--
if a.Uses == 0 {
- a.invalidateRecursively()
+ lost := a.invalidateRecursively()
+ lostStmt = lost || lostStmt
}
}
@@ -375,6 +372,7 @@ func (v *Value) invalidateRecursively() {
v.AuxInt = 0
v.Aux = nil
+ return lostStmt
}
// copyOf is called from rewrite rules.
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index 419d91d0d367e609dcf9874cced1a58c04acdb4a..5120cd1086ac6088b54636180a6f44364d4dc550 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -544,7 +544,7 @@ func IsStackAddr(v *Value) bool {
v = v.Args[0]
}
switch v.Op {
- case OpSP, OpLocalAddr, OpSelectNAddr:
+ case OpSP, OpLocalAddr, OpSelectNAddr, OpGetCallerSP:
return true
}
return false
@@ -552,6 +552,9 @@ func IsStackAddr(v *Value) bool {
// IsGlobalAddr reports whether v is known to be an address of a global (or nil).
func IsGlobalAddr(v *Value) bool {
+ for v.Op == OpOffPtr || v.Op == OpAddPtr || v.Op == OpPtrIndex || v.Op == OpCopy {
+ v = v.Args[0]
+ }
if v.Op == OpAddr && v.Args[0].Op == OpSB {
return true // address of a global
}
@@ -647,7 +650,7 @@ func IsSanitizerSafeAddr(v *Value) bool {
// read-only once initialized.
return true
case OpAddr:
- return v.Aux.(*obj.LSym).Type == objabi.SRODATA
+ return v.Aux.(*obj.LSym).Type == objabi.SRODATA || v.Aux.(*obj.LSym).Type == objabi.SLIBFUZZER_EXTRA_COUNTER
}
return false
}
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go
index e460adaf95d143f2403f40a0ab0607a68ef2ffb1..3a653e46b4b542c744feaf90b780e78cafee76b1 100644
--- a/src/cmd/compile/internal/ssagen/abi.go
+++ b/src/cmd/compile/internal/ssagen/abi.go
@@ -152,6 +152,9 @@ func (s *SymABIs) GenABIWrappers() {
// Apply definitions.
defABI, hasDefABI := s.defs[symName]
if hasDefABI {
+ if len(fn.Body) != 0 {
+ base.ErrorfAt(fn.Pos(), "%v defined in both Go and assembly", fn)
+ }
fn.ABI = defABI
}
@@ -215,8 +218,6 @@ func (s *SymABIs) GenABIWrappers() {
}
if !buildcfg.Experiment.RegabiWrappers {
- // We'll generate ABI aliases instead of
- // wrappers once we have LSyms in InitLSym.
continue
}
@@ -248,16 +249,11 @@ func InitLSym(f *ir.Func, hasBody bool) {
// the funcsym for either the defining
// function or its wrapper as appropriate.
//
- // If we're using ABI aliases instead of
- // wrappers, we only InitLSym for the defining
- // ABI of a function, so we make the funcsym
- // when we see that.
+ // If we're not using ABI wrappers, we only
+ // InitLSym for the defining ABI of a function,
+ // so we make the funcsym when we see that.
staticdata.NeedFuncSym(f)
}
- if !buildcfg.Experiment.RegabiWrappers {
- // Create ABI aliases instead of wrappers.
- forEachWrapperABI(f, makeABIAlias)
- }
}
if hasBody {
setupTextLSym(f, 0)
@@ -278,22 +274,6 @@ func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
}
}
-// makeABIAlias creates a new ABI alias so calls to f via wrapperABI
-// will be resolved directly to f's ABI by the linker.
-func makeABIAlias(f *ir.Func, wrapperABI obj.ABI) {
- // These LSyms have the same name as the native function, so
- // we create them directly rather than looking them up.
- // The uniqueness of f.lsym ensures uniqueness of asym.
- asym := &obj.LSym{
- Name: f.LSym.Name,
- Type: objabi.SABIALIAS,
- R: []obj.Reloc{{Sym: f.LSym}}, // 0 size, so "informational"
- }
- asym.SetABI(wrapperABI)
- asym.Set(obj.AttrDuplicateOK, true)
- base.Ctxt.ABIAliases = append(base.Ctxt.ABIAliases, asym)
-}
-
// makeABIWrapper creates a new function that will be called with
// wrapperABI and calls "f" using f.ABI.
func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
@@ -379,18 +359,16 @@ func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
}
var tail ir.Node
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ call.IsDDD = tfn.Type().IsVariadic()
+ tail = call
if tailcall {
- tail = ir.NewTailCallStmt(base.Pos, f.Nname)
- } else {
- call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
- call.Args = ir.ParamNames(tfn.Type())
- call.IsDDD = tfn.Type().IsVariadic()
- tail = call
- if tfn.Type().NumResults() > 0 {
- n := ir.NewReturnStmt(base.Pos, nil)
- n.Results = []ir.Node{call}
- tail = n
- }
+ tail = ir.NewTailCallStmt(base.Pos, call)
+ } else if tfn.Type().NumResults() > 0 {
+ n := ir.NewReturnStmt(base.Pos, nil)
+ n.Results = []ir.Node{call}
+ tail = n
}
fn.Body.Append(tail)
diff --git a/src/cmd/compile/internal/ssagen/arch.go b/src/cmd/compile/internal/ssagen/arch.go
index 7215f42c059a147272500b7916df51287e7bdd7b..483e45cad43c7445c46032111434fffe16cd62a7 100644
--- a/src/cmd/compile/internal/ssagen/arch.go
+++ b/src/cmd/compile/internal/ssagen/arch.go
@@ -29,8 +29,7 @@ type ArchInfo struct {
// at function entry, and it is ok to clobber registers.
ZeroRange func(*objw.Progs, *obj.Prog, int64, int64, *uint32) *obj.Prog
- Ginsnop func(*objw.Progs) *obj.Prog
- Ginsnopdefer func(*objw.Progs) *obj.Prog // special ginsnop for deferreturn
+ Ginsnop func(*objw.Progs) *obj.Prog
// SSAMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
SSAMarkMoves func(*State, *ssa.Block)
@@ -42,10 +41,10 @@ type ArchInfo struct {
// for all values in the block before SSAGenBlock.
SSAGenBlock func(s *State, b, next *ssa.Block)
- // LoadRegResults emits instructions that loads register-assigned results
- // into registers. They are already in memory (PPARAMOUT nodes).
- // Used in open-coded defer return path.
- LoadRegResults func(s *State, f *ssa.Func)
+ // LoadRegResult emits instructions that loads register-assigned result
+ // at n+off (n is PPARAMOUT) to register reg. The result is already in
+ // memory. Used in open-coded defer return path.
+ LoadRegResult func(s *State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog
// SpillArgReg emits instructions that spill reg to n+off.
SpillArgReg func(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog
diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go
index 62567535d76caccdd37e1fdea3996b51a0fac13c..86d40e239d67a11816fc109bd90ab1504fa72e66 100644
--- a/src/cmd/compile/internal/ssagen/pgen.go
+++ b/src/cmd/compile/internal/ssagen/pgen.go
@@ -57,8 +57,8 @@ func cmpstackvarlt(a, b *ir.Name) bool {
return ap
}
- if a.Type().Width != b.Type().Width {
- return a.Type().Width > b.Type().Width
+ if a.Type().Size() != b.Type().Size() {
+ return a.Type().Size() > b.Type().Size()
}
return a.Sym().Name < b.Sym().Name
@@ -75,7 +75,22 @@ func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// allocate space. In particular, it excludes arguments and results, which are in
// the callers frame.
func needAlloc(n *ir.Name) bool {
- return n.Class == ir.PAUTO || n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters()
+ if n.Op() != ir.ONAME {
+ base.FatalfAt(n.Pos(), "%v has unexpected Op %v", n, n.Op())
+ }
+
+ switch n.Class {
+ case ir.PAUTO:
+ return true
+ case ir.PPARAM:
+ return false
+ case ir.PPARAMOUT:
+ return n.IsOutputParamInRegisters()
+
+ default:
+ base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class)
+ return false
+ }
}
func (s *ssafn) AllocFrame(f *ssa.Func) {
@@ -114,7 +129,10 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
}
}
- sort.Sort(byStackVar(fn.Dcl))
+ // Use sort.Stable instead of sort.Sort so stack layout (and thus
+ // compiler output) is less sensitive to frontend changes that
+ // introduce or remove unused variables.
+ sort.Stable(byStackVar(fn.Dcl))
// Reassign stack offsets of the locals that are used.
lastHasPtr := false
@@ -129,7 +147,7 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
}
types.CalcSize(n.Type())
- w := n.Type().Width
+ w := n.Type().Size()
if w >= types.MaxWidth || w < 0 {
base.Fatalf("bad width")
}
@@ -141,7 +159,7 @@ func (s *ssafn) AllocFrame(f *ssa.Func) {
w = 1
}
s.stksize += w
- s.stksize = types.Rnd(s.stksize, int64(n.Type().Align))
+ s.stksize = types.Rnd(s.stksize, n.Type().Alignment())
if n.Type().HasPointers() {
s.stkptrsize = s.stksize
lastHasPtr = true
diff --git a/src/cmd/compile/internal/ssagen/pgen_test.go b/src/cmd/compile/internal/ssagen/pgen_test.go
deleted file mode 100644
index 69ed8ad74e974490714d93321c62239cd0b1bb89..0000000000000000000000000000000000000000
--- a/src/cmd/compile/internal/ssagen/pgen_test.go
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2015 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 ssagen
-
-import (
- "reflect"
- "sort"
- "testing"
-
- "cmd/compile/internal/ir"
- "cmd/compile/internal/typecheck"
- "cmd/compile/internal/types"
- "cmd/internal/src"
-)
-
-func typeWithoutPointers() *types.Type {
- return types.NewStruct(types.NoPkg, []*types.Field{
- types.NewField(src.NoXPos, nil, types.New(types.TINT)),
- })
-}
-
-func typeWithPointers() *types.Type {
- return types.NewStruct(types.NoPkg, []*types.Field{
- types.NewField(src.NoXPos, nil, types.NewPtr(types.New(types.TINT))),
- })
-}
-
-func markUsed(n *ir.Name) *ir.Name {
- n.SetUsed(true)
- return n
-}
-
-func markNeedZero(n *ir.Name) *ir.Name {
- n.SetNeedzero(true)
- return n
-}
-
-// Test all code paths for cmpstackvarlt.
-func TestCmpstackvar(t *testing.T) {
- nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name {
- if s == nil {
- s = &types.Sym{Name: "."}
- }
- n := typecheck.NewName(s)
- n.SetType(t)
- n.SetFrameOffset(xoffset)
- n.Class = cl
- return n
- }
- testdata := []struct {
- a, b *ir.Name
- lt bool
- }{
- {
- nod(0, nil, nil, ir.PAUTO),
- nod(0, nil, nil, ir.PFUNC),
- false,
- },
- {
- nod(0, nil, nil, ir.PFUNC),
- nod(0, nil, nil, ir.PAUTO),
- true,
- },
- {
- nod(0, nil, nil, ir.PFUNC),
- nod(10, nil, nil, ir.PFUNC),
- true,
- },
- {
- nod(20, nil, nil, ir.PFUNC),
- nod(10, nil, nil, ir.PFUNC),
- false,
- },
- {
- nod(10, nil, nil, ir.PFUNC),
- nod(10, nil, nil, ir.PFUNC),
- false,
- },
- {
- nod(10, nil, nil, ir.PPARAM),
- nod(20, nil, nil, ir.PPARAMOUT),
- true,
- },
- {
- nod(10, nil, nil, ir.PPARAMOUT),
- nod(20, nil, nil, ir.PPARAM),
- true,
- },
- {
- markUsed(nod(0, nil, nil, ir.PAUTO)),
- nod(0, nil, nil, ir.PAUTO),
- true,
- },
- {
- nod(0, nil, nil, ir.PAUTO),
- markUsed(nod(0, nil, nil, ir.PAUTO)),
- false,
- },
- {
- nod(0, typeWithoutPointers(), nil, ir.PAUTO),
- nod(0, typeWithPointers(), nil, ir.PAUTO),
- false,
- },
- {
- nod(0, typeWithPointers(), nil, ir.PAUTO),
- nod(0, typeWithoutPointers(), nil, ir.PAUTO),
- true,
- },
- {
- markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)),
- nod(0, &types.Type{}, nil, ir.PAUTO),
- true,
- },
- {
- nod(0, &types.Type{}, nil, ir.PAUTO),
- markNeedZero(nod(0, &types.Type{}, nil, ir.PAUTO)),
- false,
- },
- {
- nod(0, &types.Type{Width: 1}, nil, ir.PAUTO),
- nod(0, &types.Type{Width: 2}, nil, ir.PAUTO),
- false,
- },
- {
- nod(0, &types.Type{Width: 2}, nil, ir.PAUTO),
- nod(0, &types.Type{Width: 1}, nil, ir.PAUTO),
- true,
- },
- {
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
- true,
- },
- {
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- false,
- },
- {
- nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- false,
- },
- }
- for _, d := range testdata {
- got := cmpstackvarlt(d.a, d.b)
- if got != d.lt {
- t.Errorf("want %v < %v", d.a, d.b)
- }
- // If we expect a < b to be true, check that b < a is false.
- if d.lt && cmpstackvarlt(d.b, d.a) {
- t.Errorf("unexpected %v < %v", d.b, d.a)
- }
- }
-}
-
-func TestStackvarSort(t *testing.T) {
- nod := func(xoffset int64, t *types.Type, s *types.Sym, cl ir.Class) *ir.Name {
- n := typecheck.NewName(s)
- n.SetType(t)
- n.SetFrameOffset(xoffset)
- n.Class = cl
- return n
- }
- inp := []*ir.Name{
- nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC),
- markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
- nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
- markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
- nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
- }
- want := []*ir.Name{
- nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(0, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(10, &types.Type{}, &types.Sym{}, ir.PFUNC),
- nod(20, &types.Type{}, &types.Sym{}, ir.PFUNC),
- markUsed(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
- markNeedZero(nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO)),
- nod(0, &types.Type{Width: 2}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{Width: 1}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "abc"}, ir.PAUTO),
- nod(0, &types.Type{}, &types.Sym{Name: "xyz"}, ir.PAUTO),
- nod(0, typeWithoutPointers(), &types.Sym{}, ir.PAUTO),
- }
- sort.Sort(byStackVar(inp))
- if !reflect.DeepEqual(want, inp) {
- t.Error("sort failed")
- for i := range inp {
- g := inp[i]
- w := want[i]
- eq := reflect.DeepEqual(w, g)
- if !eq {
- t.Log(i, w, g)
- }
- }
- }
-}
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index dfa76006de790110b05def67fe3d8a4451584245..265ef1aab32e5fcc213168123c6e4a7c4fb87e55 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -87,8 +87,7 @@ func InitConfig() {
_ = types.NewPtr(types.Types[types.TINT64]) // *int64
_ = types.NewPtr(types.ErrorType) // *error
types.NewPtrCacheEnabled = false
- ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0)
- ssaConfig.SoftFloat = Arch.SoftFloat
+ ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat)
ssaConfig.Race = base.Flag.Race
ssaCaches = make([]ssa.Cache, base.Flag.LowerC)
@@ -97,6 +96,7 @@ func InitConfig() {
ir.Syms.AssertE2I2 = typecheck.LookupRuntimeFunc("assertE2I2")
ir.Syms.AssertI2I = typecheck.LookupRuntimeFunc("assertI2I")
ir.Syms.AssertI2I2 = typecheck.LookupRuntimeFunc("assertI2I2")
+ ir.Syms.CheckPtrAlignment = typecheck.LookupRuntimeFunc("checkptrAlignment")
ir.Syms.Deferproc = typecheck.LookupRuntimeFunc("deferproc")
ir.Syms.DeferprocStack = typecheck.LookupRuntimeFunc("deferprocStack")
ir.Syms.Deferreturn = typecheck.LookupRuntimeFunc("deferreturn")
@@ -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")
@@ -279,18 +281,6 @@ func regAbiForFuncType(ft *types.Func) bool {
return np > 0 && strings.Contains(ft.Params.FieldType(np-1).String(), magicLastTypeName)
}
-// getParam returns the Field of ith param of node n (which is a
-// function/method/interface call), where the receiver of a method call is
-// considered as the 0th parameter. This does not include the receiver of an
-// interface call.
-func getParam(n *ir.CallExpr, i int) *types.Field {
- t := n.X.Type()
- if n.Op() == ir.OCALLMETH {
- base.Fatalf("OCALLMETH missed by walkCall")
- }
- return t.Params().Field(i)
-}
-
// dvarint writes a varint v to the funcdata in symbol x and returns the new offset
func dvarint(x *obj.LSym, off int, v int64) int {
if v < 0 || v > 1e9 {
@@ -324,66 +314,22 @@ func dvarint(x *obj.LSym, off int, v int64) int {
// for stack variables are specified as the number of bytes below varp (pointer to the
// top of the local variables) for their starting address. The format is:
//
-// - Max total argument size among all the defers
// - Offset of the deferBits variable
// - Number of defers in the function
// - Information about each defer call, in reverse order of appearance in the function:
-// - Total argument size of the call
// - Offset of the closure value to call
-// - Number of arguments (including interface receiver or method receiver as first arg)
-// - Information about each argument
-// - Offset of the stored defer argument in this function's frame
-// - Size of the argument
-// - Offset of where argument should be placed in the args frame when making call
func (s *state) emitOpenDeferInfo() {
x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer")
+ x.Set(obj.AttrContentAddressable, true)
s.curfn.LSym.Func().OpenCodedDeferInfo = x
off := 0
-
- // Compute maxargsize (max size of arguments for all defers)
- // first, so we can output it first to the funcdata
- var maxargsize int64
- for i := len(s.openDefers) - 1; i >= 0; i-- {
- r := s.openDefers[i]
- argsize := r.n.X.Type().ArgWidth() // TODO register args: but maybe use of abi0 will make this easy
- if argsize > maxargsize {
- maxargsize = argsize
- }
- }
- off = dvarint(x, off, maxargsize)
off = dvarint(x, off, -s.deferBitsTemp.FrameOffset())
off = dvarint(x, off, int64(len(s.openDefers)))
// Write in reverse-order, for ease of running in that order at runtime
for i := len(s.openDefers) - 1; i >= 0; i-- {
r := s.openDefers[i]
- off = dvarint(x, off, r.n.X.Type().ArgWidth())
off = dvarint(x, off, -r.closureNode.FrameOffset())
- numArgs := len(r.argNodes)
- if r.rcvrNode != nil {
- // If there's an interface receiver, treat/place it as the first
- // arg. (If there is a method receiver, it's already included as
- // first arg in r.argNodes.)
- numArgs++
- }
- off = dvarint(x, off, int64(numArgs))
- argAdjust := 0 // presence of receiver offsets the parameter count.
- if r.rcvrNode != nil {
- off = dvarint(x, off, -okOffset(r.rcvrNode.FrameOffset()))
- off = dvarint(x, off, s.config.PtrSize)
- off = dvarint(x, off, 0) // This is okay because defer records use ABI0 (for now)
- argAdjust++
- }
-
- // TODO(register args) assume abi0 for this?
- ab := s.f.ABI0
- pri := ab.ABIAnalyzeFuncType(r.n.X.Type().FuncType())
- for j, arg := range r.argNodes {
- f := getParam(r.n, j)
- off = dvarint(x, off, -okOffset(arg.FrameOffset()))
- off = dvarint(x, off, f.Type.Size())
- off = dvarint(x, off, okOffset(pri.InParam(j+argAdjust).FrameOffset(pri)))
- }
}
}
@@ -424,6 +370,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
if fn.Pragma&ir.CgoUnsafeArgs != 0 {
s.cgoUnsafeArgs = true
}
+ s.checkPtrEnabled = ir.ShouldCheckPtr(fn, 1)
fe := ssafn{
curfn: fn,
@@ -537,6 +484,19 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
var params *abi.ABIParamResultInfo
params = s.f.ABISelf.ABIAnalyze(fn.Type(), true)
+ // The backend's stackframe pass prunes away entries from the fn's
+ // Dcl list, including PARAMOUT nodes that correspond to output
+ // params passed in registers. Walk the Dcl list and capture these
+ // nodes to a side list, so that we'll have them available during
+ // DWARF-gen later on. See issue 48573 for more details.
+ var debugInfo ssa.FuncDebug
+ for _, n := range fn.Dcl {
+ if n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters() {
+ debugInfo.RegOutputParams = append(debugInfo.RegOutputParams, n)
+ }
+ }
+ fn.DebugInfo = &debugInfo
+
// Generate addresses of local declarations
s.decladdrs = map[*ir.Name]*ssa.Value{}
for _, n := range fn.Dcl {
@@ -580,7 +540,7 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
}
// Populate closure variables.
- if !fn.ClosureCalled() {
+ if fn.Needctxt() {
clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field
for _, n := range fn.ClosureVars {
@@ -650,7 +610,6 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
// it mimics the behavior of the former ABI (everything stored) and because it's not 100%
// clear if naming conventions are respected in autogenerated code.
// TODO figure out exactly what's unused, don't spill it. Make liveness fine-grained, also.
- // TODO non-amd64 architectures have link registers etc that may require adjustment here.
for _, p := range params.InParams() {
typs, offs := p.RegisterTypesAndOffsets()
for i, t := range typs {
@@ -768,6 +727,31 @@ func (s *state) newObject(typ *types.Type) *ssa.Value {
return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, s.reflectType(typ))[0]
}
+func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) {
+ if !n.Type().IsPtr() {
+ s.Fatalf("expected pointer type: %v", n.Type())
+ }
+ elem := n.Type().Elem()
+ if count != nil {
+ if !elem.IsArray() {
+ s.Fatalf("expected array type: %v", elem)
+ }
+ elem = elem.Elem()
+ }
+ size := elem.Size()
+ // Casting from larger type to smaller one is ok, so for smallest type, do nothing.
+ if elem.Alignment() == 1 && (size == 0 || size == 1 || count == nil) {
+ return
+ }
+ if count == nil {
+ count = s.constInt(types.Types[types.TUINTPTR], 1)
+ }
+ if count.Type.Size() != s.config.PtrSize {
+ s.Fatalf("expected count fit to an uintptr size, have: %d, want: %d", count.Type.Size(), s.config.PtrSize)
+ }
+ s.rtcall(ir.Syms.CheckPtrAlignment, true, nil, v, s.reflectType(elem), count)
+}
+
// reflectType returns an SSA value representing a pointer to typ's
// reflection type descriptor.
func (s *state) reflectType(typ *types.Type) *ssa.Value {
@@ -865,16 +849,6 @@ type openDeferInfo struct {
// function, method, or interface call, to store a closure that panic
// processing can use for this defer.
closureNode *ir.Name
- // If defer call is interface call, the address of the argtmp where the
- // receiver is stored
- rcvr *ssa.Value
- // The node representing the argtmp where the receiver is stored
- rcvrNode *ir.Name
- // The addresses of the argtmps where the evaluated arguments of the defer
- // function call are stored.
- argVals []*ssa.Value
- // The nodes representing the argtmps where the args of the defer are stored
- argNodes []*ir.Name
}
type state struct {
@@ -930,10 +904,11 @@ type state struct {
// Used to deduplicate panic calls.
panics map[funcLine]*ssa.Block
- cgoUnsafeArgs bool
- hasdefer bool // whether the function contains a defer statement
- softFloat bool
- hasOpenDefers bool // whether we are doing open-coded defers
+ cgoUnsafeArgs bool
+ hasdefer bool // whether the function contains a defer statement
+ softFloat bool
+ hasOpenDefers bool // whether we are doing open-coded defers
+ checkPtrEnabled bool // whether to insert checkptr instrumentation
// If doing open-coded defers, list of info about the defer calls in
// scanning order. Hence, at exit we should run these defers in reverse
@@ -1285,10 +1260,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
}
@@ -1367,6 +1342,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")
}
@@ -1491,7 +1476,12 @@ func (s *state) stmt(n ir.Node) {
case ir.OAS2DOTTYPE:
n := n.(*ir.AssignListStmt)
- res, resok := s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true)
+ var res, resok *ssa.Value
+ if n.Rhs[0].Op() == ir.ODOTTYPE2 {
+ res, resok = s.dottype(n.Rhs[0].(*ir.TypeAssertExpr), true)
+ } else {
+ res, resok = s.dynamicDottype(n.Rhs[0].(*ir.DynamicTypeAssertExpr), true)
+ }
deref := false
if !TypeOK(n.Rhs[0].Type()) {
if res.Op != ssa.OpLoad {
@@ -1732,9 +1722,11 @@ func (s *state) stmt(n ir.Node) {
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
- b := s.exit()
- b.Kind = ssa.BlockRetJmp // override BlockRet
- b.Aux = callTargetLSym(n.Target)
+ s.callResult(n.Call, callTail)
+ call := s.mem()
+ b := s.endBlock()
+ b.Kind = ssa.BlockRetJmp // could use BlockExit. BlockRetJmp is mostly for clarity.
+ b.SetControl(call)
case ir.OCONTINUE, ir.OBREAK:
n := n.(*ir.BranchStmt)
@@ -2387,8 +2379,181 @@ func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op {
return x
}
+func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value {
+ if ft.IsBoolean() && tt.IsKind(types.TUINT8) {
+ // Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
+ return s.newValue1(ssa.OpCopy, tt, v)
+ }
+ if ft.IsInteger() && tt.IsInteger() {
+ var op ssa.Op
+ if tt.Size() == ft.Size() {
+ op = ssa.OpCopy
+ } else if tt.Size() < ft.Size() {
+ // truncation
+ switch 10*ft.Size() + tt.Size() {
+ case 21:
+ op = ssa.OpTrunc16to8
+ case 41:
+ op = ssa.OpTrunc32to8
+ case 42:
+ op = ssa.OpTrunc32to16
+ case 81:
+ op = ssa.OpTrunc64to8
+ case 82:
+ op = ssa.OpTrunc64to16
+ case 84:
+ op = ssa.OpTrunc64to32
+ default:
+ s.Fatalf("weird integer truncation %v -> %v", ft, tt)
+ }
+ } else if ft.IsSigned() {
+ // sign extension
+ switch 10*ft.Size() + tt.Size() {
+ case 12:
+ op = ssa.OpSignExt8to16
+ case 14:
+ op = ssa.OpSignExt8to32
+ case 18:
+ op = ssa.OpSignExt8to64
+ case 24:
+ op = ssa.OpSignExt16to32
+ case 28:
+ op = ssa.OpSignExt16to64
+ case 48:
+ op = ssa.OpSignExt32to64
+ default:
+ s.Fatalf("bad integer sign extension %v -> %v", ft, tt)
+ }
+ } else {
+ // zero extension
+ switch 10*ft.Size() + tt.Size() {
+ case 12:
+ op = ssa.OpZeroExt8to16
+ case 14:
+ op = ssa.OpZeroExt8to32
+ case 18:
+ op = ssa.OpZeroExt8to64
+ case 24:
+ op = ssa.OpZeroExt16to32
+ case 28:
+ op = ssa.OpZeroExt16to64
+ case 48:
+ op = ssa.OpZeroExt32to64
+ default:
+ s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
+ }
+ }
+ return s.newValue1(op, tt, v)
+ }
+
+ if ft.IsFloat() || tt.IsFloat() {
+ conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
+ if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
+ if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
+ conv = conv1
+ }
+ }
+ if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat {
+ if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
+ conv = conv1
+ }
+ }
+
+ if Arch.LinkArch.Family == sys.MIPS && !s.softFloat {
+ if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() {
+ // tt is float32 or float64, and ft is also unsigned
+ if tt.Size() == 4 {
+ return s.uint32Tofloat32(n, v, ft, tt)
+ }
+ if tt.Size() == 8 {
+ return s.uint32Tofloat64(n, v, ft, tt)
+ }
+ } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() {
+ // ft is float32 or float64, and tt is unsigned integer
+ if ft.Size() == 4 {
+ return s.float32ToUint32(n, v, ft, tt)
+ }
+ if ft.Size() == 8 {
+ return s.float64ToUint32(n, v, ft, tt)
+ }
+ }
+ }
+
+ if !ok {
+ s.Fatalf("weird float conversion %v -> %v", ft, tt)
+ }
+ op1, op2, it := conv.op1, conv.op2, conv.intermediateType
+
+ if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid {
+ // normal case, not tripping over unsigned 64
+ if op1 == ssa.OpCopy {
+ if op2 == ssa.OpCopy {
+ return v
+ }
+ return s.newValueOrSfCall1(op2, tt, v)
+ }
+ if op2 == ssa.OpCopy {
+ return s.newValueOrSfCall1(op1, tt, v)
+ }
+ return s.newValueOrSfCall1(op2, tt, s.newValueOrSfCall1(op1, types.Types[it], v))
+ }
+ // Tricky 64-bit unsigned cases.
+ if ft.IsInteger() {
+ // tt is float32 or float64, and ft is also unsigned
+ if tt.Size() == 4 {
+ return s.uint64Tofloat32(n, v, ft, tt)
+ }
+ if tt.Size() == 8 {
+ return s.uint64Tofloat64(n, v, ft, tt)
+ }
+ s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt)
+ }
+ // ft is float32 or float64, and tt is unsigned integer
+ if ft.Size() == 4 {
+ return s.float32ToUint64(n, v, ft, tt)
+ }
+ if ft.Size() == 8 {
+ return s.float64ToUint64(n, v, ft, tt)
+ }
+ s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt)
+ return nil
+ }
+
+ if ft.IsComplex() && tt.IsComplex() {
+ var op ssa.Op
+ if ft.Size() == tt.Size() {
+ switch ft.Size() {
+ case 8:
+ op = ssa.OpRound32F
+ case 16:
+ op = ssa.OpRound64F
+ default:
+ s.Fatalf("weird complex conversion %v -> %v", ft, tt)
+ }
+ } else if ft.Size() == 8 && tt.Size() == 16 {
+ op = ssa.OpCvt32Fto64F
+ } else if ft.Size() == 16 && tt.Size() == 8 {
+ op = ssa.OpCvt64Fto32F
+ } else {
+ s.Fatalf("weird complex conversion %v -> %v", ft, tt)
+ }
+ ftp := types.FloatForComplex(ft)
+ ttp := types.FloatForComplex(tt)
+ return s.newValue2(ssa.OpComplexMake, tt,
+ s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, v)),
+ s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, v)))
+ }
+
+ s.Fatalf("unhandled OCONV %s -> %s", ft.Kind(), tt.Kind())
+ return nil
+}
+
// expr converts the expression n to ssa, adds it to s and returns the ssa result.
func (s *state) expr(n ir.Node) *ssa.Value {
+ return s.exprCheckPtr(n, true)
+}
+
+func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
if ir.HasUniquePos(n) {
// ONAMEs and named OLITERALs have the line number
// of the decl, not the use. See issue 14742.
@@ -2536,6 +2701,9 @@ func (s *state) expr(n ir.Node) *ssa.Value {
// unsafe.Pointer <--> *T
if to.IsUnsafePtr() && from.IsPtrShaped() || from.IsUnsafePtr() && to.IsPtrShaped() {
+ if s.checkPtrEnabled && checkPtrOK && to.IsPtr() && from.IsUnsafePtr() {
+ s.checkPtrAlignment(n, v, nil)
+ }
return v
}
@@ -2547,8 +2715,8 @@ func (s *state) expr(n ir.Node) *ssa.Value {
types.CalcSize(from)
types.CalcSize(to)
- if from.Width != to.Width {
- s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Width, to, to.Width)
+ if from.Size() != to.Size() {
+ s.Fatalf("CONVNOP width mismatch %v (%d) -> %v (%d)\n", from, from.Size(), to, to.Size())
return nil
}
if etypesign(from.Kind()) != etypesign(to.Kind()) {
@@ -2574,180 +2742,18 @@ func (s *state) expr(n ir.Node) *ssa.Value {
case ir.OCONV:
n := n.(*ir.ConvExpr)
x := s.expr(n.X)
- ft := n.X.Type() // from type
- tt := n.Type() // to type
- if ft.IsBoolean() && tt.IsKind(types.TUINT8) {
- // Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
- return s.newValue1(ssa.OpCopy, n.Type(), x)
- }
- if ft.IsInteger() && tt.IsInteger() {
- var op ssa.Op
- if tt.Size() == ft.Size() {
- op = ssa.OpCopy
- } else if tt.Size() < ft.Size() {
- // truncation
- switch 10*ft.Size() + tt.Size() {
- case 21:
- op = ssa.OpTrunc16to8
- case 41:
- op = ssa.OpTrunc32to8
- case 42:
- op = ssa.OpTrunc32to16
- case 81:
- op = ssa.OpTrunc64to8
- case 82:
- op = ssa.OpTrunc64to16
- case 84:
- op = ssa.OpTrunc64to32
- default:
- s.Fatalf("weird integer truncation %v -> %v", ft, tt)
- }
- } else if ft.IsSigned() {
- // sign extension
- switch 10*ft.Size() + tt.Size() {
- case 12:
- op = ssa.OpSignExt8to16
- case 14:
- op = ssa.OpSignExt8to32
- case 18:
- op = ssa.OpSignExt8to64
- case 24:
- op = ssa.OpSignExt16to32
- case 28:
- op = ssa.OpSignExt16to64
- case 48:
- op = ssa.OpSignExt32to64
- default:
- s.Fatalf("bad integer sign extension %v -> %v", ft, tt)
- }
- } else {
- // zero extension
- switch 10*ft.Size() + tt.Size() {
- case 12:
- op = ssa.OpZeroExt8to16
- case 14:
- op = ssa.OpZeroExt8to32
- case 18:
- op = ssa.OpZeroExt8to64
- case 24:
- op = ssa.OpZeroExt16to32
- case 28:
- op = ssa.OpZeroExt16to64
- case 48:
- op = ssa.OpZeroExt32to64
- default:
- s.Fatalf("weird integer sign extension %v -> %v", ft, tt)
- }
- }
- return s.newValue1(op, n.Type(), x)
- }
-
- if ft.IsFloat() || tt.IsFloat() {
- conv, ok := fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]
- if s.config.RegSize == 4 && Arch.LinkArch.Family != sys.MIPS && !s.softFloat {
- if conv1, ok1 := fpConvOpToSSA32[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
- conv = conv1
- }
- }
- if Arch.LinkArch.Family == sys.ARM64 || Arch.LinkArch.Family == sys.Wasm || Arch.LinkArch.Family == sys.S390X || s.softFloat {
- if conv1, ok1 := uint64fpConvOpToSSA[twoTypes{s.concreteEtype(ft), s.concreteEtype(tt)}]; ok1 {
- conv = conv1
- }
- }
-
- if Arch.LinkArch.Family == sys.MIPS && !s.softFloat {
- if ft.Size() == 4 && ft.IsInteger() && !ft.IsSigned() {
- // tt is float32 or float64, and ft is also unsigned
- if tt.Size() == 4 {
- return s.uint32Tofloat32(n, x, ft, tt)
- }
- if tt.Size() == 8 {
- return s.uint32Tofloat64(n, x, ft, tt)
- }
- } else if tt.Size() == 4 && tt.IsInteger() && !tt.IsSigned() {
- // ft is float32 or float64, and tt is unsigned integer
- if ft.Size() == 4 {
- return s.float32ToUint32(n, x, ft, tt)
- }
- if ft.Size() == 8 {
- return s.float64ToUint32(n, x, ft, tt)
- }
- }
- }
-
- if !ok {
- s.Fatalf("weird float conversion %v -> %v", ft, tt)
- }
- op1, op2, it := conv.op1, conv.op2, conv.intermediateType
-
- if op1 != ssa.OpInvalid && op2 != ssa.OpInvalid {
- // normal case, not tripping over unsigned 64
- if op1 == ssa.OpCopy {
- if op2 == ssa.OpCopy {
- return x
- }
- return s.newValueOrSfCall1(op2, n.Type(), x)
- }
- if op2 == ssa.OpCopy {
- return s.newValueOrSfCall1(op1, n.Type(), x)
- }
- return s.newValueOrSfCall1(op2, n.Type(), s.newValueOrSfCall1(op1, types.Types[it], x))
- }
- // Tricky 64-bit unsigned cases.
- if ft.IsInteger() {
- // tt is float32 or float64, and ft is also unsigned
- if tt.Size() == 4 {
- return s.uint64Tofloat32(n, x, ft, tt)
- }
- if tt.Size() == 8 {
- return s.uint64Tofloat64(n, x, ft, tt)
- }
- s.Fatalf("weird unsigned integer to float conversion %v -> %v", ft, tt)
- }
- // ft is float32 or float64, and tt is unsigned integer
- if ft.Size() == 4 {
- return s.float32ToUint64(n, x, ft, tt)
- }
- if ft.Size() == 8 {
- return s.float64ToUint64(n, x, ft, tt)
- }
- s.Fatalf("weird float to unsigned integer conversion %v -> %v", ft, tt)
- return nil
- }
-
- if ft.IsComplex() && tt.IsComplex() {
- var op ssa.Op
- if ft.Size() == tt.Size() {
- switch ft.Size() {
- case 8:
- op = ssa.OpRound32F
- case 16:
- op = ssa.OpRound64F
- default:
- s.Fatalf("weird complex conversion %v -> %v", ft, tt)
- }
- } else if ft.Size() == 8 && tt.Size() == 16 {
- op = ssa.OpCvt32Fto64F
- } else if ft.Size() == 16 && tt.Size() == 8 {
- op = ssa.OpCvt64Fto32F
- } else {
- s.Fatalf("weird complex conversion %v -> %v", ft, tt)
- }
- ftp := types.FloatForComplex(ft)
- ttp := types.FloatForComplex(tt)
- return s.newValue2(ssa.OpComplexMake, tt,
- s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexReal, ftp, x)),
- s.newValueOrSfCall1(op, ttp, s.newValue1(ssa.OpComplexImag, ftp, x)))
- }
-
- s.Fatalf("unhandled OCONV %s -> %s", n.X.Type().Kind(), n.Type().Kind())
- return nil
+ return s.conv(n, x, n.X.Type(), n.Type())
case ir.ODOTTYPE:
n := n.(*ir.TypeAssertExpr)
res, _ := s.dottype(n, false)
return res
+ case ir.ODYNAMICDOTTYPE:
+ n := n.(*ir.DynamicTypeAssertExpr)
+ res, _ := s.dynamicDottype(n, false)
+ return res
+
// binary ops
case ir.OLT, ir.OEQ, ir.ONE, ir.OLE, ir.OGE, ir.OGT:
n := n.(*ir.BinaryExpr)
@@ -3021,7 +3027,7 @@ func (s *state) expr(n ir.Node) *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)
@@ -3073,7 +3079,8 @@ func (s *state) expr(n ir.Node) *ssa.Value {
z := s.constInt(types.Types[types.TINT], 0)
s.boundsCheck(z, z, ssa.BoundsIndex, false)
// The return value won't be live, return junk.
- return s.newValue0(ssa.OpUnknown, n.Type())
+ // But not quite junk, in case bounds checks are turned off. See issue 48092.
+ return s.zeroVal(n.Type())
}
len := s.constInt(types.Types[types.TINT], bound)
s.boundsCheck(i, len, ssa.BoundsIndex, n.Bounded()) // checks i == 0
@@ -3137,7 +3144,8 @@ func (s *state) expr(n ir.Node) *ssa.Value {
case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
n := n.(*ir.SliceExpr)
- v := s.expr(n.X)
+ check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr()
+ v := s.exprCheckPtr(n.X, !check)
var i, j, k *ssa.Value
if n.Low != nil {
i = s.expr(n.Low)
@@ -3149,6 +3157,10 @@ func (s *state) expr(n ir.Node) *ssa.Value {
k = s.expr(n.Max)
}
p, l, c := s.slice(v, i, j, k, n.Bounded())
+ if check {
+ // Emit checkptr instrumentation after bound check to prevent false positive, see #46938.
+ s.checkPtrAlignment(n.X.(*ir.ConvExpr), v, s.conv(n.Max, k, k.Type, types.Types[types.TUINTPTR]))
+ }
return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
case ir.OSLICESTR:
@@ -3183,7 +3195,7 @@ func (s *state) expr(n ir.Node) *ssa.Value {
}
fallthrough
- case ir.OCALLINTER, ir.OCALLMETH:
+ case ir.OCALLINTER:
n := n.(*ir.CallExpr)
return s.callResult(n, callNormal)
@@ -3191,6 +3203,14 @@ func (s *state) expr(n ir.Node) *ssa.Value {
n := n.(*ir.CallExpr)
return s.newValue1(ssa.OpGetG, n.Type(), s.mem())
+ case ir.OGETCALLERPC:
+ n := n.(*ir.CallExpr)
+ return s.newValue0(ssa.OpGetCallerPC, n.Type())
+
+ case ir.OGETCALLERSP:
+ n := n.(*ir.CallExpr)
+ return s.newValue0(ssa.OpGetCallerSP, n.Type())
+
case ir.OAPPEND:
return s.append(n.(*ir.CallExpr), false)
@@ -3212,6 +3232,11 @@ func (s *state) expr(n ir.Node) *ssa.Value {
n := n.(*ir.BinaryExpr)
ptr := s.expr(n.X)
len := s.expr(n.Y)
+
+ // Force len to uintptr to prevent misuse of garbage bits in the
+ // upper part of the register (#48536).
+ len = s.conv(n, len, len.Type, types.Types[types.TUINTPTR])
+
return s.newValue2(ssa.OpAddPtr, n.Type(), ptr, len)
default:
@@ -3653,6 +3678,7 @@ const (
callDefer
callDeferStack
callGo
+ callTail
)
type sfRtCallDef struct {
@@ -3703,6 +3729,16 @@ func softfloatInit() {
// TODO: do not emit sfcall if operation can be optimized to constant in later
// opt phase
func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
+ f2i := func(t *types.Type) *types.Type {
+ switch t.Kind() {
+ case types.TFLOAT32:
+ return types.Types[types.TUINT32]
+ case types.TFLOAT64:
+ return types.Types[types.TUINT64]
+ }
+ return t
+ }
+
if callDef, ok := softFloatOps[op]; ok {
switch op {
case ssa.OpLess32F,
@@ -3715,7 +3751,19 @@ func (s *state) sfcall(op ssa.Op, args ...*ssa.Value) (*ssa.Value, bool) {
args[1] = s.newValue1(s.ssaOp(ir.ONEG, types.Types[callDef.rtype]), args[1].Type, args[1])
}
- result := s.rtcall(callDef.rtfn, true, []*types.Type{types.Types[callDef.rtype]}, args...)[0]
+ // runtime functions take uints for floats and returns uints.
+ // Convert to uints so we use the right calling convention.
+ for i, a := range args {
+ if a.Type.IsFloat() {
+ args[i] = s.newValue1(ssa.OpCopy, f2i(a.Type), a)
+ }
+ }
+
+ rt := types.Types[callDef.rtype]
+ result := s.rtcall(callDef.rtfn, true, []*types.Type{f2i(rt)}, args...)[0]
+ if rt.IsFloat() {
+ result = s.newValue1(ssa.OpCopy, rt, result)
+ }
if op == ssa.OpNeq32F || op == ssa.OpNeq64F {
result = s.newValue1(ssa.OpNot, result.Type, result)
}
@@ -3808,7 +3856,7 @@ func InitTables() {
}
return s.newValue2(ssa.OpMul64uover, types.NewTuple(types.Types[types.TUINT], types.Types[types.TUINT]), args[0], args[1])
},
- sys.AMD64, sys.I386, sys.MIPS64)
+ sys.AMD64, sys.I386, sys.MIPS64, sys.RISCV64)
add("runtime", "KeepAlive",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
data := s.newValue1(ssa.OpIData, s.f.Config.Types.BytePtr, args[0])
@@ -3834,6 +3882,13 @@ func InitTables() {
},
all...)
+ addF("runtime", "publicationBarrier",
+ func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue1(ssa.OpPubBarrier, types.TypeMem, s.mem())
+ return nil
+ },
+ sys.ARM64)
+
/******** runtime/internal/sys ********/
addF("runtime/internal/sys", "Ctz32",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
@@ -3856,6 +3911,21 @@ func InitTables() {
},
sys.AMD64, sys.ARM64, sys.ARM, sys.S390X)
+ /****** Prefetch ******/
+ makePrefetchFunc := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ s.vars[memVar] = s.newValue2(op, types.TypeMem, args[0], s.mem())
+ return nil
+ }
+ }
+
+ // Make Prefetch intrinsics for supported platforms
+ // On the unsupported platforms stub function will be eliminated
+ addF("runtime/internal/sys", "Prefetch", makePrefetchFunc(ssa.OpPrefetchCache),
+ sys.AMD64, sys.ARM64, sys.PPC64)
+ addF("runtime/internal/sys", "PrefetchStreamed", makePrefetchFunc(ssa.OpPrefetchCacheStreamed),
+ sys.AMD64, sys.ARM64, sys.PPC64)
+
/******** runtime/internal/atomic ********/
addF("runtime/internal/atomic", "Load",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
@@ -4183,23 +4253,28 @@ func InitTables() {
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpAbs, types.Types[types.TFLOAT64], args[0])
},
- sys.ARM64, sys.ARM, sys.PPC64, sys.Wasm)
+ sys.ARM64, sys.ARM, sys.PPC64, sys.RISCV64, sys.Wasm)
addF("math", "Copysign",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue2(ssa.OpCopysign, types.Types[types.TFLOAT64], args[0], args[1])
},
- sys.PPC64, sys.Wasm)
+ sys.PPC64, sys.RISCV64, sys.Wasm)
addF("math", "FMA",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
},
- sys.ARM64, sys.PPC64, sys.S390X)
+ sys.ARM64, sys.PPC64, sys.RISCV64, sys.S390X)
addF("math", "FMA",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
if !s.config.UseFMA {
s.vars[n] = s.callResult(n, callNormal) // types.Types[TFLOAT64]
return s.variable(n, types.Types[types.TFLOAT64])
}
+
+ if buildcfg.GOAMD64 >= 3 {
+ return s.newValue3(ssa.OpFMA, types.Types[types.TFLOAT64], args[0], args[1], args[2])
+ }
+
v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasFMA)
b := s.endBlock()
b.Kind = ssa.BlockIf
@@ -4262,6 +4337,10 @@ func InitTables() {
makeRoundAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ if buildcfg.GOAMD64 >= 2 {
+ return s.newValue1(op, types.Types[types.TFLOAT64], args[0])
+ }
+
v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasSSE41)
b := s.endBlock()
b.Kind = ssa.BlockIf
@@ -4367,7 +4446,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 {
@@ -4376,7 +4455,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 {
@@ -4466,8 +4545,12 @@ func InitTables() {
sys.AMD64, sys.ARM64, sys.S390X, sys.PPC64, sys.Wasm)
alias("math/bits", "RotateLeft", "math/bits", "RotateLeft64", p8...)
- makeOnesCountAMD64 := func(op64 ssa.Op, op32 ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ makeOnesCountAMD64 := func(op ssa.Op) func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
+ if buildcfg.GOAMD64 >= 2 {
+ return s.newValue1(op, types.Types[types.TINT], args[0])
+ }
+
v := s.entryNewValue0A(ssa.OpHasCPUFeature, types.Types[types.TBOOL], ir.Syms.X86HasPOPCNT)
b := s.endBlock()
b.Kind = ssa.BlockIf
@@ -4481,10 +4564,6 @@ func InitTables() {
// We have the intrinsic - use it directly.
s.startBlock(bTrue)
- op := op64
- if s.config.PtrSize == 4 {
- op = op32
- }
s.vars[n] = s.newValue1(op, types.Types[types.TINT], args[0])
s.endBlock().AddEdgeTo(bEnd)
@@ -4499,7 +4578,7 @@ func InitTables() {
}
}
addF("math/bits", "OnesCount64",
- makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount64),
+ makeOnesCountAMD64(ssa.OpPopCount64),
sys.AMD64)
addF("math/bits", "OnesCount64",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
@@ -4507,7 +4586,7 @@ func InitTables() {
},
sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm)
addF("math/bits", "OnesCount32",
- makeOnesCountAMD64(ssa.OpPopCount32, ssa.OpPopCount32),
+ makeOnesCountAMD64(ssa.OpPopCount32),
sys.AMD64)
addF("math/bits", "OnesCount32",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
@@ -4515,7 +4594,7 @@ func InitTables() {
},
sys.PPC64, sys.ARM64, sys.S390X, sys.Wasm)
addF("math/bits", "OnesCount16",
- makeOnesCountAMD64(ssa.OpPopCount16, ssa.OpPopCount16),
+ makeOnesCountAMD64(ssa.OpPopCount16),
sys.AMD64)
addF("math/bits", "OnesCount16",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
@@ -4528,15 +4607,15 @@ func InitTables() {
},
sys.S390X, sys.PPC64, sys.Wasm)
addF("math/bits", "OnesCount",
- makeOnesCountAMD64(ssa.OpPopCount64, ssa.OpPopCount32),
+ makeOnesCountAMD64(ssa.OpPopCount64),
sys.AMD64)
addF("math/bits", "Mul64",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue2(ssa.OpMul64uhilo, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1])
},
- sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.MIPS64)
- alias("math/bits", "Mul", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE)
- alias("runtime/internal/math", "Mul64", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE)
+ sys.AMD64, sys.ARM64, sys.PPC64, sys.S390X, sys.MIPS64, sys.RISCV64)
+ alias("math/bits", "Mul", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE, sys.ArchRISCV64)
+ alias("runtime/internal/math", "Mul64", "math/bits", "Mul64", sys.ArchAMD64, sys.ArchARM64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchS390X, sys.ArchMIPS64, sys.ArchMIPS64LE, sys.ArchRISCV64)
addF("math/bits", "Add64",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue3(ssa.OpAdd64carry, types.NewTuple(types.Types[types.TUINT64], types.Types[types.TUINT64]), args[0], args[1], args[2])
@@ -4687,17 +4766,14 @@ func (s *state) intrinsicArgs(n *ir.CallExpr) []*ssa.Value {
return args
}
-// openDeferRecord adds code to evaluate and store the args for an open-code defer
+// openDeferRecord adds code to evaluate and store the function for an open-code defer
// call, and records info about the defer, so we can generate proper code on the
// exit paths. n is the sub-node of the defer node that is the actual function
-// call. We will also record funcdata information on where the args are stored
+// call. We will also record funcdata information on where the function is stored
// (as well as the deferBits variable), and this will enable us to run the proper
// defer calls during panics.
func (s *state) openDeferRecord(n *ir.CallExpr) {
- var args []*ssa.Value
- var argNodes []*ir.Name
-
- if buildcfg.Experiment.RegabiDefer && (len(n.Args) != 0 || n.Op() == ir.OCALLINTER || n.X.Type().NumResults() != 0) {
+ if len(n.Args) != 0 || n.Op() != ir.OCALLFUNC || n.X.Type().NumResults() != 0 {
s.Fatalf("defer call with arguments or results: %v", n)
}
@@ -4705,48 +4781,20 @@ func (s *state) openDeferRecord(n *ir.CallExpr) {
n: n,
}
fn := n.X
- if n.Op() == ir.OCALLFUNC {
- // We must always store the function value in a stack slot for the
- // runtime panic code to use. But in the defer exit code, we will
- // call the function directly if it is a static function.
- closureVal := s.expr(fn)
- closure := s.openDeferSave(nil, fn.Type(), closureVal)
- opendefer.closureNode = closure.Aux.(*ir.Name)
- if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) {
- opendefer.closure = closure
- }
- } else if n.Op() == ir.OCALLMETH {
- base.Fatalf("OCALLMETH missed by walkCall")
- } else {
- if fn.Op() != ir.ODOTINTER {
- base.Fatalf("OCALLINTER: n.Left not an ODOTINTER: %v", fn.Op())
- }
- fn := fn.(*ir.SelectorExpr)
- closure, rcvr := s.getClosureAndRcvr(fn)
- opendefer.closure = s.openDeferSave(nil, closure.Type, closure)
- // Important to get the receiver type correct, so it is recognized
- // as a pointer for GC purposes.
- opendefer.rcvr = s.openDeferSave(nil, fn.Type().Recv().Type, rcvr)
- opendefer.closureNode = opendefer.closure.Aux.(*ir.Name)
- opendefer.rcvrNode = opendefer.rcvr.Aux.(*ir.Name)
- }
- for _, argn := range n.Args {
- var v *ssa.Value
- if TypeOK(argn.Type()) {
- v = s.openDeferSave(nil, argn.Type(), s.expr(argn))
- } else {
- v = s.openDeferSave(argn, argn.Type(), nil)
- }
- args = append(args, v)
- argNodes = append(argNodes, v.Aux.(*ir.Name))
+ // We must always store the function value in a stack slot for the
+ // runtime panic code to use. But in the defer exit code, we will
+ // call the function directly if it is a static function.
+ closureVal := s.expr(fn)
+ closure := s.openDeferSave(fn.Type(), closureVal)
+ opendefer.closureNode = closure.Aux.(*ir.Name)
+ if !(fn.Op() == ir.ONAME && fn.(*ir.Name).Class == ir.PFUNC) {
+ opendefer.closure = closure
}
- opendefer.argVals = args
- opendefer.argNodes = argNodes
index := len(s.openDefers)
s.openDefers = append(s.openDefers, opendefer)
// Update deferBits only after evaluation and storage to stack of
- // args/receiver/interface is successful.
+ // the function is successful.
bitvalue := s.constInt8(types.Types[types.TUINT8], 1< int64(4*types.PtrSize) {
+ if t.Size() > int64(4*types.PtrSize) {
// 4*Widthptr is an arbitrary constant. We want it
// to be at least 3*Widthptr so slices can be registerized.
// Too big and we'll introduce too much register pressure.
@@ -5881,7 +5867,7 @@ func (s *state) slice(v, i, j, k *ssa.Value, bounded bool) (p, l, c *ssa.Value)
//
// Where mask(x) is 0 if x==0 and -1 if x>0 and stride is the width
// of the element type.
- stride := s.constInt(types.Types[types.TINT], ptr.Type.Elem().Width)
+ stride := s.constInt(types.Types[types.TINT], ptr.Type.Elem().Size())
// The delta is the number of bytes to offset ptr by.
delta := s.newValue2(mulOp, types.Types[types.TINT], i, stride)
@@ -5936,7 +5922,6 @@ func (s *state) uint64Tofloat(cvttab *u642fcvtTab, n ir.Node, x *ssa.Value, ft,
// } else {
// y = uintX(x) ; y = x & 1
// z = uintX(x) ; z = z >> 1
- // z = z >> 1
// z = z | y
// result = floatY(z)
// result = result + result
@@ -6089,7 +6074,7 @@ func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value {
s.vars[n] = s.load(lenType, x)
case ir.OCAP:
// capacity is stored in the second word for chan
- sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Width, x)
+ sw := s.newValue1I(ssa.OpOffPtr, lenType.PtrTo(), lenType.Size(), x)
s.vars[n] = s.load(lenType, sw)
default:
s.Fatalf("op must be OLEN or OCAP")
@@ -6210,14 +6195,38 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n ir.Node, x *ssa.Value, ft, tt *
func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Value) {
iface := s.expr(n.X) // input interface
target := s.reflectType(n.Type()) // target type
- byteptr := s.f.Config.Types.BytePtr
+ var targetItab *ssa.Value
+ if n.Itab != nil {
+ targetItab = s.expr(n.Itab)
+ }
+ return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, targetItab, commaok)
+}
+
+func (s *state) dynamicDottype(n *ir.DynamicTypeAssertExpr, commaok bool) (res, resok *ssa.Value) {
+ iface := s.expr(n.X)
+ target := s.expr(n.T)
+ var itab *ssa.Value
+ if !n.X.Type().IsEmptyInterface() && !n.Type().IsInterface() {
+ byteptr := s.f.Config.Types.BytePtr
+ itab = target
+ target = s.load(byteptr, s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)) // itab.typ
+ }
+ return s.dottype1(n.Pos(), n.X.Type(), n.Type(), iface, target, itab, commaok)
+}
- if n.Type().IsInterface() {
- if n.Type().IsEmptyInterface() {
+// dottype1 implements a x.(T) operation. iface is the argument (x), dst is the type we're asserting to (T)
+// and src is the type we're asserting from.
+// target is the *runtime._type of dst.
+// If src is a nonempty interface and dst is not an interface, targetItab is an itab representing (dst, src). Otherwise it is nil.
+// commaok is true if the caller wants a boolean success value. Otherwise, the generated code panics if the conversion fails.
+func (s *state) dottype1(pos src.XPos, src, dst *types.Type, iface, target, targetItab *ssa.Value, commaok bool) (res, resok *ssa.Value) {
+ byteptr := s.f.Config.Types.BytePtr
+ if dst.IsInterface() {
+ if dst.IsEmptyInterface() {
// Converting to an empty interface.
// Input could be an empty or nonempty interface.
if base.Debug.TypeAssert > 0 {
- base.WarnfAt(n.Pos(), "type assertion inlined")
+ base.WarnfAt(pos, "type assertion inlined")
}
// Get itab/type field from input.
@@ -6225,7 +6234,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
// Conversion succeeds iff that field is not nil.
cond := s.newValue2(ssa.OpNeqPtr, types.Types[types.TBOOL], itab, s.constNil(byteptr))
- if n.X.Type().IsEmptyInterface() && commaok {
+ if src.IsEmptyInterface() && commaok {
// Converting empty interface to empty interface with ,ok is just a nil check.
return iface, cond
}
@@ -6247,7 +6256,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
// On success, return (perhaps modified) input interface.
s.startBlock(bOk)
- if n.X.Type().IsEmptyInterface() {
+ if src.IsEmptyInterface() {
res = iface // Use input interface unchanged.
return
}
@@ -6255,7 +6264,7 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(types.PtrSize), itab)
typ := s.load(byteptr, off)
idata := s.newValue1(ssa.OpIData, byteptr, iface)
- res = s.newValue2(ssa.OpIMake, n.Type(), typ, idata)
+ res = s.newValue2(ssa.OpIMake, dst, typ, idata)
return
}
@@ -6277,62 +6286,62 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
bFail.AddEdgeTo(bEnd)
s.startBlock(bEnd)
idata := s.newValue1(ssa.OpIData, byteptr, iface)
- res = s.newValue2(ssa.OpIMake, n.Type(), s.variable(typVar, byteptr), idata)
+ res = s.newValue2(ssa.OpIMake, dst, s.variable(typVar, byteptr), idata)
resok = cond
delete(s.vars, typVar)
return
}
// converting to a nonempty interface needs a runtime call.
if base.Debug.TypeAssert > 0 {
- base.WarnfAt(n.Pos(), "type assertion not inlined")
+ base.WarnfAt(pos, "type assertion not inlined")
}
if !commaok {
fn := ir.Syms.AssertI2I
- if n.X.Type().IsEmptyInterface() {
+ if src.IsEmptyInterface() {
fn = ir.Syms.AssertE2I
}
data := s.newValue1(ssa.OpIData, types.Types[types.TUNSAFEPTR], iface)
tab := s.newValue1(ssa.OpITab, byteptr, iface)
tab = s.rtcall(fn, true, []*types.Type{byteptr}, target, tab)[0]
- return s.newValue2(ssa.OpIMake, n.Type(), tab, data), nil
+ return s.newValue2(ssa.OpIMake, dst, tab, data), nil
}
fn := ir.Syms.AssertI2I2
- if n.X.Type().IsEmptyInterface() {
+ if src.IsEmptyInterface() {
fn = ir.Syms.AssertE2I2
}
- res = s.rtcall(fn, true, []*types.Type{n.Type()}, target, iface)[0]
- resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(n.Type()))
+ res = s.rtcall(fn, true, []*types.Type{dst}, target, iface)[0]
+ resok = s.newValue2(ssa.OpNeqInter, types.Types[types.TBOOL], res, s.constInterface(dst))
return
}
if base.Debug.TypeAssert > 0 {
- base.WarnfAt(n.Pos(), "type assertion inlined")
+ base.WarnfAt(pos, "type assertion inlined")
}
// Converting to a concrete type.
- direct := types.IsDirectIface(n.Type())
+ direct := types.IsDirectIface(dst)
itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface
if base.Debug.TypeAssert > 0 {
- base.WarnfAt(n.Pos(), "type assertion inlined")
+ base.WarnfAt(pos, "type assertion inlined")
}
- var targetITab *ssa.Value
- if n.X.Type().IsEmptyInterface() {
+ var wantedFirstWord *ssa.Value
+ if src.IsEmptyInterface() {
// Looking for pointer to target type.
- targetITab = target
+ wantedFirstWord = target
} else {
// Looking for pointer to itab for target type and source interface.
- targetITab = s.expr(n.Itab)
+ wantedFirstWord = targetItab
}
var tmp ir.Node // temporary for use with large types
var addr *ssa.Value // address of tmp
- if commaok && !TypeOK(n.Type()) {
+ if commaok && !TypeOK(dst) {
// unSSAable type, use temporary.
// TODO: get rid of some of these temporaries.
- tmp, addr = s.temp(n.Pos(), n.Type())
+ tmp, addr = s.temp(pos, dst)
}
- cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, targetITab)
+ cond := s.newValue2(ssa.OpEqPtr, types.Types[types.TBOOL], itab, wantedFirstWord)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.SetControl(cond)
@@ -6346,8 +6355,8 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
if !commaok {
// on failure, panic by calling panicdottype
s.startBlock(bFail)
- taddr := s.reflectType(n.X.Type())
- if n.X.Type().IsEmptyInterface() {
+ taddr := s.reflectType(src)
+ if src.IsEmptyInterface() {
s.rtcall(ir.Syms.PanicdottypeE, false, nil, itab, target, taddr)
} else {
s.rtcall(ir.Syms.PanicdottypeI, false, nil, itab, target, taddr)
@@ -6356,10 +6365,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
// on success, return data from interface
s.startBlock(bOk)
if direct {
- return s.newValue1(ssa.OpIData, n.Type(), iface), nil
+ return s.newValue1(ssa.OpIData, dst, iface), nil
}
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
- return s.load(n.Type(), p), nil
+ p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface)
+ return s.load(dst, p), nil
}
// commaok is the more complicated case because we have
@@ -6373,14 +6382,14 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
s.startBlock(bOk)
if tmp == nil {
if direct {
- s.vars[valVar] = s.newValue1(ssa.OpIData, n.Type(), iface)
+ s.vars[valVar] = s.newValue1(ssa.OpIData, dst, iface)
} else {
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
- s.vars[valVar] = s.load(n.Type(), p)
+ p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface)
+ s.vars[valVar] = s.load(dst, p)
}
} else {
- p := s.newValue1(ssa.OpIData, types.NewPtr(n.Type()), iface)
- s.move(n.Type(), addr, p)
+ p := s.newValue1(ssa.OpIData, types.NewPtr(dst), iface)
+ s.move(dst, addr, p)
}
s.vars[okVar] = s.constBool(true)
s.endBlock()
@@ -6389,9 +6398,9 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
// type assertion failed
s.startBlock(bFail)
if tmp == nil {
- s.vars[valVar] = s.zeroVal(n.Type())
+ s.vars[valVar] = s.zeroVal(dst)
} else {
- s.zero(n.Type(), addr)
+ s.zero(dst, addr)
}
s.vars[okVar] = s.constBool(false)
s.endBlock()
@@ -6400,10 +6409,10 @@ func (s *state) dottype(n *ir.TypeAssertExpr, commaok bool) (res, resok *ssa.Val
// merge point
s.startBlock(bEnd)
if tmp == nil {
- res = s.variable(valVar, n.Type())
+ res = s.variable(valVar, dst)
delete(s.vars, valVar)
} else {
- res = s.load(n.Type(), addr)
+ res = s.load(dst, addr)
s.vars[memVar] = s.newValue1A(ssa.OpVarKill, types.TypeMem, tmp.(*ir.Name), s.mem())
}
resok = s.variable(okVar, types.Types[types.TBOOL])
@@ -6568,6 +6577,22 @@ func (s *State) DebugFriendlySetPosFrom(v *ssa.Value) {
// explicit statement boundaries should appear
// in the generated code.
if p.IsStmt() != src.PosIsStmt {
+ if s.pp.Pos.IsStmt() == src.PosIsStmt && s.pp.Pos.SameFileAndLine(p) {
+ // If s.pp.Pos already has a statement mark, then it was set here (below) for
+ // the previous value. If an actual instruction had been emitted for that
+ // value, then the statement mark would have been reset. Since the statement
+ // mark of s.pp.Pos was not reset, this position (file/line) still needs a
+ // statement mark on an instruction. If file and line for this value are
+ // the same as the previous value, then the first instruction for this
+ // value will work to take the statement mark. Return early to avoid
+ // resetting the statement mark.
+ //
+ // The reset of s.pp.Pos occurs in (*Progs).Prog() -- if it emits
+ // an instruction, and the instruction's statement mark was set,
+ // and it is not one of the LosesStmtMark instructions,
+ // then Prog() resets the statement mark on the (*Progs).Pos.
+ return
+ }
p = p.WithNotStmt()
// Calls use the pos attached to v, but copy the statement mark from State
}
@@ -6586,6 +6611,7 @@ func emitArgInfo(e *ssafn, f *ssa.Func, pp *objw.Progs) {
}
x := EmitArgInfo(e.curfn, f.OwnAux.ABIInfo())
+ x.Set(obj.AttrContentAddressable, true)
e.curfn.LSym.Func().ArgInfo = x
// Emit a funcdata pointing at the arg info data.
@@ -6599,6 +6625,9 @@ func emitArgInfo(e *ssafn, f *ssa.Func, pp *objw.Progs) {
// emit argument info (locations on stack) of f for traceback.
func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
x := base.Ctxt.Lookup(fmt.Sprintf("%s.arginfo%d", f.LSym.Name, f.ABI))
+ // NOTE: do not set ContentAddressable here. This may be referenced from
+ // assembly code by name (in this case f is a declaration).
+ // Instead, set it in emitArgInfo above.
PtrSize := int64(types.PtrSize)
uintptrTyp := types.Types[types.TUINTPTR]
@@ -6713,7 +6742,13 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
return true
}
- for _, a := range abiInfo.InParams() {
+ start := 0
+ if strings.Contains(f.LSym.Name, "[") {
+ // Skip the dictionary argument - it is implicit and the user doesn't need to see it.
+ start = 1
+ }
+
+ for _, a := range abiInfo.InParams()[start:] {
if !visitType(a.FrameOffset(abiInfo), a.Type, 0) {
break
}
@@ -6735,6 +6770,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 {
@@ -6753,7 +6789,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
var progToValue map[*obj.Prog]*ssa.Value
var progToBlock map[*obj.Prog]*ssa.Block
var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point.
- if f.PrintOrHtmlSSA {
+ gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name]
+ if gatherPrintInfo {
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
f.Logf("genssa %s\n", f.Name)
@@ -6791,10 +6828,13 @@ 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
s.lineRunStart = nil
+ s.SetPos(s.pp.Pos.WithNotStmt()) // It needs a non-empty Pos, but cannot be a statement boundary (yet).
// Attach a "default" liveness info. Normally this will be
// overwritten in the Values loop below for each Value. But
@@ -6804,6 +6844,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 {
@@ -6860,11 +6907,18 @@ 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
}
- if f.PrintOrHtmlSSA {
+ if gatherPrintInfo {
for ; x != s.pp.Next; x = x.Link {
progToValue[x] = v
}
@@ -6894,7 +6948,7 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
x := s.pp.Next
s.SetPos(b.Pos)
Arch.SSAGenBlock(&s, b, next)
- if f.PrintOrHtmlSSA {
+ if gatherPrintInfo {
for ; x != s.pp.Next; x = x.Link {
progToBlock[x] = b
}
@@ -6921,8 +6975,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
// recovers a panic, it will return to caller with right results.
// The results are already in memory, because they are not SSA'd
// when the function has defers (see canSSAName).
- if f.OwnAux.ABIInfo().OutRegistersUsed() != 0 {
- Arch.LoadRegResults(&s, f)
+ for _, o := range f.OwnAux.ABIInfo().OutParams() {
+ n := o.Name.(*ir.Name)
+ rts, offs := o.RegisterTypesAndOffsets()
+ for i := range o.Registers {
+ Arch.LoadRegResult(&s, f, rts[i], ssa.ObjRegForAbiReg(o.Registers[i], f.Config), n, offs[i])
+ }
}
pp.Prog(obj.ARET)
@@ -6975,12 +7033,12 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
if base.Ctxt.Flag_locationlists {
var debugInfo *ssa.FuncDebug
+ debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug)
if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 {
- debugInfo = ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset)
+ ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
} else {
- debugInfo = ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset)
+ ssa.BuildFuncDebug(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
}
- e.curfn.DebugInfo = debugInfo
bstart := s.bstart
idToIdx := make([]int, f.NumBlocks())
for i, b := range f.Blocks {
@@ -7069,6 +7127,54 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
buf.WriteString("")
f.HTMLWriter.WriteColumn("genssa", "genssa", "ssa-prog", buf.String())
}
+ if ssa.GenssaDump[f.Name] {
+ fi := f.DumpFileForPhase("genssa")
+ if fi != nil {
+
+ // inliningDiffers if any filename changes or if any line number except the innermost (index 0) changes.
+ inliningDiffers := func(a, b []src.Pos) bool {
+ if len(a) != len(b) {
+ return true
+ }
+ for i := range a {
+ if a[i].Filename() != b[i].Filename() {
+ return true
+ }
+ if i > 0 && a[i].Line() != b[i].Line() {
+ return true
+ }
+ }
+ return false
+ }
+
+ var allPosOld []src.Pos
+ var allPos []src.Pos
+
+ for p := pp.Text; p != nil; p = p.Link {
+ if p.Pos.IsKnown() {
+ allPos = p.AllPos(allPos)
+ if inliningDiffers(allPos, allPosOld) {
+ for i := len(allPos) - 1; i >= 0; i-- {
+ pos := allPos[i]
+ fmt.Fprintf(fi, "# %s:%d\n", pos.Filename(), pos.Line())
+ }
+ allPos, allPosOld = allPosOld, allPos // swap, not copy, so that they do not share slice storage.
+ }
+ }
+
+ var s string
+ if v, ok := progToValue[p]; ok {
+ s = v.String()
+ } else if b, ok := progToBlock[p]; ok {
+ s = b.String()
+ } else {
+ s = " " // most value and branch strings are 2-3 characters long
+ }
+ fmt.Fprintf(fi, " %-6s\t%.5d %s\t%s\n", s, p.Pc, ssa.StmtString(p.Pos), p.InstructionString())
+ }
+ fi.Close()
+ }
+ }
defframe(&s, e, f)
@@ -7446,6 +7552,14 @@ func (s *State) Call(v *ssa.Value) *obj.Prog {
return p
}
+// TailCall returns a new tail call instruction for the SSA value v.
+// It is like Call, but for a tail call.
+func (s *State) TailCall(v *ssa.Value) *obj.Prog {
+ p := s.Call(v)
+ p.As = obj.ARET
+ return p
+}
+
// PrepareCall prepares to emit a CALL instruction for v and does call-related bookkeeping.
// It must be called immediately before emitting the actual CALL instruction,
// since it emits PCDATA for the stack map at the call (calls are safe points).
@@ -7460,18 +7574,6 @@ func (s *State) PrepareCall(v *ssa.Value) {
call, ok := v.Aux.(*ssa.AuxCall)
- if ok && call.Fn == ir.Syms.Deferreturn {
- // Deferred calls will appear to be returning to
- // the CALL deferreturn(SB) that we are about to emit.
- // However, the stack trace code will show the line
- // of the instruction byte before the return PC.
- // To avoid that being an unrelated instruction,
- // insert an actual hardware NOP that will have the right line number.
- // This is different from obj.ANOP, which is a virtual no-op
- // that doesn't make it into the instruction stream.
- Arch.Ginsnopdefer(s.pp)
- }
-
if ok {
// Record call graph information for nowritebarrierrec
// analysis.
@@ -7542,10 +7644,6 @@ func (e *ssafn) Auto(pos src.XPos, t *types.Type) *ir.Name {
return typecheck.TempAt(pos, e.curfn, t) // Note: adds new auto to e.curfn.Func.Dcl list
}
-func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
- return reflectdata.ITabSym(it, offset)
-}
-
// SplitSlot returns a slot representing the data of parent starting at offset.
func (e *ssafn) SplitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot {
node := parent.N
@@ -7676,9 +7774,8 @@ func max8(a, b int8) int8 {
return b
}
-// deferstruct makes a runtime._defer structure, with additional space for
-// stksize bytes of args.
-func deferstruct(stksize int64) *types.Type {
+// deferstruct makes a runtime._defer structure.
+func deferstruct() *types.Type {
makefield := func(name string, typ *types.Type) *types.Field {
// Unlike the global makefield function, this one needs to set Pkg
// because these types might be compared (in SSA CSE sorting).
@@ -7686,13 +7783,9 @@ func deferstruct(stksize int64) *types.Type {
sym := &types.Sym{Name: name, Pkg: types.LocalPkg}
return types.NewField(src.NoXPos, sym, typ)
}
- argtype := types.NewArray(types.Types[types.TUINT8], stksize)
- argtype.Width = stksize
- argtype.Align = 1
// These fields must match the ones in runtime/runtime2.go:_defer and
- // cmd/compile/internal/gc/ssa.go:(*state).call.
+ // (*state).call above.
fields := []*types.Field{
- makefield("siz", types.Types[types.TUINT32]),
makefield("started", types.Types[types.TBOOL]),
makefield("heap", types.Types[types.TBOOL]),
makefield("openDefer", types.Types[types.TBOOL]),
@@ -7704,10 +7797,9 @@ func deferstruct(stksize int64) *types.Type {
makefield("fn", types.Types[types.TUINTPTR]),
makefield("_panic", types.Types[types.TUINTPTR]),
makefield("link", types.Types[types.TUINTPTR]),
- makefield("framepc", types.Types[types.TUINTPTR]),
- makefield("varp", types.Types[types.TUINTPTR]),
makefield("fd", types.Types[types.TUINTPTR]),
- makefield("args", argtype),
+ makefield("varp", types.Types[types.TUINTPTR]),
+ makefield("framepc", types.Types[types.TUINTPTR]),
}
// build struct holding the above fields
diff --git a/src/cmd/compile/internal/staticdata/data.go b/src/cmd/compile/internal/staticdata/data.go
index abb0bba646e0caacf183e436cc084784b5907250..57c15a34a0cd4c82bc1add58b3eb61dfeea8a6a9 100644
--- a/src/cmd/compile/internal/staticdata/data.go
+++ b/src/cmd/compile/internal/staticdata/data.go
@@ -92,6 +92,10 @@ func StringSym(pos src.XPos, s string) (data *obj.LSym) {
return symdata
}
+// maxFileSize is the maximum file size permitted by the linker
+// (see issue #9862).
+const maxFileSize = int64(2e9)
+
// fileStringSym returns a symbol for the contents and the size of file.
// If readonly is true, the symbol shares storage with any literal string
// or other file with the same content and is placed in a read-only section.
@@ -133,12 +137,12 @@ func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.
}
return sym, size, nil
}
- if size > 2e9 {
+ if size > maxFileSize {
// ggloblsym takes an int32,
// and probably the rest of the toolchain
// can't handle such big symbols either.
// See golang.org/issue/9862.
- return nil, 0, fmt.Errorf("file too large")
+ return nil, 0, fmt.Errorf("file too large (%d bytes > %d bytes)", size, maxFileSize)
}
// File is too big to read and keep in memory.
@@ -275,7 +279,7 @@ func NeedFuncSym(fn *ir.Func) {
// entry points, so it doesn't make sense to create a
// funcsym for other ABIs.
//
- // (If we're using ABI aliases, it doesn't matter.)
+ // (If we're not using ABI wrappers, it doesn't matter.)
base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI)
}
if ir.IsBlank(fn.Nname) {
diff --git a/src/cmd/compile/internal/staticdata/embed.go b/src/cmd/compile/internal/staticdata/embed.go
index 8936c4f5b44e44c7f03edf81a137e49eb68ac564..627c98ba447f004b1c78394a291cab1f40467103 100644
--- a/src/cmd/compile/internal/staticdata/embed.go
+++ b/src/cmd/compile/internal/staticdata/embed.go
@@ -73,7 +73,7 @@ func embedKind(typ *types.Type) int {
if typ.Kind() == types.TSTRING {
return embedString
}
- if typ.Sym() == nil && typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
+ if typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 {
return embedBytes
}
return embedUnknown
@@ -108,13 +108,6 @@ func WriteEmbed(v *ir.Name) {
// TODO(mdempsky): User errors should be reported by the frontend.
commentPos := (*v.Embed)[0].Pos
- if !types.AllowsGoVersion(types.LocalPkg, 1, 16) {
- prevPos := base.Pos
- base.Pos = commentPos
- base.ErrorfVers("go1.16", "go:embed")
- base.Pos = prevPos
- return
- }
if base.Flag.Cfg.Embed.Patterns == nil {
base.ErrorfAt(commentPos, "invalid go:embed: build system did not supply embed configuration")
return
diff --git a/src/cmd/compile/internal/staticinit/sched.go b/src/cmd/compile/internal/staticinit/sched.go
index 0c97b6de747660541c604beb3c7f4046f8dae10a..636199de47642a3bae215ae0897769a205fd545c 100644
--- a/src/cmd/compile/internal/staticinit/sched.go
+++ b/src/cmd/compile/internal/staticinit/sched.go
@@ -133,7 +133,7 @@ func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Ty
if ir.IsZero(r) {
return true
}
- staticdata.InitConst(l, loff, r, int(typ.Width))
+ staticdata.InitConst(l, loff, r, int(typ.Size()))
return true
case ir.OADDR:
@@ -165,7 +165,7 @@ func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Ty
e := &p.E[i]
typ := e.Expr.Type()
if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
- staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Width))
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size()))
continue
}
x := e.Expr
@@ -229,7 +229,7 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty
if ir.IsZero(r) {
return true
}
- staticdata.InitConst(l, loff, r, int(typ.Width))
+ staticdata.InitConst(l, loff, r, int(typ.Size()))
return true
case ir.OADDR:
@@ -286,7 +286,7 @@ func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Ty
for i := range p.E {
e := &p.E[i]
if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
- staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Width))
+ staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
continue
}
ir.SetPos(e.Expr)
@@ -392,7 +392,7 @@ func (s *Schedule) initplan(n ir.Node) {
}
a = kv.Value
}
- s.addvalue(p, k*n.Type().Elem().Width, a)
+ s.addvalue(p, k*n.Type().Elem().Size(), a)
k++
}
@@ -403,10 +403,10 @@ func (s *Schedule) initplan(n ir.Node) {
base.Fatalf("initplan structlit")
}
a := a.(*ir.StructKeyExpr)
- if a.Field.IsBlank() {
+ if a.Sym().IsBlank() {
continue
}
- s.addvalue(p, a.Offset, a.Value)
+ s.addvalue(p, a.Field.Offset, a.Value)
}
case ir.OMAPLIT:
@@ -499,10 +499,10 @@ func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
}
// Check for overflow.
- if n.Type().Width != 0 && types.MaxWidth/n.Type().Width <= int64(l) {
+ if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) {
break
}
- offset += int64(l) * n.Type().Width
+ offset += int64(l) * n.Type().Size()
return name, offset, true
}
diff --git a/src/cmd/compile/internal/syntax/dumper_test.go b/src/cmd/compile/internal/syntax/dumper_test.go
index 22680dce786c52214dce93b05b579cecc8f89b51..033283a3528784ce6dde2cfc13ebf7a57b08d9e5 100644
--- a/src/cmd/compile/internal/syntax/dumper_test.go
+++ b/src/cmd/compile/internal/syntax/dumper_test.go
@@ -13,11 +13,7 @@ func TestDump(t *testing.T) {
t.Skip("skipping test in short mode")
}
- // provide a no-op error handler so parsing doesn't stop after first error
- ast, err := ParseFile(*src_, func(error) {}, nil, CheckBranches)
- if err != nil {
- t.Error(err)
- }
+ ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, CheckBranches|AllowGenerics)
if ast != nil {
Fdump(testOut(), ast)
diff --git a/src/cmd/compile/internal/syntax/error_test.go b/src/cmd/compile/internal/syntax/error_test.go
index e4bedf54fdc04fec00253a41a00640c5fdafc7c8..d87e8eaee3213e8b31f6f19c1d8e61a4a306e1ab 100644
--- a/src/cmd/compile/internal/syntax/error_test.go
+++ b/src/cmd/compile/internal/syntax/error_test.go
@@ -154,11 +154,11 @@ func testSyntaxErrors(t *testing.T, filename string) {
if found {
rx, err := regexp.Compile(pattern)
if err != nil {
- t.Errorf("%s: %v", pos, err)
+ t.Errorf("%s:%s: %v", filename, pos, err)
return
}
if match := rx.MatchString(e.Msg); !match {
- t.Errorf("%s: %q does not match %q", pos, e.Msg, pattern)
+ t.Errorf("%s:%s: %q does not match %q", filename, pos, e.Msg, pattern)
return
}
// we have a match - eliminate this error
diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go
index fb9786daa325c580d5402603e9241ae67ab36ddc..2f9b43edef98ad42edd4ad01e676fff5d83c26e0 100644
--- a/src/cmd/compile/internal/syntax/nodes.go
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -275,14 +275,14 @@ type (
// Name Type
// Type
Field struct {
- Name *Name // nil means anonymous field/parameter (structs/parameters), or embedded interface (interfaces)
+ Name *Name // nil means anonymous field/parameter (structs/parameters), or embedded element (interfaces)
Type Expr // field names declared in a list share the same Type (identical pointers)
node
}
// interface { MethodList[0]; MethodList[1]; ... }
InterfaceType struct {
- MethodList []*Field // a field named "type" means a type constraint
+ MethodList []*Field
expr
}
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index e7b8840b337e679bb11e32ef844c1bf32764412a..770175fe54f8d2b5aee7071e924d37cea019a2df 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -87,6 +87,8 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm
p.indent = nil
}
+func (p *parser) allowGenerics() bool { return p.mode&AllowGenerics != 0 }
+
// takePragma returns the current parsed pragmas
// and clears them from the parser state.
func (p *parser) takePragma() Pragma {
@@ -146,11 +148,13 @@ func (p *parser) updateBase(pos Pos, tline, tcol uint, text string) {
// If we have a column (//line filename:line:col form),
// an empty filename means to use the previous filename.
filename := text[:i-1] // lop off ":line"
+ trimmed := false
if filename == "" && ok2 {
filename = p.base.Filename()
+ trimmed = p.base.Trimmed()
}
- p.base = NewLineBase(pos, filename, line, col)
+ p.base = NewLineBase(pos, filename, trimmed, line, col)
}
func commentText(s string) string {
@@ -274,7 +278,9 @@ func (p *parser) syntaxErrorAt(pos Pos, msg string) {
}
// tokstring returns the English word for selected punctuation tokens
-// for more readable error messages.
+// for more readable error messages. Use tokstring (not tok.String())
+// for user-facing (error) messages; use tok.String() for debugging
+// output.
func tokstring(tok token) string {
switch tok {
case _Comma:
@@ -580,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.mode&AllowGenerics != 0 && 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("internal error: nil expression")
- }
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 {
@@ -683,15 +708,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
}
f.Name = p.name()
- if p.mode&AllowGenerics != 0 && 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()
}
@@ -723,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()
@@ -738,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
@@ -833,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'.
@@ -847,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
@@ -917,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++
@@ -963,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 {
@@ -1049,7 +1070,16 @@ loop:
}
// x[i:...
- p.want(_Colon)
+ // For better error message, don't simply use p.want(_Colon) here (issue #47704).
+ if !p.got(_Colon) {
+ if p.mode&AllowGenerics == 0 {
+ p.syntaxError("expecting : or ]")
+ p.advance(_Colon, _Rbrack)
+ } else {
+ p.syntaxError("expecting comma, : or ]")
+ p.advance(_Comma, _Colon, _Rbrack)
+ }
+ }
p.xnest++
t := new(SliceExpr)
t.pos = pos
@@ -1100,7 +1130,7 @@ loop:
complit_ok = true
}
case *IndexExpr:
- if p.xnest >= 0 {
+ if p.xnest >= 0 && !isValue(t) {
// x is possibly a composite literal type
complit_ok = true
}
@@ -1127,6 +1157,21 @@ loop:
return x
}
+// isValue reports whether x syntactically must be a value (and not a type) expression.
+func isValue(x Expr) bool {
+ switch x := x.(type) {
+ case *BasicLit, *CompositeLit, *FuncLit, *SliceExpr, *AssertExpr, *TypeSwitchGuard, *CallExpr:
+ return true
+ case *Operation:
+ return x.Op != Mul || x.Y != nil // *T may be a type
+ case *ParenExpr:
+ return isValue(x.X)
+ case *IndexExpr:
+ return isValue(x.X) || isValue(x.Index)
+ }
+ return false
+}
+
// Element = Expression | LiteralValue .
func (p *parser) bare_complitexpr() Expr {
if trace {
@@ -1231,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
@@ -1304,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.
@@ -1397,7 +1459,7 @@ func (p *parser) interfaceType() *InterfaceType {
switch p.tok {
case _Name:
f := p.methodDecl()
- if f.Name == nil && p.mode&AllowGenerics != 0 {
+ if f.Name == nil && p.allowGenerics() {
f = p.embeddedElem(f)
}
typ.MethodList = append(typ.MethodList, f)
@@ -1415,39 +1477,27 @@ func (p *parser) interfaceType() *InterfaceType {
return false
case _Operator:
- if p.op == Tilde && p.mode&AllowGenerics != 0 {
+ if p.op == Tilde && p.allowGenerics() {
typ.MethodList = append(typ.MethodList, p.embeddedElem(nil))
return false
}
- case _Type:
- // TODO(gri) remove TypeList syntax if we accept #45346
- if p.mode&AllowGenerics != 0 {
- type_ := NewName(p.pos(), "type") // cannot have a method named "type"
- p.next()
- if p.tok != _Semi && p.tok != _Rbrace {
+ default:
+ if p.allowGenerics() {
+ pos := p.pos()
+ if t := p.typeOrNil(); t != nil {
f := new(Field)
- f.pos = p.pos()
- f.Name = type_
- f.Type = p.type_()
- typ.MethodList = append(typ.MethodList, f)
- for p.got(_Comma) {
- f := new(Field)
- f.pos = p.pos()
- f.Name = type_
- f.Type = p.type_()
- typ.MethodList = append(typ.MethodList, f)
- }
- } else {
- p.syntaxError("expecting type")
+ f.pos = pos
+ f.Type = t
+ typ.MethodList = append(typ.MethodList, p.embeddedElem(f))
+ return false
}
- return false
}
}
- if p.mode&AllowGenerics != 0 {
- p.syntaxError("expecting method, type list, or embedded element")
- p.advance(_Semi, _Rbrace, _Type) // TODO(gri) remove _Type if we don't accept it anymore
+ if p.allowGenerics() {
+ p.syntaxError("expecting method or embedded element")
+ p.advance(_Semi, _Rbrace)
return false
}
@@ -1525,7 +1575,7 @@ func (p *parser) fieldDecl(styp *StructType) {
// Careful dance: We don't know if we have an embedded instantiated
// type T[P1, P2, ...] or a field T of array/slice type [P]E or []E.
- if p.mode&AllowGenerics != 0 && len(names) == 1 && p.tok == _Lbrack {
+ if p.allowGenerics() && len(names) == 1 && p.tok == _Lbrack {
typ = p.arrayOrTArgs()
if typ, ok := typ.(*IndexExpr); ok {
// embedded type T[P1, P2, ...]
@@ -1656,14 +1706,16 @@ 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.mode&AllowGenerics != 0 {
+ if p.allowGenerics() {
// Careful dance: We don't know if we have a generic method m[T C](x T)
// or an embedded instantiated type T[P1, P2] (we accept generic methods
// for generality and robustness of parsing).
@@ -1680,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
@@ -1697,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
}
@@ -1708,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.
@@ -1796,39 +1848,70 @@ func (p *parser) embeddedTerm() Expr {
}
// ParameterDecl = [ IdentifierList ] [ "..." ] Type .
-func (p *parser) paramDeclOrNil(name *Name) *Field {
+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 := follow == _Rbrack
+
+ pos := p.pos()
+ if name != nil {
+ pos = name.pos
+ } else if typeSetsOk && p.tok == _Operator && p.op == Tilde {
+ // "~" ...
+ return p.embeddedElem(nil)
}
f := new(Field)
- f.pos = p.pos()
+ f.pos = pos
if p.tok == _Name || name != nil {
+ // name
if name == nil {
name = p.name()
}
- if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
+ if p.allowGenerics() && p.tok == _Lbrack {
+ // name "[" ...
f.Type = p.arrayOrTArgs()
if typ, ok := f.Type.(*IndexExpr); ok {
+ // name "[" ... "]"
typ.X = name
} else {
+ // name "[" n "]" E
f.Name = name
}
+ if typeSetsOk && p.tok == _Operator && p.op == Or {
+ // name "[" ... "]" "|" ...
+ // name "[" n "]" E "|" ...
+ f = p.embeddedElem(f)
+ }
return f
}
if p.tok == _Dot {
- // name_or_type
+ // name "." ...
f.Type = p.qualifiedName(name)
+ if typeSetsOk && p.tok == _Operator && p.op == Or {
+ // name "." name "|" ...
+ f = p.embeddedElem(f)
+ }
return f
}
+ if typeSetsOk && p.tok == _Operator && p.op == Or {
+ // name "|" ...
+ f.Type = name
+ return p.embeddedElem(f)
+ }
+
f.Name = name
}
if p.tok == _DotDotDot {
+ // [name] "..." ...
t := new(DotsType)
t.pos = p.pos()
p.next()
@@ -1841,13 +1924,23 @@ func (p *parser) paramDeclOrNil(name *Name) *Field {
return f
}
+ if typeSetsOk && p.tok == _Operator && p.op == Tilde {
+ // [name] "~" ...
+ f.Type = p.embeddedElem(nil).Type
+ return f
+ }
+
f.Type = p.typeOrNil()
+ if typeSetsOk && p.tok == _Operator && p.op == Or && f.Type != nil {
+ // [name] type "|"
+ f = p.embeddedElem(f)
+ }
if f.Name != nil || f.Type != nil {
return f
}
- p.syntaxError("expecting )")
- p.advance(_Comma, _Rparen)
+ p.syntaxError("expecting " + tokstring(follow))
+ p.advance(_Comma, follow)
return nil
}
@@ -1861,9 +1954,10 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
defer p.trace("paramList")()
}
- var named int // number of parameters that have an explicit name and type/bound
- p.list(_Comma, close, func() bool {
- par := p.paramDeclOrNil(name)
+ var named int // number of parameters that have an explicit name and type
+ var typed int // number of parameters that have an explicit type
+ end := p.list(_Comma, close, func() bool {
+ par := p.paramDeclOrNil(name, close)
name = nil // 1st name was consumed if present
if par != nil {
if debug && par.Name == nil && par.Type == nil {
@@ -1872,6 +1966,9 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
if par.Name != nil && par.Type != nil {
named++
}
+ if par.Type != nil {
+ typed++
+ }
list = append(list, par)
}
return false
@@ -1882,7 +1979,7 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
}
// distribute parameter types (len(list) > 0)
- if named == 0 {
+ if named == 0 && !requireNames {
// all unnamed => found names are named types
for _, par := range list {
if typ := par.Name; typ != nil {
@@ -1890,18 +1987,16 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
par.Name = nil
}
}
- if requireNames {
- p.syntaxErrorAt(list[0].Type.Pos(), "type parameters must be named")
- }
} else if named != len(list) {
// some named => all must have names and types
- var pos Pos // left-most error position (or unknown)
- var typ Expr
+ var pos Pos // left-most error position (or unknown)
+ var typ Expr // current type (from right to left)
for i := len(list) - 1; i >= 0; i-- {
- if par := list[i]; par.Type != nil {
+ par := list[i]
+ if par.Type != nil {
typ = par.Type
if par.Name == nil {
- pos = typ.Pos()
+ pos = StartPos(typ)
par.Name = NewName(pos, "_")
}
} else if typ != nil {
@@ -1917,7 +2012,12 @@ func (p *parser) paramList(name *Name, close token, requireNames bool) (list []*
if pos.IsKnown() {
var msg string
if requireNames {
- msg = "type parameters must be named"
+ if named == typed {
+ pos = end // position error at closing ]
+ msg = "missing type constraint"
+ } else {
+ msg = "type parameters must be named"
+ }
} else {
msg = "mixed named and unnamed parameters"
}
@@ -2157,7 +2257,7 @@ func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleS
if p.tok != _Semi {
// accept potential varDecl but complain
if p.got(_Var) {
- p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", keyword.String()))
+ p.syntaxError(fmt.Sprintf("var declaration not allowed in %s initializer", tokstring(keyword)))
}
init = p.simpleStmt(nil, keyword)
// If we have a range clause, we are done (can only happen for keyword == _For).
@@ -2598,7 +2698,7 @@ func (p *parser) qualifiedName(name *Name) Expr {
x = s
}
- if p.mode&AllowGenerics != 0 && p.tok == _Lbrack {
+ if p.allowGenerics() && p.tok == _Lbrack {
x = p.typeInstance(x)
}
diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go
index 340ca6bb6f637b05d5756a44f09a371eec69819f..68f3c376c929216693493d99894f0273c8f86e67 100644
--- a/src/cmd/compile/internal/syntax/parser_test.go
+++ b/src/cmd/compile/internal/syntax/parser_test.go
@@ -51,10 +51,7 @@ func TestParseGo2(t *testing.T) {
}
}
-func TestStdLib(t *testing.T) { testStdLib(t, 0) }
-func TestStdLibGeneric(t *testing.T) { testStdLib(t, AllowGenerics) }
-
-func testStdLib(t *testing.T, mode Mode) {
+func TestStdLib(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode")
}
@@ -93,7 +90,7 @@ func testStdLib(t *testing.T, mode Mode) {
if debug {
fmt.Printf("parsing %s\n", filename)
}
- ast, err := ParseFile(filename, nil, nil, mode)
+ ast, err := ParseFile(filename, nil, nil, AllowGenerics)
if err != nil {
t.Error(err)
return
diff --git a/src/cmd/compile/internal/syntax/pos.go b/src/cmd/compile/internal/syntax/pos.go
index baebcc995c759bbcb4719600e658b0a4920f881a..1494c0989fb11a1258649f6f0e45c39f7e147747 100644
--- a/src/cmd/compile/internal/syntax/pos.go
+++ b/src/cmd/compile/internal/syntax/pos.go
@@ -133,13 +133,19 @@ type PosBase struct {
pos Pos
filename string
line, col uint32
+ trimmed bool // whether -trimpath has been applied
}
// NewFileBase returns a new PosBase for the given filename.
// A file PosBase's position is relative to itself, with the
// position being filename:1:1.
func NewFileBase(filename string) *PosBase {
- base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase}
+ return NewTrimmedFileBase(filename, false)
+}
+
+// NewTrimmedFileBase is like NewFileBase, but allows specifying Trimmed.
+func NewTrimmedFileBase(filename string, trimmed bool) *PosBase {
+ base := &PosBase{MakePos(nil, linebase, colbase), filename, linebase, colbase, trimmed}
base.pos.base = base
return base
}
@@ -149,8 +155,8 @@ func NewFileBase(filename string) *PosBase {
// the comment containing the line directive. For a directive in a line comment,
// that position is the beginning of the next line (i.e., the newline character
// belongs to the line comment).
-func NewLineBase(pos Pos, filename string, line, col uint) *PosBase {
- return &PosBase{pos, filename, sat32(line), sat32(col)}
+func NewLineBase(pos Pos, filename string, trimmed bool, line, col uint) *PosBase {
+ return &PosBase{pos, filename, sat32(line), sat32(col), trimmed}
}
func (base *PosBase) IsFileBase() bool {
@@ -188,6 +194,13 @@ func (base *PosBase) Col() uint {
return uint(base.col)
}
+func (base *PosBase) Trimmed() bool {
+ if base == nil {
+ return false
+ }
+ return base.trimmed
+}
+
func sat32(x uint) uint32 {
if x > PosMax {
return PosMax
diff --git a/src/cmd/compile/internal/syntax/positions.go b/src/cmd/compile/internal/syntax/positions.go
index b00f86c67cdab32322449056a2caf9c7e7aa7a50..93596559a02328a3a23296c15ffa2983630a20a1 100644
--- a/src/cmd/compile/internal/syntax/positions.go
+++ b/src/cmd/compile/internal/syntax/positions.go
@@ -12,7 +12,7 @@ func StartPos(n Node) Pos {
for m := n; ; {
switch n := m.(type) {
case nil:
- panic("internal error: nil")
+ panic("nil node")
// packages
case *File:
@@ -124,7 +124,7 @@ func EndPos(n Node) Pos {
for m := n; ; {
switch n := m.(type) {
case nil:
- panic("internal error: nil")
+ panic("nil node")
// packages
case *File:
diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go
index e557f5d9247b521a67984f5b60d8963174b45a1d..c8d31799afcb4e479f31e6a07b004b8de028d5ae 100644
--- a/src/cmd/compile/internal/syntax/printer.go
+++ b/src/cmd/compile/internal/syntax/printer.go
@@ -494,39 +494,16 @@ func (p *printer) printRawNode(n Node) {
p.printSignature(n)
case *InterfaceType:
- // separate type list and method list
- var types []Expr
- var methods []*Field
- for _, f := range n.MethodList {
- if f.Name != nil && f.Name.Value == "type" {
- types = append(types, f.Type)
- } else {
- // method or embedded interface
- methods = append(methods, f)
- }
- }
-
- multiLine := len(n.MethodList) > 0 && p.linebreaks
p.print(_Interface)
- if multiLine {
+ if p.linebreaks && len(n.MethodList) > 1 {
p.print(blank)
- }
- p.print(_Lbrace)
- if multiLine {
+ p.print(_Lbrace)
p.print(newline, indent)
- }
- if len(types) > 0 {
- p.print(_Type, blank)
- p.printExprList(types)
- if len(methods) > 0 {
- p.print(_Semi, blank)
- }
- }
- if len(methods) > 0 {
- p.printMethodList(methods)
- }
- if multiLine {
+ p.printMethodList(n.MethodList)
p.print(outdent, newline)
+ } else {
+ p.print(_Lbrace)
+ p.printMethodList(n.MethodList)
}
p.print(_Rbrace)
diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go
index ec4b1de573f6bec08883996037bccf320cb210ad..604f1fc1ca71a296ed449126a4e685a47145aeb4 100644
--- a/src/cmd/compile/internal/syntax/printer_test.go
+++ b/src/cmd/compile/internal/syntax/printer_test.go
@@ -18,11 +18,7 @@ func TestPrint(t *testing.T) {
t.Skip("skipping test in short mode")
}
- // provide a no-op error handler so parsing doesn't stop after first error
- ast, err := ParseFile(*src_, func(error) {}, nil, 0)
- if err != nil {
- t.Error(err)
- }
+ ast, _ := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
if ast != nil {
Fprint(testOut(), ast, LineForm)
@@ -64,18 +60,22 @@ var stringTests = []string{
// generic type declarations
"package p; type _[T any] struct{}",
"package p; type _[A, B, C interface{m()}] struct{}",
- "package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{type int}] struct{}",
+ "package p; type _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}] struct{}",
// generic function declarations
"package p; func _[T any]()",
"package p; func _[A, B, C interface{m()}]()",
- "package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{type int}]()",
+ "package p; func _[T any, A, B, C interface{m()}, X, Y, Z interface{~int}]()",
// methods with generic receiver types
"package p; func (R[T]) _()",
"package p; func (*R[A, B, C]) _()",
"package p; func (_ *R[A, B, C]) _()",
+ // type constraint literals with elided interfaces
+ "package p; func _[P ~int, Q int | string]() {}",
+ "package p; func _[P struct{f int}, Q *P]() {}",
+
// channels
"package p; type _ chan chan int",
"package p; type _ chan (<-chan int)",
@@ -140,10 +140,10 @@ var exprTests = [][2]string{
dup("func(int, float32) string"),
dup("interface{m()}"),
dup("interface{m() string; n(x int)}"),
- dup("interface{type int}"),
- dup("interface{type int, float64, string}"),
- dup("interface{type int; m()}"),
- dup("interface{type int, float64, string; m() string; n(x int)}"),
+ dup("interface{~int}"),
+ dup("interface{~int | ~float64 | ~string}"),
+ dup("interface{~int; m()}"),
+ dup("interface{~int | ~float64 | ~string; m() string; n(x int)}"),
dup("map[string]int"),
dup("chan E"),
dup("<-chan E"),
@@ -155,7 +155,7 @@ var exprTests = [][2]string{
dup("interface{~int}"),
dup("interface{int | string}"),
dup("interface{~int | ~string; float64; m()}"),
- dup("interface{type a, b, c; ~int | ~string; float64; m()}"),
+ dup("interface{~a | ~b | ~c; ~int | ~string; float64; m()}"),
dup("interface{~T[int, string] | string}"),
// non-type expressions
diff --git a/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2 b/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2
index 0d27603a5837a2747922d9c1e52802b840ca8c4e..822d0287e7490ada0681ce74a10de3ab0122c765 100644
--- a/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2
+++ b/src/cmd/compile/internal/syntax/testdata/go2/linalg.go2
@@ -9,10 +9,10 @@ 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 {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64,
- complex64, complex128
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ float32 | ~float64 |
+ complex64 | ~complex128
}
func DotProduct[T Numeric](s1, s2 []T) T {
@@ -42,14 +42,14 @@ func AbsDifference[T NumericAbs[T]](a, b T) T {
// OrderedNumeric is a type bound that matches numeric types that support the < operator.
type OrderedNumeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ float32 | ~float64
}
// Complex is a type bound that matches the two complex types, which do not have a < operator.
type Complex interface {
- type complex64, complex128
+ ~complex64 | ~complex128
}
// OrderedAbs is a helper type that defines an Abs method for
diff --git a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2 b/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2
index e5cfba061254165a62c45bc04a2ea055033785d1..42efb4252755c2400bf9b334f2b048b70fa3017f 100644
--- a/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2
+++ b/src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2
@@ -46,12 +46,12 @@ type _ struct{ T[int] }
// interfaces
type _ interface{
m()
- type int
+ ~int
}
type _ interface{
- type int, float, string
- type complex128
+ ~int | ~float | ~string
+ ~complex128
underlying(underlying underlying) underlying
}
diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2
index 6e2104a5150da8fead95d91dc7cdad447fc59f8d..76b8d5591fbf99cfa49264e51f13c67e6d154046 100644
--- a/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2
+++ b/src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2
@@ -148,56 +148,32 @@ func _[T any](r R2[T, int], p *R2[string, T]) {
p.pm()
}
-// An interface can (explicitly) declare at most one type list.
-type _ interface {
- m0()
- type int, string, bool
- type /* ERROR multiple type lists */ float32, float64
- m1()
- m2()
- type /* ERROR multiple type lists */ complex64, complex128
- type /* ERROR multiple type lists */ rune
-}
-
-// Interface type lists may contain each type at most once.
-// (If there are multiple lists, we assume the author intended
-// for them to be all in a single list, and we report the error
-// as well.)
-type _ interface {
- type int, int /* ERROR duplicate type int */
- type /* ERROR multiple type lists */ int /* ERROR duplicate type int */
-}
-
-type _ interface {
- type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int}
-}
-
-// Interface type lists can contain any type, incl. *Named types.
+// Interface type constraints 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{type MyInt}](x T) T {
+func add1[T interface{ ~MyInt }](x T) T {
return x + 1
}
type MyString string
-func double[T interface{type MyInt, MyString}](x T) T {
+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 type constraints leads to interfaces
+// with type constraints that are the intersection of the embedded
+// type constraints.
type E0 interface {
- type int, bool, string
+ ~int | ~bool | ~string
}
type E1 interface {
- type int, float64, string
+ ~int | ~float64 | ~string
}
type E2 interface {
- type float64
+ ~float64
}
type I0 interface {
@@ -246,7 +222,7 @@ var _ = f12[float64]
type I0_ interface {
E0
- type int
+ ~int
}
func f0_[T I0_]()
diff --git a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2 b/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2
index f78037f0f5d033c63236e8246644b2b401509713..111f7c10042389ce70c659529f95cc279b06edf3 100644
--- a/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2
+++ b/src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2
@@ -48,22 +48,22 @@ func swapswap[A, B any](a A, b B) (A, B) {
type F[A, B any] func(A, B) (B, A)
-func min[T interface{ type int }](x, y T) T {
+func min[T interface{ ~int }](x, y T) T {
if x < y {
return x
}
return y
}
-func _[T interface{type int, float32}](x, y T) bool { return x < y }
+func _[T interface{ ~int | ~float32 }](x, y T) bool { return x < y }
func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y }
-func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y }
+func _[T interface{ ~int | ~float32 | ~bool }](x, y T) bool { return x /* ERROR cannot compare */ < y }
func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y }
func _[T C2[T]](x, y T) bool { return x < y }
type C1[T any] interface{}
-type C2[T any] interface{ type int, float32 }
+type C2[T any] interface{ ~int | ~float32 }
func new[T any]() *T {
var x T
@@ -91,40 +91,40 @@ var _ = f3[int, rune, bool](1, struct{x rune}{}, nil)
// indexing
func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type string }] (x T, i int) { _ = x[i] }
-func _[T interface{ type []int }] (x T, i int) { _ = x[i] }
-func _[T interface{ type [10]int, *[20]int, map[string]int }] (x T, i int) { _ = x[i] }
-func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] }
-func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
-func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+func _[T interface{ ~string }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[10]int | ~*[20]int | ~map[string]int }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] }
+func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
+func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
// slicing
// TODO(gri) implement this
-func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] }
+func _[T interface{ ~string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] }
// len/cap built-ins
func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string }](x T) { _ = len(x) }
-func _[T interface{ type [10]int }](x T) { _ = len(x) }
-func _[T interface{ type []byte }](x T) { _ = len(x) }
-func _[T interface{ type map[int]int }](x T) { _ = len(x) }
-func _[T interface{ type chan int }](x T) { _ = len(x) }
-func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) }
+func _[T interface{ ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string }](x T) { _ = len(x) }
+func _[T interface{ ~[10]int }](x T) { _ = len(x) }
+func _[T interface{ ~[]byte }](x T) { _ = len(x) }
+func _[T interface{ ~map[int]int }](x T) { _ = len(x) }
+func _[T interface{ ~chan int }](x T) { _ = len(x) }
+func _[T interface{ ~string | ~[]byte | ~chan int }](x T) { _ = len(x) }
func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type [10]int }](x T) { _ = cap(x) }
-func _[T interface{ type []byte }](x T) { _ = cap(x) }
-func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
-func _[T interface{ type chan int }](x T) { _ = cap(x) }
-func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) }
+func _[T interface{ ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string | ~[]byte | ~int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~string }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~[10]int }](x T) { _ = cap(x) }
+func _[T interface{ ~[]byte }](x T) { _ = cap(x) }
+func _[T interface{ ~map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
+func _[T interface{ ~chan int }](x T) { _ = cap(x) }
+func _[T interface{ ~[]byte | ~chan int }](x T) { _ = cap(x) }
// range iteration
@@ -132,7 +132,7 @@ func _[T interface{}](x T) {
for range x /* ERROR cannot range */ {}
}
-func _[T interface{ type string, []string }](x T) {
+func _[T interface{ ~string | ~[]string }](x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i }
@@ -144,23 +144,23 @@ func _[T interface{ type string, []string }](x T) {
}
-func _[T interface{ type string, []rune, map[int]rune }](x T) {
+func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) {
for _, e := range x { _ = e }
for i, e := range x { _ = i; _ = e }
}
-func _[T interface{ type string, []rune, map[string]rune }](x T) {
+func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) {
for _, e := range x { _ = e }
for i, e := range x /* ERROR must have the same key type */ { _ = e }
}
-func _[T interface{ type string, chan int }](x T) {
+func _[T interface{ ~string | ~chan int }](x T) {
for range x {}
for i := range x { _ = i }
for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
}
-func _[T interface{ type string, chan<-int }](x T) {
+func _[T interface{ ~string | ~chan<-int }](x T) {
for i := range x /* ERROR send-only channel */ { _ = i }
}
@@ -388,7 +388,7 @@ func _[T any](x T) {
}
}
-func _[T interface{type int}](x T) {
+func _[T interface{ ~int }](x T) {
_ = x /* ERROR not an interface */ .(int)
switch x /* ERROR not an interface */ .(type) {
}
diff --git a/src/cmd/compile/internal/syntax/testdata/interface.go2 b/src/cmd/compile/internal/syntax/testdata/interface.go2
index a817327a43f4c289e55b541880974981c1b54278..dbc41879896920495d064f92cb1c16cc0c8d71fb 100644
--- a/src/cmd/compile/internal/syntax/testdata/interface.go2
+++ b/src/cmd/compile/internal/syntax/testdata/interface.go2
@@ -4,16 +4,11 @@
// This file contains test cases for interfaces containing
// constraint elements.
-//
-// For now, we accept both ordinary type lists and the
-// more complex constraint elements.
package p
type _ interface {
m()
- type int
- type int, string
E
}
@@ -25,12 +20,55 @@ type _ interface {
~int | ~string
}
-
type _ interface {
m()
~int
T[int, string] | string
int | ~T[string, struct{}]
~int | ~string
- type bool, int, float64
+}
+
+type _ interface {
+ int
+ []byte
+ [10]int
+ struct{}
+ *int
+ func()
+ interface{}
+ map[string]int
+ chan T
+ chan<- T
+ <-chan T
+ T[int]
+}
+
+type _ interface {
+ int | string
+ []byte | string
+ [10]int | string
+ struct{} | string
+ *int | string
+ func() | string
+ interface{} | string
+ map[string]int | string
+ chan T | string
+ chan<- T | string
+ <-chan T | string
+ T[int] | string
+}
+
+type _ interface {
+ ~int | string
+ ~[]byte | string
+ ~[10]int | string
+ ~struct{} | string
+ ~*int | string
+ ~func() | string
+ ~interface{} | string
+ ~map[string]int | string
+ ~chan T | string
+ ~chan<- T | string
+ ~<-chan T | string
+ ~T[int] | string
}
diff --git a/src/cmd/compile/internal/syntax/testdata/issue43527.go2 b/src/cmd/compile/internal/syntax/testdata/issue43527.go2
new file mode 100644
index 0000000000000000000000000000000000000000..dd2c9b1272f79d28c21bcf3b38eb72a8940937af
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue43527.go2
@@ -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 p
+
+type (
+ // 0 and 1-element []-lists are syntactically valid
+ _[A, B /* ERROR missing type constraint */ ] int
+ _[A, /* ERROR type parameters must be named */ interface{}] int
+ _[A, B, C /* ERROR missing type constraint */ ] int
+ _[A B, C /* ERROR missing type constraint */ ] int
+ _[A B, /* ERROR type parameters must be named */ interface{}] int
+ _[A B, /* ERROR type parameters must be named */ interface{}, C D] int
+ _[A B, /* ERROR type parameters must be named */ interface{}, C, D] int
+ _[A B, /* ERROR type parameters must be named */ interface{}, C, interface{}] int
+ _[A B, C interface{}, D, /* ERROR type parameters must be named */ interface{}] int
+)
+
+// function type parameters use the same parsing routine - just have a couple of tests
+
+func _[A, B /* ERROR missing type constraint */ ]() {}
+func _[A, /* ERROR type parameters must be named */ interface{}]() {}
diff --git a/src/cmd/compile/internal/syntax/testdata/issue46558.src b/src/cmd/compile/internal/syntax/testdata/issue46558.src
new file mode 100644
index 0000000000000000000000000000000000000000..a22b6008258464503dd41b037a3094dccec10ebb
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue46558.src
@@ -0,0 +1,14 @@
+// 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) {
+ switch s[0] {
+ case 'a':
+ case s[2] { // ERROR unexpected {
+ case 'b':
+ }
+ }
+} // ERROR non-declaration statement
diff --git a/src/cmd/compile/internal/syntax/testdata/issue47704.go2 b/src/cmd/compile/internal/syntax/testdata/issue47704.go2
new file mode 100644
index 0000000000000000000000000000000000000000..4e65857f3b191c8f94523f39ed8bc999d421258d
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue47704.go2
@@ -0,0 +1,18 @@
+// 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
+
+// error messages for parser in generic mode
+func _() {
+ _ = m[] // ERROR expecting operand
+ _ = m[x,]
+ _ = m[x /* ERROR unexpected a */ a b c d]
+}
+
+// test case from the issue
+func f(m map[int]int) int {
+ return m[0 // ERROR expecting comma, \: or \]
+ ]
+}
diff --git a/src/cmd/compile/internal/syntax/testdata/issue47704.src b/src/cmd/compile/internal/syntax/testdata/issue47704.src
new file mode 100644
index 0000000000000000000000000000000000000000..0156af7d8d1b040963c3a707a17f44a636c77f01
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue47704.src
@@ -0,0 +1,18 @@
+// 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
+
+// error messages for parser in non-generic mode
+func _() {
+ _ = m[] // ERROR expecting operand
+ _ = m[x,] // ERROR unexpected comma, expecting \: or \]
+ _ = m[x /* ERROR unexpected a */ a b c d]
+}
+
+// test case from the issue
+func f(m map[int]int) int {
+ return m[0 // ERROR expecting \: or \]
+ ]
+}
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 0000000000000000000000000000000000000000..1e8f4b0ec646d4b7ad1ca77d09ed360190089232
--- /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/tparams.go2 b/src/cmd/compile/internal/syntax/testdata/tparams.go2
index 42031c32774f9419625b21b3ea14d005a2cf5449..80e155bfe05213df73abaa04ffea861e377cfd49 100644
--- a/src/cmd/compile/internal/syntax/testdata/tparams.go2
+++ b/src/cmd/compile/internal/syntax/testdata/tparams.go2
@@ -4,8 +4,8 @@
package p
-type t[ /* ERROR type parameters must be named */ a, b] struct{}
-type t[a t, b t, /* ERROR type parameters must be named */ c] struct{}
+type t[a, b /* ERROR missing type constraint */ ] struct{}
+type t[a t, b t, c /* ERROR missing type constraint */ ] struct{}
type t struct {
t [n]byte
t[a]
@@ -18,5 +18,7 @@ type t interface {
}
func f[ /* ERROR empty type parameter list */ ]()
-func f[ /* ERROR type parameters must be named */ a, b]()
-func f[a t, b t, /* ERROR type parameters must be named */ c]()
+func f[a, b /* ERROR missing type constraint */ ]()
+func f[a t, b t, c /* ERROR missing type constraint */ ]()
+
+func f[a b, /* ERROR expecting ] */ 0] ()
diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go2 b/src/cmd/compile/internal/syntax/testdata/typeset.go2
new file mode 100644
index 0000000000000000000000000000000000000000..19b74f28eacdb8e24ceea65735167ec1c5bd5369
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/typeset.go2
@@ -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.
+
+// This file contains test cases for typeset-only constraint elements.
+
+package p
+
+type (
+ _[_ t] t
+ _[_ ~t] t
+ _[_ t|t] t
+ _[_ ~t|t] t
+ _[_ t|~t] t
+ _[_ ~t|~t] t
+
+ _[_ t, _, _ t|t] t
+ _[_ t, _, _ ~t|t] t
+ _[_ t, _, _ t|~t] t
+ _[_ t, _, _ ~t|~t] t
+
+ _[_ t.t] t
+ _[_ ~t.t] t
+ _[_ t.t|t.t] t
+ _[_ ~t.t|t.t] t
+ _[_ t.t|~t.t] t
+ _[_ ~t.t|~t.t] t
+
+ _[_ t, _, _ t.t|t.t] t
+ _[_ t, _, _ ~t.t|t.t] t
+ _[_ t, _, _ t.t|~t.t] t
+ _[_ t, _, _ ~t.t|~t.t] t
+
+ _[_ struct{}] t
+ _[_ ~struct{}] t
+
+ _[_ struct{}|t] t
+ _[_ ~struct{}|t] t
+ _[_ struct{}|~t] t
+ _[_ ~struct{}|~t] t
+
+ _[_ t|struct{}] t
+ _[_ ~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
+// a type-checker will complain - but we don't allow ~ in the expr).
+type (
+ _[t] t
+ _[/* ERROR unexpected ~ */ ~t] t
+ _[t|t] t
+ _[/* ERROR unexpected ~ */ ~t|t] t
+ _[t| /* ERROR unexpected ~ */ ~t] t
+ _[/* ERROR unexpected ~ */ ~t|~t] t
+)
+
+type (
+ _[_ t, t /* ERROR missing type constraint */ ] t
+ _[_ ~t, t /* ERROR missing type constraint */ ] t
+ _[_ t, /* ERROR type parameters must be named */ ~t] t
+ _[_ ~t, /* ERROR type parameters must be named */ ~t] t
+
+ _[_ t|t, /* ERROR type parameters must be named */ t|t] t
+ _[_ ~t|t, /* ERROR type parameters must be named */ t|t] t
+ _[_ t|t, /* ERROR type parameters must be named */ ~t|t] t
+ _[_ ~t|t, /* ERROR type parameters must be named */ ~t|t] t
+)
diff --git a/src/cmd/compile/internal/syntax/walk.go b/src/cmd/compile/internal/syntax/walk.go
index c26e97a0d8f88771986be07a4a300ff98b993f4e..b02584420488442a3e284d5b611fa025001bd59e 100644
--- a/src/cmd/compile/internal/syntax/walk.go
+++ b/src/cmd/compile/internal/syntax/walk.go
@@ -8,31 +8,73 @@ package syntax
import "fmt"
-// Walk traverses a syntax in pre-order: It starts by calling f(root);
-// root must not be nil. If f returns false (== "continue"), Walk calls
+// Inspect traverses an AST in pre-order: It starts by calling
+// f(node); node must not be nil. If f returns true, Inspect invokes f
+// recursively for each of the non-nil children of node, followed by a
+// call of f(nil).
+//
+// See Walk for caveats about shared nodes.
+func Inspect(root Node, f func(Node) bool) {
+ Walk(root, inspector(f))
+}
+
+type inspector func(Node) bool
+
+func (v inspector) Visit(node Node) Visitor {
+ if v(node) {
+ return v
+ }
+ return nil
+}
+
+// Crawl traverses a syntax in pre-order: It starts by calling f(root);
+// root must not be nil. If f returns false (== "continue"), Crawl calls
// f recursively for each of the non-nil children of that node; if f
-// returns true (== "stop"), Walk does not traverse the respective node's
+// returns true (== "stop"), Crawl does not traverse the respective node's
// children.
+//
+// See Walk for caveats about shared nodes.
+//
+// Deprecated: Use Inspect instead.
+func Crawl(root Node, f func(Node) bool) {
+ Inspect(root, func(node Node) bool {
+ return node != nil && !f(node)
+ })
+}
+
+// Walk traverses an AST in pre-order: It starts by calling
+// v.Visit(node); node must not be nil. If the visitor w returned by
+// v.Visit(node) is not nil, Walk is invoked recursively with visitor
+// w for each of the non-nil children of node, followed by a call of
+// w.Visit(nil).
+//
// Some nodes may be shared among multiple parent nodes (e.g., types in
// field lists such as type T in "a, b, c T"). Such shared nodes are
// walked multiple times.
// TODO(gri) Revisit this design. It may make sense to walk those nodes
// only once. A place where this matters is types2.TestResolveIdents.
-func Walk(root Node, f func(Node) bool) {
- w := walker{f}
- w.node(root)
+func Walk(root Node, v Visitor) {
+ walker{v}.node(root)
+}
+
+// A Visitor's Visit method is invoked for each node encountered by Walk.
+// If the result visitor w is not nil, Walk visits each of the children
+// of node with the visitor w, followed by a call of w.Visit(nil).
+type Visitor interface {
+ Visit(node Node) (w Visitor)
}
type walker struct {
- f func(Node) bool
+ v Visitor
}
-func (w *walker) node(n Node) {
+func (w walker) node(n Node) {
if n == nil {
- panic("invalid syntax tree: nil node")
+ panic("nil node")
}
- if w.f(n) {
+ w.v = w.v.Visit(n)
+ if w.v == nil {
return
}
@@ -285,33 +327,35 @@ func (w *walker) node(n Node) {
default:
panic(fmt.Sprintf("internal error: unknown node type %T", n))
}
+
+ w.v.Visit(nil)
}
-func (w *walker) declList(list []Decl) {
+func (w walker) declList(list []Decl) {
for _, n := range list {
w.node(n)
}
}
-func (w *walker) exprList(list []Expr) {
+func (w walker) exprList(list []Expr) {
for _, n := range list {
w.node(n)
}
}
-func (w *walker) stmtList(list []Stmt) {
+func (w walker) stmtList(list []Stmt) {
for _, n := range list {
w.node(n)
}
}
-func (w *walker) nameList(list []*Name) {
+func (w walker) nameList(list []*Name) {
for _, n := range list {
w.node(n)
}
}
-func (w *walker) fieldList(list []*Field) {
+func (w walker) fieldList(list []*Field) {
for _, n := range list {
w.node(n)
}
diff --git a/src/cmd/compile/internal/test/abiutils_test.go b/src/cmd/compile/internal/test/abiutils_test.go
index b752c486126dc37acfb08bef3ee8205f5ab032e2..12b4a0c361360564e9d7bab8a45bbe6045577828 100644
--- a/src/cmd/compile/internal/test/abiutils_test.go
+++ b/src/cmd/compile/internal/test/abiutils_test.go
@@ -33,6 +33,8 @@ func TestMain(m *testing.M) {
base.Ctxt.DiagFunc = base.Errorf
base.Ctxt.DiagFlush = base.FlushErrors
base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
+ types.LocalPkg = types.NewPkg("", "local")
+ types.LocalPkg.Prefix = `""`
types.PtrSize = ssagen.Arch.LinkArch.PtrSize
types.RegSize = ssagen.Arch.LinkArch.RegSize
typecheck.InitUniverse()
@@ -245,7 +247,7 @@ func TestABIUtilsSliceString(t *testing.T) {
// p6 int64, p6 []intr32) (r1 string, r2 int64, r3 string, r4 []int32)
i32 := types.Types[types.TINT32]
sli32 := types.NewSlice(i32)
- str := types.New(types.TSTRING)
+ str := types.Types[types.TSTRING]
i8 := types.Types[types.TINT8]
i64 := types.Types[types.TINT64]
ft := mkFuncType(nil, []*types.Type{sli32, i8, sli32, i8, str, i8, i64, sli32},
@@ -309,9 +311,9 @@ func TestABIUtilsInterfaces(t *testing.T) {
ei := types.Types[types.TINTER] // interface{}
pei := types.NewPtr(ei) // *interface{}
fldt := mkFuncType(types.FakeRecvType(), []*types.Type{},
- []*types.Type{types.UntypedString})
- field := types.NewField(src.NoXPos, nil, fldt)
- nei := types.NewInterface(types.LocalPkg, []*types.Field{field})
+ []*types.Type{types.Types[types.TSTRING]})
+ field := types.NewField(src.NoXPos, typecheck.Lookup("F"), fldt)
+ 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})
@@ -322,12 +324,12 @@ func TestABIUtilsInterfaces(t *testing.T) {
IN 0: R{ I0 I1 I2 } spilloffset: 0 typ: struct { int16; int16; bool }
IN 1: R{ I3 I4 } spilloffset: 8 typ: interface {}
IN 2: R{ I5 I6 } spilloffset: 24 typ: interface {}
- IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { () untyped string }
+ IN 3: R{ I7 I8 } spilloffset: 40 typ: interface { F() string }
IN 4: R{ } offset: 0 typ: *interface {}
- IN 5: R{ } offset: 8 typ: interface { () untyped string }
+ IN 5: R{ } offset: 8 typ: interface { F() string }
IN 6: R{ } offset: 24 typ: int16
OUT 0: R{ I0 I1 } spilloffset: -1 typ: interface {}
- OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { () untyped string }
+ OUT 1: R{ I2 I3 } spilloffset: -1 typ: interface { F() string }
OUT 2: R{ I4 } spilloffset: -1 typ: *interface {}
offsetToSpillArea: 32 spillAreaSize: 56
`)
diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go
index 6f100033cf5b8b9666c458ce82b894db12d2a914..b10d37a17cfd0d242775b60ece9eaed8b30bfbb0 100644
--- a/src/cmd/compile/internal/test/inl_test.go
+++ b/src/cmd/compile/internal/test/inl_test.go
@@ -42,13 +42,11 @@ func TestIntendedInlining(t *testing.T) {
"bucketMask",
"bucketShift",
"chanbuf",
- "deferArgs",
- "deferclass",
"evacuated",
"fastlog2",
"fastrand",
"float64bits",
- "funcPC",
+ "funcspdelta",
"getArgInfoFast",
"getm",
"getMCache",
@@ -65,10 +63,10 @@ func TestIntendedInlining(t *testing.T) {
"subtract1",
"subtractb",
"tophash",
- "totaldefersize",
"(*bmap).keys",
"(*bmap).overflow",
"(*waitq).enqueue",
+ "funcInfo.entry",
// GC-related ones
"cgoInRange",
@@ -126,9 +124,14 @@ func TestIntendedInlining(t *testing.T) {
"FullRune",
"FullRuneInString",
"RuneLen",
+ "AppendRune",
"ValidRune",
},
"reflect": {
+ "Value.CanInt",
+ "Value.CanUint",
+ "Value.CanFloat",
+ "Value.CanComplex",
"Value.CanAddr",
"Value.CanSet",
"Value.CanInterface",
diff --git a/src/cmd/compile/internal/test/inst_test.go b/src/cmd/compile/internal/test/inst_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..951f6a05aa57df1a3daf55700f48eb760d47aa4f
--- /dev/null
+++ b/src/cmd/compile/internal/test/inst_test.go
@@ -0,0 +1,73 @@
+// 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 test
+
+import (
+ "internal/goexperiment"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "testing"
+)
+
+// TestInst tests that only one instantiation of Sort is created, even though generic
+// Sort is used for multiple pointer types across two packages.
+func TestInst(t *testing.T) {
+ if goexperiment.Unified {
+ t.Skip("unified currently does stenciling, not dictionaries")
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveGoRun(t)
+
+ var tmpdir string
+ var err error
+ tmpdir, err = ioutil.TempDir("", "TestDict")
+ if err != nil {
+ t.Fatalf("Failed to create temporary directory: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ // Build ptrsort.go, which uses package mysort.
+ var output []byte
+ filename := "ptrsort.go"
+ exename := "ptrsort"
+ outname := "ptrsort.out"
+ gotool := testenv.GoToolPath(t)
+ dest := filepath.Join(tmpdir, exename)
+ cmd := exec.Command(gotool, "build", "-o", dest, filepath.Join("testdata", filename))
+ if output, err = cmd.CombinedOutput(); err != nil {
+ t.Fatalf("Failed: %v:\nOutput: %s\n", err, output)
+ }
+
+ // Test that there is exactly one shape-based instantiation of Sort in
+ // the executable.
+ cmd = exec.Command(gotool, "tool", "nm", dest)
+ if output, err = cmd.CombinedOutput(); err != nil {
+ t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
+ }
+ // 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))
+ }
+
+ // Actually run the test and make sure output is correct.
+ cmd = exec.Command(gotool, "run", filepath.Join("testdata", filename))
+ if output, err = cmd.CombinedOutput(); err != nil {
+ t.Fatalf("Failed: %v:\nOut: %s\n", err, output)
+ }
+ out, err := ioutil.ReadFile(filepath.Join("testdata", outname))
+ if err != nil {
+ t.Fatalf("Could not find %s\n", outname)
+ }
+ if string(out) != string(output) {
+ t.Fatalf("Wanted output %v, got %v\n", string(out), string(output))
+ }
+}
diff --git a/src/cmd/compile/internal/test/ssa_test.go b/src/cmd/compile/internal/test/ssa_test.go
index 2f3e24c2d37ca0b6c9b6226e6611de369ff39cc9..af7d9626f95f9efb596614bf2c6061d5a502eed3 100644
--- a/src/cmd/compile/internal/test/ssa_test.go
+++ b/src/cmd/compile/internal/test/ssa_test.go
@@ -162,7 +162,7 @@ func TestCode(t *testing.T) {
}
flags := []string{""}
- if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" {
+ if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" || runtime.GOARCH == "386" {
flags = append(flags, ",softfloat")
}
for _, flag := range flags {
diff --git a/src/cmd/compile/internal/test/testdata/mysort/mysort.go b/src/cmd/compile/internal/test/testdata/mysort/mysort.go
new file mode 100644
index 0000000000000000000000000000000000000000..14852c868a79452d153f3dc6eff834d5d9a58841
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/mysort/mysort.go
@@ -0,0 +1,40 @@
+// 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.
+
+// Generic sort function, tested with two different pointer types.
+
+package mysort
+
+import (
+ "fmt"
+)
+
+type LessConstraint[T any] interface {
+ Less(T) bool
+}
+
+//go:noinline
+func Sort[T LessConstraint[T]](x []T) {
+ n := len(x)
+ for i := 1; i < n; i++ {
+ for j := i; j > 0 && x[j].Less(x[j-1]); j-- {
+ x[j], x[j-1] = x[j-1], x[j]
+ }
+ }
+}
+
+type MyInt struct {
+ Value int
+}
+
+func (a *MyInt) Less(b *MyInt) bool {
+ return a.Value < b.Value
+}
+
+//go:noinline
+func F() {
+ sl1 := []*MyInt{&MyInt{4}, &MyInt{3}, &MyInt{8}, &MyInt{7}}
+ Sort(sl1)
+ fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3])
+}
diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.go b/src/cmd/compile/internal/test/testdata/ptrsort.go
new file mode 100644
index 0000000000000000000000000000000000000000..6cc7ba4851bba39fef93918ed78aa76d2fb17397
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/ptrsort.go
@@ -0,0 +1,30 @@
+package main
+
+// Test generic sort function with two different pointer types in different packages,
+// make sure only one instantiation is created.
+
+import (
+ "fmt"
+
+ "./mysort"
+)
+
+type MyString struct {
+ string
+}
+
+func (a *MyString) Less(b *MyString) bool {
+ return a.string < b.string
+}
+
+func main() {
+ mysort.F()
+
+ sl1 := []*mysort.MyInt{{7}, {1}, {4}, {6}}
+ mysort.Sort(sl1)
+ fmt.Printf("%v %v %v %v\n", sl1[0], sl1[1], sl1[2], sl1[3])
+
+ sl2 := []*MyString{{"when"}, {"in"}, {"the"}, {"course"}, {"of"}}
+ mysort.Sort(sl2)
+ fmt.Printf("%v %v %v %v %v\n", sl2[0], sl2[1], sl2[2], sl2[3], sl2[4])
+}
diff --git a/src/cmd/compile/internal/test/testdata/ptrsort.out b/src/cmd/compile/internal/test/testdata/ptrsort.out
new file mode 100644
index 0000000000000000000000000000000000000000..41f1621d1a29b5977888b8b7d2572f23117a059b
--- /dev/null
+++ b/src/cmd/compile/internal/test/testdata/ptrsort.out
@@ -0,0 +1,3 @@
+&{3} &{4} &{7} &{8}
+&{1} &{4} &{6} &{7}
+&{course} &{in} &{of} &{the} &{when}
diff --git a/src/cmd/compile/internal/typebits/typebits.go b/src/cmd/compile/internal/typebits/typebits.go
index 1c1b077423dc973c20767f0b6d96280e07899e88..fddad6e7e84b8176fd3d2865433d53d0b95236b2 100644
--- a/src/cmd/compile/internal/typebits/typebits.go
+++ b/src/cmd/compile/internal/typebits/typebits.go
@@ -14,8 +14,8 @@ import (
// the first run and then simply copied into bv at the correct offset
// on future calls with the same type t.
func Set(t *types.Type, off int64, bv bitvec.BitVec) {
- if t.Align > 0 && off&int64(t.Align-1) != 0 {
- base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, t.Align, off)
+ if uint8(t.Alignment()) > 0 && off&int64(uint8(t.Alignment())-1) != 0 {
+ base.Fatalf("typebits.Set: invalid initial alignment: type %v has alignment %d, but offset is %v", t, uint8(t.Alignment()), off)
}
if !t.HasPointers() {
// Note: this case ensures that pointers to go:notinheap types
@@ -67,13 +67,13 @@ func Set(t *types.Type, off int64, bv bitvec.BitVec) {
case types.TARRAY:
elt := t.Elem()
- if elt.Width == 0 {
+ if elt.Size() == 0 {
// Short-circuit for #20739.
break
}
for i := int64(0); i < t.NumElem(); i++ {
Set(elt, off, bv)
- off += elt.Width
+ off += elt.Size()
}
case types.TSTRUCT:
diff --git a/src/cmd/compile/internal/typecheck/bexport.go b/src/cmd/compile/internal/typecheck/bexport.go
index 4a84bb13fa48ebfc63eb1dd36a5b7206af21a5c5..352f7a96ad3eac4ec468bfb6d4baf4e276a2920b 100644
--- a/src/cmd/compile/internal/typecheck/bexport.go
+++ b/src/cmd/compile/internal/typecheck/bexport.go
@@ -96,6 +96,12 @@ func predeclared() []*types.Type {
// any type, for builtin export data
types.Types[types.TANY],
+
+ // comparable
+ types.ComparableType,
+
+ // any
+ types.AnyType,
}
}
return predecl
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 833b17b4148ed5ab022e0b859084573227151ed3..67597cebb463025cc3db34e74b052eec973b83ca 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -71,137 +71,139 @@ var runtimeDecls = [...]struct {
{"slicecopy", funcTag, 54},
{"decoderune", funcTag, 55},
{"countrunes", funcTag, 56},
- {"convI2I", funcTag, 57},
- {"convT16", funcTag, 59},
- {"convT32", funcTag, 61},
- {"convT64", funcTag, 62},
- {"convTstring", funcTag, 63},
- {"convTslice", funcTag, 66},
- {"convT2E", funcTag, 67},
- {"convT2Enoptr", funcTag, 67},
- {"convT2I", funcTag, 67},
- {"convT2Inoptr", funcTag, 67},
- {"assertE2I", funcTag, 68},
- {"assertE2I2", funcTag, 57},
- {"assertI2I", funcTag, 68},
- {"assertI2I2", funcTag, 57},
- {"panicdottypeE", funcTag, 69},
- {"panicdottypeI", funcTag, 69},
- {"panicnildottype", funcTag, 70},
- {"ifaceeq", funcTag, 72},
- {"efaceeq", funcTag, 72},
- {"fastrand", funcTag, 73},
- {"makemap64", funcTag, 75},
- {"makemap", funcTag, 76},
- {"makemap_small", funcTag, 77},
- {"mapaccess1", funcTag, 78},
- {"mapaccess1_fast32", funcTag, 79},
- {"mapaccess1_fast64", funcTag, 80},
- {"mapaccess1_faststr", funcTag, 81},
- {"mapaccess1_fat", funcTag, 82},
- {"mapaccess2", funcTag, 83},
- {"mapaccess2_fast32", funcTag, 84},
- {"mapaccess2_fast64", funcTag, 85},
- {"mapaccess2_faststr", funcTag, 86},
- {"mapaccess2_fat", funcTag, 87},
- {"mapassign", funcTag, 78},
- {"mapassign_fast32", funcTag, 79},
- {"mapassign_fast32ptr", funcTag, 88},
- {"mapassign_fast64", funcTag, 80},
- {"mapassign_fast64ptr", funcTag, 88},
- {"mapassign_faststr", funcTag, 81},
- {"mapiterinit", funcTag, 89},
- {"mapdelete", funcTag, 89},
- {"mapdelete_fast32", funcTag, 90},
- {"mapdelete_fast64", funcTag, 91},
- {"mapdelete_faststr", funcTag, 92},
- {"mapiternext", funcTag, 93},
- {"mapclear", funcTag, 94},
- {"makechan64", funcTag, 96},
- {"makechan", funcTag, 97},
- {"chanrecv1", funcTag, 99},
- {"chanrecv2", funcTag, 100},
- {"chansend1", funcTag, 102},
+ {"convI2I", funcTag, 58},
+ {"convT", funcTag, 59},
+ {"convTnoptr", funcTag, 59},
+ {"convT16", funcTag, 61},
+ {"convT32", funcTag, 63},
+ {"convT64", funcTag, 64},
+ {"convTstring", funcTag, 65},
+ {"convTslice", funcTag, 68},
+ {"assertE2I", funcTag, 69},
+ {"assertE2I2", funcTag, 70},
+ {"assertI2I", funcTag, 69},
+ {"assertI2I2", funcTag, 70},
+ {"panicdottypeE", funcTag, 71},
+ {"panicdottypeI", funcTag, 71},
+ {"panicnildottype", funcTag, 72},
+ {"ifaceeq", funcTag, 73},
+ {"efaceeq", funcTag, 73},
+ {"fastrand", funcTag, 74},
+ {"makemap64", funcTag, 76},
+ {"makemap", funcTag, 77},
+ {"makemap_small", funcTag, 78},
+ {"mapaccess1", funcTag, 79},
+ {"mapaccess1_fast32", funcTag, 80},
+ {"mapaccess1_fast64", funcTag, 81},
+ {"mapaccess1_faststr", funcTag, 82},
+ {"mapaccess1_fat", funcTag, 83},
+ {"mapaccess2", funcTag, 84},
+ {"mapaccess2_fast32", funcTag, 85},
+ {"mapaccess2_fast64", funcTag, 86},
+ {"mapaccess2_faststr", funcTag, 87},
+ {"mapaccess2_fat", funcTag, 88},
+ {"mapassign", funcTag, 79},
+ {"mapassign_fast32", funcTag, 80},
+ {"mapassign_fast32ptr", funcTag, 89},
+ {"mapassign_fast64", funcTag, 81},
+ {"mapassign_fast64ptr", funcTag, 89},
+ {"mapassign_faststr", funcTag, 82},
+ {"mapiterinit", funcTag, 90},
+ {"mapdelete", funcTag, 90},
+ {"mapdelete_fast32", funcTag, 91},
+ {"mapdelete_fast64", funcTag, 92},
+ {"mapdelete_faststr", funcTag, 93},
+ {"mapiternext", funcTag, 94},
+ {"mapclear", funcTag, 95},
+ {"makechan64", funcTag, 97},
+ {"makechan", funcTag, 98},
+ {"chanrecv1", funcTag, 100},
+ {"chanrecv2", funcTag, 101},
+ {"chansend1", funcTag, 103},
{"closechan", funcTag, 30},
- {"writeBarrier", varTag, 104},
- {"typedmemmove", funcTag, 105},
- {"typedmemclr", funcTag, 106},
- {"typedslicecopy", funcTag, 107},
- {"selectnbsend", funcTag, 108},
- {"selectnbrecv", funcTag, 109},
- {"selectsetpc", funcTag, 110},
- {"selectgo", funcTag, 111},
+ {"writeBarrier", varTag, 105},
+ {"typedmemmove", funcTag, 106},
+ {"typedmemclr", funcTag, 107},
+ {"typedslicecopy", funcTag, 108},
+ {"selectnbsend", funcTag, 109},
+ {"selectnbrecv", funcTag, 110},
+ {"selectsetpc", funcTag, 111},
+ {"selectgo", funcTag, 112},
{"block", funcTag, 9},
- {"makeslice", funcTag, 112},
- {"makeslice64", funcTag, 113},
- {"makeslicecopy", funcTag, 114},
- {"growslice", funcTag, 116},
- {"unsafeslice", funcTag, 117},
- {"unsafeslice64", funcTag, 118},
- {"unsafeslicecheckptr", funcTag, 118},
- {"memmove", funcTag, 119},
- {"memclrNoHeapPointers", funcTag, 120},
- {"memclrHasPointers", funcTag, 120},
- {"memequal", funcTag, 121},
- {"memequal0", funcTag, 122},
- {"memequal8", funcTag, 122},
- {"memequal16", funcTag, 122},
- {"memequal32", funcTag, 122},
- {"memequal64", funcTag, 122},
- {"memequal128", funcTag, 122},
- {"f32equal", funcTag, 123},
- {"f64equal", funcTag, 123},
- {"c64equal", funcTag, 123},
- {"c128equal", funcTag, 123},
- {"strequal", funcTag, 123},
- {"interequal", funcTag, 123},
- {"nilinterequal", funcTag, 123},
- {"memhash", funcTag, 124},
- {"memhash0", funcTag, 125},
- {"memhash8", funcTag, 125},
- {"memhash16", funcTag, 125},
- {"memhash32", funcTag, 125},
- {"memhash64", funcTag, 125},
- {"memhash128", funcTag, 125},
- {"f32hash", funcTag, 125},
- {"f64hash", funcTag, 125},
- {"c64hash", funcTag, 125},
- {"c128hash", funcTag, 125},
- {"strhash", funcTag, 125},
- {"interhash", funcTag, 125},
- {"nilinterhash", funcTag, 125},
- {"int64div", funcTag, 126},
- {"uint64div", funcTag, 127},
- {"int64mod", funcTag, 126},
- {"uint64mod", funcTag, 127},
- {"float64toint64", funcTag, 128},
- {"float64touint64", funcTag, 129},
- {"float64touint32", funcTag, 130},
- {"int64tofloat64", funcTag, 131},
- {"uint64tofloat64", funcTag, 132},
- {"uint32tofloat64", funcTag, 133},
- {"complex128div", funcTag, 134},
- {"getcallerpc", funcTag, 135},
- {"getcallersp", funcTag, 135},
+ {"makeslice", funcTag, 113},
+ {"makeslice64", funcTag, 114},
+ {"makeslicecopy", funcTag, 115},
+ {"growslice", funcTag, 117},
+ {"unsafeslice", funcTag, 118},
+ {"unsafeslice64", funcTag, 119},
+ {"unsafeslicecheckptr", funcTag, 119},
+ {"memmove", funcTag, 120},
+ {"memclrNoHeapPointers", funcTag, 121},
+ {"memclrHasPointers", funcTag, 121},
+ {"memequal", funcTag, 122},
+ {"memequal0", funcTag, 123},
+ {"memequal8", funcTag, 123},
+ {"memequal16", funcTag, 123},
+ {"memequal32", funcTag, 123},
+ {"memequal64", funcTag, 123},
+ {"memequal128", funcTag, 123},
+ {"f32equal", funcTag, 124},
+ {"f64equal", funcTag, 124},
+ {"c64equal", funcTag, 124},
+ {"c128equal", funcTag, 124},
+ {"strequal", funcTag, 124},
+ {"interequal", funcTag, 124},
+ {"nilinterequal", funcTag, 124},
+ {"memhash", funcTag, 125},
+ {"memhash0", funcTag, 126},
+ {"memhash8", funcTag, 126},
+ {"memhash16", funcTag, 126},
+ {"memhash32", funcTag, 126},
+ {"memhash64", funcTag, 126},
+ {"memhash128", funcTag, 126},
+ {"f32hash", funcTag, 126},
+ {"f64hash", funcTag, 126},
+ {"c64hash", funcTag, 126},
+ {"c128hash", funcTag, 126},
+ {"strhash", funcTag, 126},
+ {"interhash", funcTag, 126},
+ {"nilinterhash", funcTag, 126},
+ {"int64div", funcTag, 127},
+ {"uint64div", funcTag, 128},
+ {"int64mod", funcTag, 127},
+ {"uint64mod", funcTag, 128},
+ {"float64toint64", funcTag, 129},
+ {"float64touint64", funcTag, 130},
+ {"float64touint32", funcTag, 131},
+ {"int64tofloat64", funcTag, 132},
+ {"int64tofloat32", funcTag, 134},
+ {"uint64tofloat64", funcTag, 135},
+ {"uint64tofloat32", funcTag, 136},
+ {"uint32tofloat64", funcTag, 137},
+ {"complex128div", funcTag, 138},
+ {"getcallerpc", funcTag, 139},
+ {"getcallersp", funcTag, 139},
{"racefuncenter", funcTag, 31},
{"racefuncexit", funcTag, 9},
{"raceread", funcTag, 31},
{"racewrite", funcTag, 31},
- {"racereadrange", funcTag, 136},
- {"racewriterange", funcTag, 136},
- {"msanread", funcTag, 136},
- {"msanwrite", funcTag, 136},
- {"msanmove", funcTag, 137},
- {"checkptrAlignment", funcTag, 138},
- {"checkptrArithmetic", funcTag, 140},
- {"libfuzzerTraceCmp1", funcTag, 141},
- {"libfuzzerTraceCmp2", funcTag, 142},
- {"libfuzzerTraceCmp4", funcTag, 143},
- {"libfuzzerTraceCmp8", funcTag, 144},
- {"libfuzzerTraceConstCmp1", funcTag, 141},
- {"libfuzzerTraceConstCmp2", funcTag, 142},
- {"libfuzzerTraceConstCmp4", funcTag, 143},
- {"libfuzzerTraceConstCmp8", funcTag, 144},
+ {"racereadrange", funcTag, 140},
+ {"racewriterange", funcTag, 140},
+ {"msanread", funcTag, 140},
+ {"msanwrite", funcTag, 140},
+ {"msanmove", funcTag, 141},
+ {"asanread", funcTag, 140},
+ {"asanwrite", funcTag, 140},
+ {"checkptrAlignment", funcTag, 142},
+ {"checkptrArithmetic", funcTag, 144},
+ {"libfuzzerTraceCmp1", funcTag, 145},
+ {"libfuzzerTraceCmp2", funcTag, 146},
+ {"libfuzzerTraceCmp4", funcTag, 147},
+ {"libfuzzerTraceCmp8", funcTag, 148},
+ {"libfuzzerTraceConstCmp1", funcTag, 145},
+ {"libfuzzerTraceConstCmp2", funcTag, 146},
+ {"libfuzzerTraceConstCmp4", funcTag, 147},
+ {"libfuzzerTraceConstCmp8", funcTag, 148},
{"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6},
@@ -224,7 +226,7 @@ func params(tlist ...*types.Type) []*types.Field {
}
func runtimeTypes() []*types.Type {
- var typs [145]*types.Type
+ var typs [149]*types.Type
typs[0] = types.ByteType
typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[types.TANY]
@@ -282,93 +284,97 @@ func runtimeTypes() []*types.Type {
typs[54] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15]))
typs[55] = newSig(params(typs[28], typs[15]), params(typs[46], typs[15]))
typs[56] = newSig(params(typs[28]), params(typs[15]))
- typs[57] = newSig(params(typs[1], typs[2]), params(typs[2]))
- typs[58] = types.Types[types.TUINT16]
- typs[59] = newSig(params(typs[58]), params(typs[7]))
- typs[60] = types.Types[types.TUINT32]
+ typs[57] = types.NewPtr(typs[5])
+ typs[58] = newSig(params(typs[1], typs[57]), params(typs[57]))
+ typs[59] = newSig(params(typs[1], typs[3]), params(typs[7]))
+ typs[60] = types.Types[types.TUINT16]
typs[61] = newSig(params(typs[60]), params(typs[7]))
- typs[62] = newSig(params(typs[24]), params(typs[7]))
- typs[63] = newSig(params(typs[28]), params(typs[7]))
- typs[64] = types.Types[types.TUINT8]
- typs[65] = types.NewSlice(typs[64])
- typs[66] = newSig(params(typs[65]), params(typs[7]))
- typs[67] = newSig(params(typs[1], typs[3]), params(typs[2]))
- typs[68] = newSig(params(typs[1], typs[1]), params(typs[1]))
- typs[69] = newSig(params(typs[1], typs[1], typs[1]), nil)
- typs[70] = newSig(params(typs[1]), nil)
- typs[71] = types.NewPtr(typs[5])
- typs[72] = newSig(params(typs[71], typs[7], typs[7]), params(typs[6]))
- typs[73] = newSig(nil, params(typs[60]))
- typs[74] = types.NewMap(typs[2], typs[2])
- typs[75] = newSig(params(typs[1], typs[22], typs[3]), params(typs[74]))
- typs[76] = newSig(params(typs[1], typs[15], typs[3]), params(typs[74]))
- typs[77] = newSig(nil, params(typs[74]))
- typs[78] = newSig(params(typs[1], typs[74], typs[3]), params(typs[3]))
- typs[79] = newSig(params(typs[1], typs[74], typs[60]), params(typs[3]))
- typs[80] = newSig(params(typs[1], typs[74], typs[24]), params(typs[3]))
- typs[81] = newSig(params(typs[1], typs[74], typs[28]), params(typs[3]))
- typs[82] = newSig(params(typs[1], typs[74], typs[3], typs[1]), params(typs[3]))
- typs[83] = newSig(params(typs[1], typs[74], typs[3]), params(typs[3], typs[6]))
- typs[84] = newSig(params(typs[1], typs[74], typs[60]), params(typs[3], typs[6]))
- typs[85] = newSig(params(typs[1], typs[74], typs[24]), params(typs[3], typs[6]))
- typs[86] = newSig(params(typs[1], typs[74], typs[28]), params(typs[3], typs[6]))
- typs[87] = newSig(params(typs[1], typs[74], typs[3], typs[1]), params(typs[3], typs[6]))
- typs[88] = newSig(params(typs[1], typs[74], typs[7]), params(typs[3]))
- typs[89] = newSig(params(typs[1], typs[74], typs[3]), nil)
- typs[90] = newSig(params(typs[1], typs[74], typs[60]), nil)
- typs[91] = newSig(params(typs[1], typs[74], typs[24]), nil)
- typs[92] = newSig(params(typs[1], typs[74], typs[28]), nil)
- typs[93] = newSig(params(typs[3]), nil)
- typs[94] = newSig(params(typs[1], typs[74]), nil)
- typs[95] = types.NewChan(typs[2], types.Cboth)
- typs[96] = newSig(params(typs[1], typs[22]), params(typs[95]))
- typs[97] = newSig(params(typs[1], typs[15]), params(typs[95]))
- typs[98] = types.NewChan(typs[2], types.Crecv)
- typs[99] = newSig(params(typs[98], typs[3]), nil)
- typs[100] = newSig(params(typs[98], typs[3]), params(typs[6]))
- typs[101] = types.NewChan(typs[2], types.Csend)
- typs[102] = newSig(params(typs[101], typs[3]), nil)
- typs[103] = types.NewArray(typs[0], 3)
- typs[104] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[103]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
- typs[105] = newSig(params(typs[1], typs[3], typs[3]), nil)
- typs[106] = newSig(params(typs[1], typs[3]), nil)
- typs[107] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
- typs[108] = newSig(params(typs[101], typs[3]), params(typs[6]))
- typs[109] = newSig(params(typs[3], typs[98]), params(typs[6], typs[6]))
- typs[110] = newSig(params(typs[71]), nil)
- typs[111] = newSig(params(typs[1], typs[1], typs[71], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
- typs[112] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
- typs[113] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
- typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
- typs[115] = types.NewSlice(typs[2])
- typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115]))
- typs[117] = newSig(params(typs[1], typs[7], typs[15]), nil)
- typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
- typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil)
- typs[120] = newSig(params(typs[7], typs[5]), nil)
- typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
- typs[122] = newSig(params(typs[3], typs[3]), params(typs[6]))
- typs[123] = newSig(params(typs[7], typs[7]), params(typs[6]))
- typs[124] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5]))
- typs[125] = newSig(params(typs[7], typs[5]), params(typs[5]))
- typs[126] = newSig(params(typs[22], typs[22]), params(typs[22]))
- typs[127] = newSig(params(typs[24], typs[24]), params(typs[24]))
- typs[128] = newSig(params(typs[20]), params(typs[22]))
- typs[129] = newSig(params(typs[20]), params(typs[24]))
- typs[130] = newSig(params(typs[20]), params(typs[60]))
- typs[131] = newSig(params(typs[22]), params(typs[20]))
- typs[132] = newSig(params(typs[24]), params(typs[20]))
- typs[133] = newSig(params(typs[60]), params(typs[20]))
- typs[134] = newSig(params(typs[26], typs[26]), params(typs[26]))
- typs[135] = newSig(nil, params(typs[5]))
- typs[136] = newSig(params(typs[5], typs[5]), nil)
- typs[137] = newSig(params(typs[5], typs[5], typs[5]), nil)
- typs[138] = newSig(params(typs[7], typs[1], typs[5]), nil)
- typs[139] = types.NewSlice(typs[7])
- typs[140] = newSig(params(typs[7], typs[139]), nil)
- typs[141] = newSig(params(typs[64], typs[64]), nil)
- typs[142] = newSig(params(typs[58], typs[58]), nil)
- typs[143] = newSig(params(typs[60], typs[60]), nil)
- typs[144] = newSig(params(typs[24], typs[24]), nil)
+ typs[62] = types.Types[types.TUINT32]
+ typs[63] = newSig(params(typs[62]), params(typs[7]))
+ typs[64] = newSig(params(typs[24]), params(typs[7]))
+ typs[65] = newSig(params(typs[28]), params(typs[7]))
+ typs[66] = types.Types[types.TUINT8]
+ typs[67] = types.NewSlice(typs[66])
+ typs[68] = newSig(params(typs[67]), params(typs[7]))
+ typs[69] = newSig(params(typs[1], typs[1]), params(typs[1]))
+ typs[70] = newSig(params(typs[1], typs[2]), params(typs[2]))
+ typs[71] = newSig(params(typs[1], typs[1], typs[1]), nil)
+ typs[72] = newSig(params(typs[1]), nil)
+ typs[73] = newSig(params(typs[57], typs[7], typs[7]), params(typs[6]))
+ typs[74] = newSig(nil, params(typs[62]))
+ typs[75] = types.NewMap(typs[2], typs[2])
+ typs[76] = newSig(params(typs[1], typs[22], typs[3]), params(typs[75]))
+ typs[77] = newSig(params(typs[1], typs[15], typs[3]), params(typs[75]))
+ typs[78] = newSig(nil, params(typs[75]))
+ typs[79] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3]))
+ typs[80] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3]))
+ typs[81] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3]))
+ typs[82] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3]))
+ typs[83] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3]))
+ typs[84] = newSig(params(typs[1], typs[75], typs[3]), params(typs[3], typs[6]))
+ typs[85] = newSig(params(typs[1], typs[75], typs[62]), params(typs[3], typs[6]))
+ typs[86] = newSig(params(typs[1], typs[75], typs[24]), params(typs[3], typs[6]))
+ typs[87] = newSig(params(typs[1], typs[75], typs[28]), params(typs[3], typs[6]))
+ typs[88] = newSig(params(typs[1], typs[75], typs[3], typs[1]), params(typs[3], typs[6]))
+ typs[89] = newSig(params(typs[1], typs[75], typs[7]), params(typs[3]))
+ typs[90] = newSig(params(typs[1], typs[75], typs[3]), nil)
+ typs[91] = newSig(params(typs[1], typs[75], typs[62]), nil)
+ typs[92] = newSig(params(typs[1], typs[75], typs[24]), nil)
+ typs[93] = newSig(params(typs[1], typs[75], typs[28]), nil)
+ typs[94] = newSig(params(typs[3]), nil)
+ typs[95] = newSig(params(typs[1], typs[75]), nil)
+ typs[96] = types.NewChan(typs[2], types.Cboth)
+ typs[97] = newSig(params(typs[1], typs[22]), params(typs[96]))
+ typs[98] = newSig(params(typs[1], typs[15]), params(typs[96]))
+ typs[99] = types.NewChan(typs[2], types.Crecv)
+ typs[100] = newSig(params(typs[99], typs[3]), nil)
+ typs[101] = newSig(params(typs[99], typs[3]), params(typs[6]))
+ typs[102] = types.NewChan(typs[2], types.Csend)
+ typs[103] = newSig(params(typs[102], typs[3]), nil)
+ typs[104] = types.NewArray(typs[0], 3)
+ typs[105] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[104]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
+ typs[106] = newSig(params(typs[1], typs[3], typs[3]), nil)
+ typs[107] = newSig(params(typs[1], typs[3]), nil)
+ typs[108] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
+ typs[109] = newSig(params(typs[102], typs[3]), params(typs[6]))
+ typs[110] = newSig(params(typs[3], typs[99]), params(typs[6], typs[6]))
+ typs[111] = newSig(params(typs[57]), nil)
+ typs[112] = newSig(params(typs[1], typs[1], typs[57], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
+ typs[113] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
+ typs[114] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
+ typs[115] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
+ typs[116] = types.NewSlice(typs[2])
+ typs[117] = newSig(params(typs[1], typs[116], typs[15]), params(typs[116]))
+ typs[118] = newSig(params(typs[1], typs[7], typs[15]), nil)
+ typs[119] = newSig(params(typs[1], typs[7], typs[22]), nil)
+ typs[120] = newSig(params(typs[3], typs[3], typs[5]), nil)
+ typs[121] = newSig(params(typs[7], typs[5]), nil)
+ typs[122] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
+ typs[123] = newSig(params(typs[3], typs[3]), params(typs[6]))
+ typs[124] = newSig(params(typs[7], typs[7]), params(typs[6]))
+ typs[125] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5]))
+ typs[126] = newSig(params(typs[7], typs[5]), params(typs[5]))
+ typs[127] = newSig(params(typs[22], typs[22]), params(typs[22]))
+ typs[128] = newSig(params(typs[24], typs[24]), params(typs[24]))
+ typs[129] = newSig(params(typs[20]), params(typs[22]))
+ typs[130] = newSig(params(typs[20]), params(typs[24]))
+ typs[131] = newSig(params(typs[20]), params(typs[62]))
+ typs[132] = newSig(params(typs[22]), params(typs[20]))
+ typs[133] = types.Types[types.TFLOAT32]
+ typs[134] = newSig(params(typs[22]), params(typs[133]))
+ typs[135] = newSig(params(typs[24]), params(typs[20]))
+ typs[136] = newSig(params(typs[24]), params(typs[133]))
+ typs[137] = newSig(params(typs[62]), params(typs[20]))
+ typs[138] = newSig(params(typs[26], typs[26]), params(typs[26]))
+ typs[139] = newSig(nil, params(typs[5]))
+ typs[140] = newSig(params(typs[5], typs[5]), nil)
+ typs[141] = newSig(params(typs[5], typs[5], typs[5]), nil)
+ typs[142] = newSig(params(typs[7], typs[1], typs[5]), nil)
+ typs[143] = types.NewSlice(typs[7])
+ typs[144] = newSig(params(typs[7], typs[143]), nil)
+ typs[145] = newSig(params(typs[66], typs[66]), nil)
+ typs[146] = newSig(params(typs[60], typs[60]), nil)
+ typs[147] = newSig(params(typs[62], typs[62]), nil)
+ typs[148] = newSig(params(typs[24], typs[24]), nil)
return typs[:]
}
diff --git a/src/cmd/compile/internal/typecheck/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go
index 2b29ea3c08ca72be0b35a2db22755490f76a4f11..04ae4f23a387790c11cc5d9e98360c55beff5019 100644
--- a/src/cmd/compile/internal/typecheck/builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go
@@ -84,10 +84,15 @@ func decoderune(string, int) (retv rune, retk int)
func countrunes(string) int
// Non-empty-interface to non-empty-interface conversion.
-func convI2I(typ *byte, elem any) (ret any)
+func convI2I(typ *byte, itab *uintptr) (ret *uintptr)
-// Specialized type-to-interface conversion.
-// These return only a data pointer.
+// Convert non-interface type to the data word of a (empty or nonempty) interface.
+func convT(typ *byte, elem *any) unsafe.Pointer
+
+// Same as convT, for types with no pointers in them.
+func convTnoptr(typ *byte, elem *any) unsafe.Pointer
+
+// Specialized versions of convT for specific types.
// These functions take concrete types in the runtime. But they may
// be used for a wider range of types, which have the same memory
// layout as the parameter type. The compiler converts the
@@ -99,14 +104,6 @@ func convT64(val uint64) unsafe.Pointer
func convTstring(val string) unsafe.Pointer
func convTslice(val []uint8) unsafe.Pointer
-// Type to empty-interface conversion.
-func convT2E(typ *byte, elem *any) (ret any)
-func convT2Enoptr(typ *byte, elem *any) (ret any)
-
-// Type to non-empty-interface conversion.
-func convT2I(tab *byte, elem *any) (ret any)
-func convT2Inoptr(tab *byte, elem *any) (ret any)
-
// interface type assertions x.(T)
func assertE2I(inter *byte, typ *byte) *byte
func assertE2I2(inter *byte, eface any) (ret any)
@@ -230,7 +227,9 @@ func float64toint64(float64) int64
func float64touint64(float64) uint64
func float64touint32(float64) uint32
func int64tofloat64(int64) float64
+func int64tofloat32(int64) float32
func uint64tofloat64(uint64) float64
+func uint64tofloat32(uint64) float32
func uint32tofloat64(uint32) float64
func complex128div(num complex128, den complex128) (quo complex128)
@@ -251,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/const.go b/src/cmd/compile/internal/typecheck/const.go
index 761b043794062d7dbb4c985d99319b35ba24f3eb..fbe7c02c49821eb587ea97bf5e27b9f507e3bff6 100644
--- a/src/cmd/compile/internal/typecheck/const.go
+++ b/src/cmd/compile/internal/typecheck/const.go
@@ -874,14 +874,16 @@ func evalunsafe(n ir.Node) int64 {
}
types.CalcSize(tr)
if n.Op() == ir.OALIGNOF {
- return int64(tr.Align)
+ return tr.Alignment()
}
- return tr.Width
+ return tr.Size()
case ir.OOFFSETOF:
// must be a selector.
n := n.(*ir.UnaryExpr)
- if n.X.Op() != ir.OXDOT {
+ // ODOT and ODOTPTR are allowed in case the OXDOT transformation has
+ // already happened (e.g. during -G=3 stenciling).
+ if n.X.Op() != ir.OXDOT && n.X.Op() != ir.ODOT && n.X.Op() != ir.ODOTPTR {
base.Errorf("invalid expression %v", n)
return 0
}
@@ -901,7 +903,7 @@ func evalunsafe(n ir.Node) int64 {
switch tsel.Op() {
case ir.ODOT, ir.ODOTPTR:
break
- case ir.OCALLPART:
+ case ir.OMETHVALUE:
base.Errorf("invalid expression %v: argument is a method value", n)
return 0
default:
diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go
new file mode 100644
index 0000000000000000000000000000000000000000..ae6542d071976ffc0a80457364c8f2138a4e203e
--- /dev/null
+++ b/src/cmd/compile/internal/typecheck/crawler.go
@@ -0,0 +1,284 @@
+// 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 typecheck
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/types"
+)
+
+// crawlExports crawls the type/object graph rooted at the given list of exported
+// objects. It descends through all parts of types and follows any methods on defined
+// types. Any functions that are found to be potentially callable by importers are
+// marked with ExportInline, so that iexport.go knows to re-export their inline body.
+// Also, any function or global referenced by a function marked by ExportInline() is
+// marked for export (whether its name is exported or not).
+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)
+ }
+}
+
+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)
+func (p *crawler) markObject(n *ir.Name) {
+ if n.Op() == ir.ONAME && n.Class == ir.PFUNC {
+ p.markInlBody(n)
+ }
+
+ // If a declared type name is reachable, users can embed it in their
+ // own types, which makes even its unexported methods reachable.
+ if n.Op() == ir.OTYPE {
+ p.markEmbed(n.Type())
+ }
+
+ p.markType(n.Type())
+}
+
+// markType recursively visits types reachable from t to identify functions whose
+// inline bodies may be needed. For instantiated generic types, it visits the base
+// generic type, which has the relevant methods.
+func (p *crawler) markType(t *types.Type) {
+ if t.OrigSym() != nil {
+ // Convert to the base generic type.
+ t = t.OrigSym().Def.Type()
+ }
+ if p.marked[t] {
+ return
+ }
+ p.marked[t] = true
+
+ // If this is a defined type, mark all of its associated
+ // methods. Skip interface types because t.Methods contains
+ // only their unexpanded method set (i.e., exclusive of
+ // interface embeddings), and the switch statement below
+ // handles their full method set.
+ if t.Sym() != nil && t.Kind() != types.TINTER {
+ for _, m := range t.Methods().Slice() {
+ if types.IsExported(m.Sym.Name) {
+ p.markObject(m.Nname.(*ir.Name))
+ }
+ }
+ }
+
+ // Recursively mark any types that can be produced given a
+ // value of type t: dereferencing a pointer; indexing or
+ // iterating over an array, slice, or map; receiving from a
+ // channel; accessing a struct field or interface method; or
+ // calling a function.
+ //
+ // Notably, we don't mark function parameter types, because
+ // the user already needs some way to construct values of
+ // those types.
+ switch t.Kind() {
+ case types.TPTR, types.TARRAY, types.TSLICE:
+ p.markType(t.Elem())
+
+ case types.TCHAN:
+ if t.ChanDir().CanRecv() {
+ p.markType(t.Elem())
+ }
+
+ case types.TMAP:
+ p.markType(t.Key())
+ p.markType(t.Elem())
+
+ case types.TSTRUCT:
+ if t.IsFuncArgStruct() {
+ break
+ }
+ for _, f := range t.FieldSlice() {
+ // Mark the type of a unexported field if it is a
+ // fully-instantiated type, since we create and instantiate
+ // the methods of any fully-instantiated type that we see
+ // during import (see end of typecheck.substInstType).
+ if types.IsExported(f.Sym.Name) || f.Embedded != 0 ||
+ isPtrFullyInstantiated(f.Type) {
+ p.markType(f.Type)
+ }
+ }
+
+ case types.TFUNC:
+ for _, f := range t.Results().FieldSlice() {
+ p.markType(f.Type)
+ }
+
+ case types.TINTER:
+ for _, f := range t.AllMethods().Slice() {
+ if types.IsExported(f.Sym.Name) {
+ p.markType(f.Type)
+ }
+ }
+
+ case types.TTYPEPARAM:
+ // No other type that needs to be followed.
+ }
+}
+
+// markEmbed is similar to markType, but handles finding methods that
+// need to be re-exported because t can be embedded in user code
+// (possibly transitively).
+func (p *crawler) markEmbed(t *types.Type) {
+ if t.IsPtr() {
+ // Defined pointer type; not allowed to embed anyway.
+ if t.Sym() != nil {
+ return
+ }
+ t = t.Elem()
+ }
+
+ if t.OrigSym() != nil {
+ // Convert to the base generic type.
+ t = t.OrigSym().Def.Type()
+ }
+
+ if p.embedded[t] {
+ return
+ }
+ p.embedded[t] = true
+
+ // If t is a defined type, then re-export all of its methods. Unlike
+ // in markType, we include even unexported methods here, because we
+ // still need to generate wrappers for them, even if the user can't
+ // refer to them directly.
+ if t.Sym() != nil && t.Kind() != types.TINTER {
+ for _, m := range t.Methods().Slice() {
+ p.markObject(m.Nname.(*ir.Name))
+ }
+ }
+
+ // If t is a struct, recursively visit its embedded fields.
+ if t.IsStruct() {
+ for _, f := range t.FieldSlice() {
+ if f.Embedded != 0 {
+ p.markEmbed(f.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) {
+ if n == nil {
+ return
+ }
+ if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
+ base.Fatalf("markInlBody: unexpected %v, %v, %v", n, n.Op(), n.Class)
+ }
+ fn := n.Func
+ if fn == nil {
+ base.Fatalf("markInlBody: missing Func on %v", n)
+ }
+ if !HaveInlineBody(fn) {
+ return
+ }
+
+ if fn.ExportInline() {
+ return
+ }
+ fn.SetExportInline(true)
+
+ ImportedBody(fn)
+
+ var doFlood func(n ir.Node)
+ doFlood = func(n ir.Node) {
+ t := n.Type()
+ 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 {
+ case ir.PFUNC:
+ p.markInlBody(n)
+ Export(n)
+ case ir.PEXTERN:
+ Export(n)
+ }
+ case ir.OMETHVALUE:
+ // Okay, because we don't yet inline indirect
+ // calls to method values.
+ case ir.OCLOSURE:
+ // VisitList doesn't visit closure bodies, so force a
+ // recursive call to VisitList on the body of the closure.
+ ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood)
+ }
+ }
+
+ // Recursively identify all referenced functions for
+ // reexport. We want to include even non-called functions,
+ // because after inlining they might be callable.
+ ir.VisitList(fn.Inl.Body, doFlood)
+}
+
+// isPtrFullyInstantiated returns true if t is a fully-instantiated type, or it is a
+// pointer to a fully-instantiated type.
+func isPtrFullyInstantiated(t *types.Type) bool {
+ return t.IsPtr() && t.Elem().IsFullyInstantiated() ||
+ t.IsFullyInstantiated()
+}
diff --git a/src/cmd/compile/internal/typecheck/dcl.go b/src/cmd/compile/internal/typecheck/dcl.go
index 5b771e3c0b1d836cb915efcd776aba3820464d41..68ab05a538e8fd52e56686892025dab8e59663e1 100644
--- a/src/cmd/compile/internal/typecheck/dcl.go
+++ b/src/cmd/compile/internal/typecheck/dcl.go
@@ -6,7 +6,7 @@ package typecheck
import (
"fmt"
- "strconv"
+ "sync"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -314,13 +314,6 @@ func checkembeddedtype(t *types.Type) {
}
}
-// TODO(mdempsky): Move to package types.
-func FakeRecv() *types.Field {
- return types.NewField(src.NoXPos, nil, types.FakeRecvType())
-}
-
-var fakeRecvField = FakeRecv
-
var funcStack []funcStackEnt // stack of previous values of ir.CurFunc/DeclContext
type funcStackEnt struct {
@@ -363,22 +356,18 @@ func funcargs(nt *ir.FuncType) {
}
// declare the out arguments.
- gen := len(nt.Params)
- for _, n := range nt.Results {
+ for i, n := range nt.Results {
if n.Sym == nil {
// Name so that escape analysis can track it. ~r stands for 'result'.
- n.Sym = LookupNum("~r", gen)
- gen++
- }
- if n.Sym.IsBlank() {
+ n.Sym = LookupNum("~r", i)
+ } 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)
// func g() int
// f is allowed to use a plain 'return' with no arguments, while g is not.
// So the two cases must be distinguished.
- n.Sym = LookupNum("~b", gen)
- gen++
+ n.Sym = LookupNum("~b", i)
}
funcarg(n, ir.PPARAMOUT)
@@ -431,6 +420,7 @@ func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name {
n := ir.NewNameAt(pos, s)
s.Def = n
n.SetType(t)
+ n.SetTypecheck(1)
n.Class = ir.PAUTO
n.SetEsc(ir.EscNever)
n.Curfn = curfn
@@ -443,20 +433,50 @@ func TempAt(pos src.XPos, curfn *ir.Func, t *types.Type) *ir.Name {
return n
}
+var (
+ autotmpnamesmu sync.Mutex
+ autotmpnames []string
+)
+
// autotmpname returns the name for an autotmp variable numbered n.
func autotmpname(n int) string {
- // Give each tmp a different name so that they can be registerized.
- // Add a preceding . to avoid clashing with legal names.
- const prefix = ".autotmp_"
- // Start with a buffer big enough to hold a large n.
- b := []byte(prefix + " ")[:len(prefix)]
- b = strconv.AppendInt(b, int64(n), 10)
- return types.InternString(b)
+ autotmpnamesmu.Lock()
+ defer autotmpnamesmu.Unlock()
+
+ // Grow autotmpnames, if needed.
+ if n >= len(autotmpnames) {
+ autotmpnames = append(autotmpnames, make([]string, n+1-len(autotmpnames))...)
+ autotmpnames = autotmpnames[:cap(autotmpnames)]
+ }
+
+ s := autotmpnames[n]
+ if s == "" {
+ // Give each tmp a different name so that they can be registerized.
+ // Add a preceding . to avoid clashing with legal names.
+ prefix := ".autotmp_%d"
+
+ // In quirks mode, pad out the number to stabilize variable
+ // sorting. This ensures autotmps 8 and 9 sort the same way even
+ // if they get renumbered to 9 and 10, respectively.
+ if base.Debug.UnifiedQuirks != 0 {
+ prefix = ".autotmp_%06d"
+ }
+
+ s = fmt.Sprintf(prefix, n)
+ autotmpnames[n] = s
+ }
+ return s
}
// f is method type, with receiver.
// return function type, receiver as first argument (or not).
func NewMethodType(sig *types.Type, recv *types.Type) *types.Type {
+ if sig.HasTParam() {
+ base.Fatalf("NewMethodType with type parameters in signature %+v", sig)
+ }
+ if recv != nil && recv.HasTParam() {
+ base.Fatalf("NewMethodType with type parameters in receiver %+v", recv)
+ }
nrecvs := 0
if recv != nil {
nrecvs++
diff --git a/src/cmd/compile/internal/typecheck/export.go b/src/cmd/compile/internal/typecheck/export.go
index 63d0a1ec6c656ce23e504458eacb456e01d12783..30726d4327f7ce8edf3d0879d73d939f094d90b0 100644
--- a/src/cmd/compile/internal/typecheck/export.go
+++ b/src/cmd/compile/internal/typecheck/export.go
@@ -15,22 +15,22 @@ import (
// importalias declares symbol s as an imported type alias with type t.
// ipkg is the package being imported
-func importalias(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
- return importobj(ipkg, pos, s, ir.OTYPE, ir.PEXTERN, t)
+func importalias(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ return importobj(pos, s, ir.OTYPE, ir.PEXTERN, t)
}
// importconst declares symbol s as an imported constant with type t and value val.
// ipkg is the package being imported
-func importconst(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name {
- n := importobj(ipkg, pos, s, ir.OLITERAL, ir.PEXTERN, t)
+func importconst(pos src.XPos, s *types.Sym, t *types.Type, val constant.Value) *ir.Name {
+ n := importobj(pos, s, ir.OLITERAL, ir.PEXTERN, t)
n.SetVal(val)
return n
}
// importfunc declares symbol s as an imported function with type t.
// ipkg is the package being imported
-func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
- n := importobj(ipkg, pos, s, ir.ONAME, ir.PFUNC, t)
+func importfunc(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ n := importobj(pos, s, ir.ONAME, ir.PFUNC, t)
n.Func = ir.NewFunc(pos)
n.Func.Nname = n
return n
@@ -38,8 +38,8 @@ func importfunc(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.
// importobj declares symbol s as an imported object representable by op.
// ipkg is the package being imported
-func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name {
- n := importsym(ipkg, pos, s, op, ctxt)
+func importobj(pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class, t *types.Type) *ir.Name {
+ n := importsym(pos, s, op, ctxt)
n.SetType(t)
if ctxt == ir.PFUNC {
n.Sym().SetFunc(true)
@@ -47,7 +47,7 @@ func importobj(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Cl
return n
}
-func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name {
+func importsym(pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Class) *ir.Name {
if n := s.PkgDef(); n != nil {
base.Fatalf("importsym of symbol that already exists: %v", n)
}
@@ -61,14 +61,14 @@ func importsym(ipkg *types.Pkg, pos src.XPos, s *types.Sym, op ir.Op, ctxt ir.Cl
// importtype returns the named type declared by symbol s.
// If no such type has been declared yet, a forward declaration is returned.
// ipkg is the package being imported
-func importtype(ipkg *types.Pkg, pos src.XPos, s *types.Sym) *ir.Name {
- n := importsym(ipkg, pos, s, ir.OTYPE, ir.PEXTERN)
+func importtype(pos src.XPos, s *types.Sym) *ir.Name {
+ n := importsym(pos, s, ir.OTYPE, ir.PEXTERN)
n.SetType(types.NewNamed(n))
return n
}
// importvar declares symbol s as an imported variable with type t.
// ipkg is the package being imported
-func importvar(ipkg *types.Pkg, pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
- return importobj(ipkg, pos, s, ir.ONAME, ir.PEXTERN, t)
+func importvar(pos src.XPos, s *types.Sym, t *types.Type) *ir.Name {
+ return importobj(pos, s, ir.ONAME, ir.PEXTERN, t)
}
diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go
index 24d141e8a2ce9149573b481b42541d353a2be704..9b74bf7a9d3762ce60c7fbc414fc9e2770885fcd 100644
--- a/src/cmd/compile/internal/typecheck/expr.go
+++ b/src/cmd/compile/internal/typecheck/expr.go
@@ -77,10 +77,6 @@ func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) {
return l, r, t
}
-func IsCmp(op ir.Op) bool {
- return iscmp[op]
-}
-
// tcArith typechecks operands of a binary arithmetic expression.
// The result of tcArith MUST be assigned back to original operands,
// t is the type of the expression, and should be set by the caller. e.g:
@@ -96,7 +92,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type)
t = r.Type()
}
aop := ir.OXXX
- if iscmp[n.Op()] && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) {
+ if n.Op().IsCmp() && t.Kind() != types.TIDEAL && !types.Identical(l.Type(), r.Type()) {
// comparison is okay as long as one side is
// assignable to the other. convert so they have
// the same type.
@@ -114,7 +110,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type)
}
types.CalcSize(l.Type())
- if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Width >= 1<<16 {
+ if r.Type().IsInterface() == l.Type().IsInterface() || l.Type().Size() >= 1<<16 {
l = ir.NewConvExpr(base.Pos, aop, r.Type(), l)
l.SetTypecheck(1)
}
@@ -133,7 +129,7 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type)
}
types.CalcSize(r.Type())
- if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Width >= 1<<16 {
+ if r.Type().IsInterface() == l.Type().IsInterface() || r.Type().Size() >= 1<<16 {
r = ir.NewConvExpr(base.Pos, aop, l.Type(), r)
r.SetTypecheck(1)
}
@@ -311,14 +307,23 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) {
f := t.Field(i)
s := f.Sym
- if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
- base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+
+ // Do the test for assigning to unexported fields.
+ // But if this is an instantiated function, then
+ // the function has already been typechecked. In
+ // that case, don't do the test, since it can fail
+ // for the closure structs created in
+ // walkClosure(), because the instantiated
+ // function is compiled as if in the source
+ // package of the generic function.
+ if !(ir.CurFunc != nil && strings.Index(ir.CurFunc.Nname.Sym().Name, "[") >= 0) {
+ if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
+ base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+ }
}
// No pushtype allowed here. Must name fields for that.
n1 = AssignConv(n1, f.Type, "field value")
- sk := ir.NewStructKeyExpr(base.Pos, f.Sym, n1)
- sk.Offset = f.Offset
- ls[i] = sk
+ ls[i] = ir.NewStructKeyExpr(base.Pos, f, n1)
}
if len(ls) < t.NumFields() {
base.Errorf("too few values in %v", n)
@@ -328,77 +333,33 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) {
// keyed list
ls := n.List
- for i, l := range ls {
- ir.SetPos(l)
-
- if l.Op() == ir.OKEY {
- kv := l.(*ir.KeyExpr)
- key := kv.Key
-
- // Sym might have resolved to name in other top-level
- // package, because of import dot. Redirect to correct sym
- // before we do the lookup.
- s := key.Sym()
- if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil {
- s = Lookup(s.Name)
- }
-
- // An OXDOT uses the Sym field to hold
- // the field to the right of the dot,
- // so s will be non-nil, but an OXDOT
- // is never a valid struct literal key.
- if s == nil || s.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || s.IsBlank() {
- base.Errorf("invalid field name %v in struct initializer", key)
- continue
- }
-
- l = ir.NewStructKeyExpr(l.Pos(), s, kv.Value)
- ls[i] = l
- }
-
- if l.Op() != ir.OSTRUCTKEY {
- if !errored {
- base.Errorf("mixture of field:value and value initializers")
- errored = true
- }
- ls[i] = Expr(ls[i])
- continue
- }
- l := l.(*ir.StructKeyExpr)
-
- f := Lookdot1(nil, l.Field, t, t.Fields(), 0)
- if f == nil {
- if ci := Lookdot1(nil, l.Field, t, t.Fields(), 2); ci != nil { // Case-insensitive lookup.
- if visible(ci.Sym) {
- base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", l.Field, t, ci.Sym)
- } else if nonexported(l.Field) && l.Field.Name == ci.Sym.Name { // Ensure exactness before the suggestion.
- base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", l.Field, t)
- } else {
- base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t)
+ for i, n := range ls {
+ ir.SetPos(n)
+
+ sk, ok := n.(*ir.StructKeyExpr)
+ if !ok {
+ kv, ok := n.(*ir.KeyExpr)
+ if !ok {
+ if !errored {
+ base.Errorf("mixture of field:value and value initializers")
+ errored = true
}
+ ls[i] = Expr(n)
continue
}
- var f *types.Field
- p, _ := dotpath(l.Field, t, &f, true)
- if p == nil || f.IsMethod() {
- base.Errorf("unknown field '%v' in struct literal of type %v", l.Field, t)
+
+ sk = tcStructLitKey(t, kv)
+ if sk == nil {
continue
}
- // dotpath returns the parent embedded types in reverse order.
- var ep []string
- for ei := len(p) - 1; ei >= 0; ei-- {
- ep = append(ep, p[ei].field.Sym.Name)
- }
- ep = append(ep, l.Field.Name)
- base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), t)
- continue
+
+ fielddup(sk.Sym().Name, hash)
}
- fielddup(f.Sym.Name, hash)
- l.Offset = f.Offset
// No pushtype allowed here. Tried and rejected.
- l.Value = Expr(l.Value)
- l.Value = AssignConv(l.Value, f.Type, "field value")
+ sk.Value = Expr(sk.Value)
+ sk.Value = AssignConv(sk.Value, sk.Field.Type, "field value")
+ ls[i] = sk
}
}
@@ -409,6 +370,60 @@ func tcCompLit(n *ir.CompLitExpr) (res ir.Node) {
return n
}
+// tcStructLitKey typechecks an OKEY node that appeared within a
+// struct literal.
+func tcStructLitKey(typ *types.Type, kv *ir.KeyExpr) *ir.StructKeyExpr {
+ key := kv.Key
+
+ // Sym might have resolved to name in other top-level
+ // package, because of import dot. Redirect to correct sym
+ // before we do the lookup.
+ sym := key.Sym()
+ if id, ok := key.(*ir.Ident); ok && DotImportRefs[id] != nil {
+ sym = Lookup(sym.Name)
+ }
+
+ // An OXDOT uses the Sym field to hold
+ // the field to the right of the dot,
+ // so s will be non-nil, but an OXDOT
+ // is never a valid struct literal key.
+ if sym == nil || sym.Pkg != types.LocalPkg || key.Op() == ir.OXDOT || sym.IsBlank() {
+ base.Errorf("invalid field name %v in struct initializer", key)
+ return nil
+ }
+
+ if f := Lookdot1(nil, sym, typ, typ.Fields(), 0); f != nil {
+ return ir.NewStructKeyExpr(kv.Pos(), f, kv.Value)
+ }
+
+ if ci := Lookdot1(nil, sym, typ, typ.Fields(), 2); ci != nil { // Case-insensitive lookup.
+ if visible(ci.Sym) {
+ base.Errorf("unknown field '%v' in struct literal of type %v (but does have %v)", sym, typ, ci.Sym)
+ } else if nonexported(sym) && sym.Name == ci.Sym.Name { // Ensure exactness before the suggestion.
+ base.Errorf("cannot refer to unexported field '%v' in struct literal of type %v", sym, typ)
+ } else {
+ base.Errorf("unknown field '%v' in struct literal of type %v", sym, typ)
+ }
+ return nil
+ }
+
+ var f *types.Field
+ p, _ := dotpath(sym, typ, &f, true)
+ if p == nil || f.IsMethod() {
+ base.Errorf("unknown field '%v' in struct literal of type %v", sym, typ)
+ return nil
+ }
+
+ // dotpath returns the parent embedded types in reverse order.
+ var ep []string
+ for ei := len(p) - 1; ei >= 0; ei-- {
+ ep = append(ep, p[ei].field.Sym.Name)
+ }
+ ep = append(ep, sym.Name)
+ base.Errorf("cannot use promoted field %v in struct literal of type %v", strings.Join(ep, "."), typ)
+ return nil
+}
+
// tcConv typechecks an OCONV node.
func tcConv(n *ir.ConvExpr) ir.Node {
types.CheckSize(n.Type()) // ensure width is calculated for backend
@@ -522,8 +537,8 @@ func tcDot(n *ir.SelectorExpr, top int) ir.Node {
}
if (n.Op() == ir.ODOTINTER || n.Op() == ir.ODOTMETH) && top&ctxCallee == 0 {
- n.SetOp(ir.OCALLPART)
- n.SetType(MethodValueWrapper(n).Type())
+ n.SetOp(ir.OMETHVALUE)
+ n.SetType(NewMethodType(n.Type(), nil))
}
return n
}
diff --git a/src/cmd/compile/internal/typecheck/func.go b/src/cmd/compile/internal/typecheck/func.go
index fbcc784627d6ce8d64bb17eab49b7a679af10ab5..57b15b7a2bc78e615b42e673d842840ff1863941 100644
--- a/src/cmd/compile/internal/typecheck/func.go
+++ b/src/cmd/compile/internal/typecheck/func.go
@@ -8,28 +8,29 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
+ "cmd/internal/src"
"fmt"
"go/constant"
"go/token"
)
-// package all the arguments that match a ... T parameter into a []T.
-func MakeDotArgs(typ *types.Type, args []ir.Node) ir.Node {
+// MakeDotArgs package all the arguments that match a ... T parameter into a []T.
+func MakeDotArgs(pos src.XPos, typ *types.Type, args []ir.Node) ir.Node {
var n ir.Node
if len(args) == 0 {
- n = NodNil()
+ n = ir.NewNilExpr(pos)
n.SetType(typ)
} else {
- lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
- lit.List.Append(args...)
+ args = append([]ir.Node(nil), args...)
+ lit := ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), args)
lit.SetImplicit(true)
n = lit
}
n = Expr(n)
if n.Type() == nil {
- base.Fatalf("mkdotargslice: typecheck failed")
+ base.FatalfAt(pos, "mkdotargslice: typecheck failed")
}
return n
}
@@ -47,7 +48,7 @@ func FixVariadicCall(call *ir.CallExpr) {
args := call.Args
extra := args[vi:]
- slice := MakeDotArgs(vt, extra)
+ slice := MakeDotArgs(call.Pos(), vt, extra)
for i := range extra {
extra[i] = nil // allow GC
}
@@ -56,6 +57,25 @@ func FixVariadicCall(call *ir.CallExpr) {
call.IsDDD = true
}
+// FixMethodCall rewrites a method call t.M(...) into a function call T.M(t, ...).
+func FixMethodCall(call *ir.CallExpr) {
+ if call.X.Op() != ir.ODOTMETH {
+ return
+ }
+
+ dot := call.X.(*ir.SelectorExpr)
+
+ fn := Expr(ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym))
+
+ args := make([]ir.Node, 1+len(call.Args))
+ args[0] = dot.X
+ copy(args[1:], call.Args)
+
+ call.SetOp(ir.OCALLFUNC)
+ call.X = fn
+ call.Args = args
+}
+
// ClosureType returns the struct type used to hold all the information
// needed in the closure for clo (clo must be a OCLOSURE node).
// The address of a variable of the returned type can be cast to a func.
@@ -73,8 +93,25 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type {
// The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor.
+
+ // Make sure the .F field is in the same package as the rest of the
+ // fields. This deals with closures in instantiated functions, which are
+ // compiled as if from the source package of the generic function.
+ var pkg *types.Pkg
+ if len(clo.Func.ClosureVars) == 0 {
+ pkg = types.LocalPkg
+ } else {
+ for _, v := range clo.Func.ClosureVars {
+ if pkg == nil {
+ pkg = v.Sym().Pkg
+ } else if pkg != v.Sym().Pkg {
+ base.Fatalf("Closure variables from multiple packages")
+ }
+ }
+ }
+
fields := []*types.Field{
- types.NewField(base.Pos, Lookup(".F"), types.Types[types.TUINTPTR]),
+ types.NewField(base.Pos, pkg.Lookup(".F"), types.Types[types.TUINTPTR]),
}
for _, v := range clo.Func.ClosureVars {
typ := v.Type()
@@ -88,10 +125,10 @@ func ClosureType(clo *ir.ClosureExpr) *types.Type {
return typ
}
-// PartialCallType returns the struct type used to hold all the information
-// needed in the closure for n (n must be a OCALLPART node).
-// The address of a variable of the returned type can be cast to a func.
-func PartialCallType(n *ir.SelectorExpr) *types.Type {
+// MethodValueType returns the struct type used to hold all the information
+// needed in the closure for a OMETHVALUE node. The address of a variable of
+// the returned type can be cast to a func.
+func MethodValueType(n *ir.SelectorExpr) *types.Type {
t := types.NewStruct(types.NoPkg, []*types.Field{
types.NewField(base.Pos, Lookup("F"), types.Types[types.TUINTPTR]),
types.NewField(base.Pos, Lookup("R"), n.X.Type()),
@@ -123,7 +160,12 @@ func ImportedBody(fn *ir.Func) {
IncrementalAddrtaken = false
defer func() {
if DirtyAddrtaken {
- ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available
+ // We do ComputeAddrTaken on function instantiations, but not
+ // generic functions (since we may not yet know if x in &x[i]
+ // is an array or a slice).
+ if !fn.Type().HasTParam() {
+ ComputeAddrtaken(fn.Inl.Body) // compute addrtaken marks once types are available
+ }
DirtyAddrtaken = false
}
IncrementalAddrtaken = true
@@ -181,153 +223,38 @@ func fnpkg(fn *ir.Name) *types.Pkg {
return fn.Sym().Pkg
}
-// ClosureName generates a new unique name for a closure within
-// outerfunc.
-func ClosureName(outerfunc *ir.Func) *types.Sym {
- outer := "glob."
- prefix := "func"
- gen := &globClosgen
-
- if outerfunc != nil {
- if outerfunc.OClosure != nil {
- prefix = ""
- }
-
- outer = ir.FuncName(outerfunc)
-
- // There may be multiple functions named "_". In those
- // cases, we can't use their individual Closgens as it
- // would lead to name clashes.
- if !ir.IsBlank(outerfunc.Nname) {
- gen = &outerfunc.Closgen
- }
- }
-
- *gen++
- return Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
-}
-
-// globClosgen is like Func.Closgen, but for the global scope.
-var globClosgen int32
-
-// MethodValueWrapper returns the DCLFUNC node representing the
-// wrapper function (*-fm) needed for the given method value. If the
-// wrapper function hasn't already been created yet, it's created and
-// added to Target.Decls.
-//
-// TODO(mdempsky): Move into walk. This isn't part of type checking.
-func MethodValueWrapper(dot *ir.SelectorExpr) *ir.Func {
- if dot.Op() != ir.OCALLPART {
- base.Fatalf("MethodValueWrapper: unexpected %v (%v)", dot, dot.Op())
- }
-
- t0 := dot.Type()
- meth := dot.Sel
- rcvrtype := dot.X.Type()
- sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
-
- if sym.Uniq() {
- return sym.Def.(*ir.Func)
- }
- sym.SetUniq(true)
-
- savecurfn := ir.CurFunc
- saveLineNo := base.Pos
- ir.CurFunc = nil
-
- // Set line number equal to the line number where the method is declared.
- if pos := dot.Selection.Pos; pos.IsKnown() {
- base.Pos = pos
- }
- // Note: !dot.Selection.Pos.IsKnown() happens for method expressions where
- // the method is implicitly declared. The Error method of the
- // built-in error type is one such method. We leave the line
- // number at the use of the method expression in this
- // case. See issue 29389.
-
- tfn := ir.NewFuncType(base.Pos, nil,
- NewFuncParams(t0.Params(), true),
- NewFuncParams(t0.Results(), false))
-
- fn := DeclFunc(sym, tfn)
- fn.SetDupok(true)
- fn.SetNeedctxt(true)
- fn.SetWrapper(true)
-
- // Declare and initialize variable holding receiver.
- ptr := ir.NewNameAt(base.Pos, Lookup(".this"))
- ptr.Class = ir.PAUTOHEAP
- ptr.SetType(rcvrtype)
- ptr.Curfn = fn
- ptr.SetIsClosureVar(true)
- ptr.SetByval(true)
- fn.ClosureVars = append(fn.ClosureVars, ptr)
-
- call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
- call.Args = ir.ParamNames(tfn.Type())
- call.IsDDD = tfn.Type().IsVariadic()
-
- var body ir.Node = call
- if t0.NumResults() != 0 {
- ret := ir.NewReturnStmt(base.Pos, nil)
- ret.Results = []ir.Node{call}
- body = ret
- }
-
- fn.Body = []ir.Node{body}
- FinishFuncBody()
-
- Func(fn)
- // Need to typecheck the body of the just-generated wrapper.
- // typecheckslice() requires that Curfn is set when processing an ORETURN.
- ir.CurFunc = fn
- Stmts(fn.Body)
- sym.Def = fn
- Target.Decls = append(Target.Decls, fn)
- ir.CurFunc = savecurfn
- base.Pos = saveLineNo
-
- return fn
-}
-
// tcClosure typechecks an OCLOSURE node. It also creates the named
// function associated with the closure.
// TODO: This creation of the named function should probably really be done in a
// separate pass from type-checking.
-func tcClosure(clo *ir.ClosureExpr, top int) {
+func tcClosure(clo *ir.ClosureExpr, top int) ir.Node {
fn := clo.Func
+
+ // We used to allow IR builders to typecheck the underlying Func
+ // themselves, but that led to too much variety and inconsistency
+ // around who's responsible for naming the function, typechecking
+ // it, or adding it to Target.Decls.
+ //
+ // It's now all or nothing. Callers are still allowed to do these
+ // themselves, but then they assume responsibility for all of them.
+ if fn.Typecheck() == 1 {
+ base.FatalfAt(fn.Pos(), "underlying closure func already typechecked: %v", fn)
+ }
+
// Set current associated iota value, so iota can be used inside
// function in ConstSpec, see issue #22344
if x := getIotaValue(); x >= 0 {
fn.Iota = x
}
- fn.SetClosureCalled(top&ctxCallee != 0)
-
- // Do not typecheck fn twice, otherwise, we will end up pushing
- // fn to Target.Decls multiple times, causing InitLSym called twice.
- // See #30709
- if fn.Typecheck() == 1 {
- clo.SetType(fn.Type())
- return
- }
-
- // Don't give a name and add to Target.Decls if we are typechecking an inlined
- // body in ImportedBody(), since we only want to create the named function
- // when the closure is actually inlined (and then we force a typecheck
- // explicitly in (*inlsubst).node()).
- if !inTypeCheckInl {
- fn.Nname.SetSym(ClosureName(ir.CurFunc))
- ir.MarkFunc(fn.Nname)
- }
+ ir.NameClosure(clo, ir.CurFunc)
Func(fn)
- clo.SetType(fn.Type())
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
// ready to type check code yet; we'll check it later, because the
// underlying closure function we create is added to Target.Decls.
- if ir.CurFunc != nil && clo.Type() != nil {
+ if ir.CurFunc != nil {
oldfn := ir.CurFunc
ir.CurFunc = fn
Stmts(fn.Body)
@@ -353,14 +280,17 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
}
fn.ClosureVars = fn.ClosureVars[:out]
- if base.Flag.W > 1 {
- s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn))
- ir.Dump(s, fn)
- }
- if !inTypeCheckInl {
- // Add function to Target.Decls once only when we give it a name
- Target.Decls = append(Target.Decls, fn)
+ clo.SetType(fn.Type())
+
+ target := Target
+ if inTypeCheckInl {
+ // We're typechecking an imported function, so it's not actually
+ // part of Target. Skip adding it to Target.Decls so we don't
+ // compile it again.
+ target = nil
}
+
+ return ir.UseClosure(clo, target)
}
// type check function definition
@@ -390,10 +320,6 @@ func tcFunc(n *ir.Func) {
// tcCall typechecks an OCALL node.
func tcCall(n *ir.CallExpr, top int) ir.Node {
- n.Use = ir.CallUseExpr
- if top == ctxStmt {
- n.Use = ir.CallUseStmt
- }
Stmts(n.Init()) // imported rewritten f(g()) calls (#30907)
n.X = typecheck(n.X, ctxExpr|ctxType|ctxCallee)
if n.X.Diag() {
@@ -509,6 +435,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
}
typecheckaste(ir.OCALL, n.X, n.IsDDD, t.Params(), n.Args, func() string { return fmt.Sprintf("argument to %v", n.X) })
+ FixMethodCall(n)
if t.NumResults() == 0 {
return n
}
@@ -979,6 +906,21 @@ func tcRecover(n *ir.CallExpr) ir.Node {
return n
}
+// tcRecoverFP typechecks an ORECOVERFP node.
+func tcRecoverFP(n *ir.CallExpr) ir.Node {
+ if len(n.Args) != 1 {
+ base.FatalfAt(n.Pos(), "wrong number of arguments: %v", n)
+ }
+
+ n.Args[0] = Expr(n.Args[0])
+ if !n.Args[0].Type().IsPtrShaped() {
+ base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.Args[0])
+ }
+
+ n.SetType(types.Types[types.TINTER])
+ return n
+}
+
// tcUnsafeAdd typechecks an OUNSAFEADD node.
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
if !types.AllowsGoVersion(curpkg(), 1, 17) {
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 64d68ef62550d8a4b5c2d3046336e48c46c8352c..7ebabe7314536694e9f13e7cc974c5d6eba4f0af 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -63,8 +63,9 @@
// }
//
// type Func struct {
-// Tag byte // 'F'
+// Tag byte // 'F' or 'G'
// Pos Pos
+// TypeParams []typeOff // only present if Tag == 'G'
// Signature Signature
// }
//
@@ -75,8 +76,9 @@
// }
//
// type Type struct {
-// Tag byte // 'T'
+// Tag byte // 'T' or 'U'
// Pos Pos
+// TypeParams []typeOff // only present if Tag == 'U'
// Underlying typeOff
//
// Methods []struct{ // omitted if Underlying is an interface type
@@ -93,6 +95,13 @@
// Type typeOff
// }
//
+// // "Automatic" declaration of each typeparam
+// type TypeParam struct {
+// Tag byte // 'P'
+// Pos Pos
+// Implicit bool
+// Constraint typeOff
+// }
//
// typeOff means a uvarint that either indicates a predeclared type,
// or an offset into the Data section. If the uvarint is less than
@@ -100,11 +109,11 @@
// 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.
//
//
-// There are nine kinds of type descriptors, distinguished by an itag:
+// There are twelve kinds of type descriptors, distinguished by an itag:
//
// type DefinedType struct {
// Tag itag // definedType
@@ -172,6 +181,30 @@
// }
// }
//
+// // Reference to a type param declaration
+// type TypeParamType struct {
+// Tag itag // typeParamType
+// Name stringOff
+// PkgPath stringOff
+// }
+//
+// // Instantiation of a generic type (like List[T2] or List[int])
+// type InstanceType struct {
+// Tag itag // instanceType
+// Pos pos
+// TypeArgs []typeOff
+// BaseType typeOff
+// }
+//
+// type UnionType struct {
+// Tag itag // interfaceType
+// Terms []struct {
+// tilde bool
+// Type typeOff
+// }
+// }
+//
+//
//
// type Signature struct {
// Params []Param
@@ -202,7 +235,6 @@
package typecheck
import (
- "bufio"
"bytes"
"crypto/md5"
"encoding/binary"
@@ -221,9 +253,19 @@ import (
)
// Current indexed export format version. Increase with each format change.
-// 1: added column details to Pos
// 0: Go1.11 encoding
-const iexportVersion = 1
+// 1: added column details to Pos
+// 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
+ iexportVersionGenerics = 2
+ iexportVersionGo1_18 = 2
+
+ iexportVersionCurrent = 2
+)
// predeclReserved is the number of type offsets reserved for types
// implicitly declared in the universe block.
@@ -244,6 +286,9 @@ const (
signatureType
structType
interfaceType
+ typeParamType
+ instanceType // Instantiation of a generic type
+ unionType
)
const (
@@ -251,13 +296,22 @@ const (
magic = 0x6742937dc293105
)
-func WriteExports(out *bufio.Writer) {
+// WriteExports writes the indexed export format to out. If extensions
+// is true, then the compiler-only extensions are included.
+func WriteExports(out io.Writer, extensions bool) {
+ if extensions {
+ // If we're exporting inline bodies, invoke the crawler to mark
+ // which bodies to include.
+ crawlExports(Target.Exports)
+ }
+
p := iexporter{
allPkgs: map[*types.Pkg]bool{},
stringIndex: map[string]uint64{},
declIndex: map[*types.Sym]uint64{},
inlineIndex: map[*types.Sym]uint64{},
typIndex: map[*types.Type]uint64{},
+ extensions: extensions,
}
for i, pt := range predeclared() {
@@ -293,7 +347,7 @@ func WriteExports(out *bufio.Writer) {
// Assemble header.
var hdr intWriter
hdr.WriteByte('i')
- hdr.uint64(iexportVersion)
+ hdr.uint64(iexportVersionCurrent)
hdr.uint64(uint64(p.strings.Len()))
hdr.uint64(dataLen)
@@ -379,6 +433,8 @@ type iexporter struct {
declIndex map[*types.Sym]uint64
inlineIndex map[*types.Sym]uint64
typIndex map[*types.Type]uint64
+
+ extensions bool
}
// stringOff returns the offset of s within the string section.
@@ -406,7 +462,7 @@ func (p *iexporter) pushDecl(n *ir.Name) {
}
// Don't export predeclared declarations.
- if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == ir.Pkgs.Unsafe {
+ if n.Sym().Pkg == types.BuiltinPkg || n.Sym().Pkg == types.UnsafePkg {
return
}
@@ -449,7 +505,9 @@ func (p *iexporter) doDecl(n *ir.Name) {
w.tag('V')
w.pos(n.Pos())
w.typ(n.Type())
- w.varExt(n)
+ if w.p.extensions {
+ w.varExt(n)
+ }
case ir.PFUNC:
if ir.IsMethod(n) {
@@ -457,10 +515,25 @@ func (p *iexporter) doDecl(n *ir.Name) {
}
// Function.
- w.tag('F')
+ if n.Type().TParams().NumFields() == 0 {
+ w.tag('F')
+ } else {
+ w.tag('G')
+ }
w.pos(n.Pos())
+ // The tparam list of the function type is the
+ // declaration of the type params. So, write out the type
+ // params right now. Then those type params will be
+ // referenced via their type offset (via typOff) in all
+ // other places in the signature and function that they
+ // are used.
+ if n.Type().TParams().NumFields() > 0 {
+ w.tparamList(n.Type().TParams().FieldSlice())
+ }
w.signature(n.Type())
- w.funcExt(n)
+ if w.p.extensions {
+ w.funcExt(n)
+ }
default:
base.Fatalf("unexpected class: %v, %v", n, n.Class)
@@ -476,10 +549,29 @@ func (p *iexporter) doDecl(n *ir.Name) {
w.tag('C')
w.pos(n.Pos())
w.value(n.Type(), n.Val())
- w.constExt(n)
+ if w.p.extensions {
+ w.constExt(n)
+ }
case ir.OTYPE:
- if types.IsDotAlias(n.Sym()) {
+ if n.Type().IsTypeParam() && n.Type().Underlying() == n.Type() {
+ // Even though it has local scope, a typeparam requires a
+ // declaration via its package and unique name, because it
+ // may be referenced within its type bound during its own
+ // definition.
+ w.tag('P')
+ // 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
+ }
+
+ if n.Alias() {
// Alias.
w.tag('A')
w.pos(n.Pos())
@@ -488,9 +580,18 @@ func (p *iexporter) doDecl(n *ir.Name) {
}
// Defined type.
- w.tag('T')
+ if len(n.Type().RParams()) == 0 {
+ w.tag('T')
+ } else {
+ w.tag('U')
+ }
w.pos(n.Pos())
+ if len(n.Type().RParams()) > 0 {
+ // Export type parameters, if any, needed for this type
+ w.typeList(n.Type().RParams())
+ }
+
underlying := n.Type().Underlying()
if underlying == types.ErrorType.Underlying() {
// For "type T error", use error as the
@@ -501,26 +602,43 @@ func (p *iexporter) doDecl(n *ir.Name) {
// for predeclared objects).
underlying = types.ErrorType
}
+ if underlying == types.ComparableType.Underlying() {
+ // Do same for ComparableType as for ErrorType.
+ underlying = types.ComparableType
+ }
+ if base.Flag.G > 0 && underlying == types.AnyType.Underlying() {
+ // Do same for AnyType as for ErrorType.
+ underlying = types.AnyType
+ }
w.typ(underlying)
t := n.Type()
if t.IsInterface() {
- w.typeExt(t)
+ if w.p.extensions {
+ w.typeExt(t)
+ }
break
}
- ms := t.Methods()
- w.uint64(uint64(ms.Len()))
- for _, m := range ms.Slice() {
+ // Sort methods, for consistency with types2.
+ methods := append([]*types.Field(nil), t.Methods().Slice()...)
+ if base.Debug.UnifiedQuirks != 0 {
+ sort.Sort(types.MethodsByName(methods))
+ }
+
+ w.uint64(uint64(len(methods)))
+ for _, m := range methods {
w.pos(m.Pos)
w.selector(m.Sym)
w.param(m.Type.Recv())
w.signature(m.Type)
}
- w.typeExt(t)
- for _, m := range ms.Slice() {
- w.methExt(m)
+ if w.p.extensions {
+ w.typeExt(t)
+ for _, m := range methods {
+ w.methExt(m)
+ }
}
default:
@@ -803,8 +921,46 @@ func (w *exportWriter) startType(k itag) {
}
func (w *exportWriter) doTyp(t *types.Type) {
- if t.Sym() != nil {
- if t.Sym().Pkg == types.BuiltinPkg || t.Sym().Pkg == ir.Pkgs.Unsafe {
+ s := t.Sym()
+ if s != nil && t.OrigSym() != nil {
+ assert(base.Flag.G > 0)
+ // This is an instantiated type - could be a re-instantiation like
+ // Value[T2] or a full instantiation like Value[int].
+ if strings.Index(s.Name, "[") < 0 {
+ base.Fatalf("incorrect name for instantiated type")
+ }
+ w.startType(instanceType)
+ w.pos(t.Pos())
+ // Export the type arguments for the instantiated type. The
+ // instantiated type could be in a method header (e.g. "func (v
+ // *Value[T2]) set (...) { ... }"), so the type args are "new"
+ // typeparams. Or the instantiated type could be in a
+ // function/method body, so the type args are either concrete
+ // types or existing typeparams from the function/method header.
+ w.typeList(t.RParams())
+ // Export a reference to the base type.
+ baseType := t.OrigSym().Def.(*ir.Name).Type()
+ w.typ(baseType)
+ return
+ }
+
+ // The 't.Underlying() == t' check is to confirm this is a base typeparam
+ // type, rather than a defined type with typeparam underlying type, like:
+ // type orderedAbs[T any] T
+ if t.IsTypeParam() && t.Underlying() == t {
+ assert(base.Flag.G > 0)
+ if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg {
+ base.Fatalf("builtin type missing from typIndex: %v", t)
+ }
+ // Write out the first use of a type param as a qualified ident.
+ // This will force a "declaration" of the type param.
+ w.startType(typeParamType)
+ w.qualifiedIdent(t.Obj().(*ir.Name))
+ return
+ }
+
+ if s != nil {
+ if s.Pkg == types.BuiltinPkg || s.Pkg == types.UnsafePkg {
base.Fatalf("builtin type missing from typIndex: %v", t)
}
@@ -865,6 +1021,14 @@ func (w *exportWriter) doTyp(t *types.Type) {
}
}
+ // Sort methods and embedded types, for consistency with types2.
+ // Note: embedded types may be anonymous, and types2 sorts them
+ // with sort.Stable too.
+ if base.Debug.UnifiedQuirks != 0 {
+ sort.Sort(types.MethodsByName(methods))
+ sort.Stable(types.EmbeddedsByName(embeddeds))
+ }
+
w.startType(interfaceType)
w.setPkg(t.Pkg(), true)
@@ -881,6 +1045,19 @@ func (w *exportWriter) doTyp(t *types.Type) {
w.signature(f.Type)
}
+ case types.TUNION:
+ assert(base.Flag.G > 0)
+ // TODO(danscales): possibly put out the tilde bools in more
+ // compact form.
+ w.startType(unionType)
+ nt := t.NumTerms()
+ w.uint64(uint64(nt))
+ for i := 0; i < nt; i++ {
+ typ, tilde := t.Term(i)
+ w.bool(tilde)
+ w.typ(typ)
+ }
+
default:
base.Fatalf("unexpected type: %v", t)
}
@@ -906,6 +1083,23 @@ func (w *exportWriter) signature(t *types.Type) {
}
}
+func (w *exportWriter) typeList(ts []*types.Type) {
+ w.uint64(uint64(len(ts)))
+ for _, rparam := range ts {
+ w.typ(rparam)
+ }
+}
+
+func (w *exportWriter) tparamList(fs []*types.Field) {
+ w.uint64(uint64(len(fs)))
+ for _, f := range fs {
+ if !f.Type.IsTypeParam() {
+ base.Fatalf("unexpected non-typeparam")
+ }
+ w.typ(f.Type)
+ }
+}
+
func (w *exportWriter) paramList(fs []*types.Field) {
w.uint64(uint64(len(fs)))
for _, f := range fs {
@@ -948,26 +1142,57 @@ func constTypeOf(typ *types.Type) constant.Kind {
}
func (w *exportWriter) value(typ *types.Type, v constant.Value) {
- ir.AssertValidTypeForConst(typ, v)
w.typ(typ)
- // Each type has only one admissible constant representation,
- // so we could type switch directly on v.U here. However,
- // switching on the type increases symmetry with import logic
- // and provides a useful consistency check.
+ if iexportVersionCurrent >= iexportVersionGo1_18 {
+ w.int64(int64(v.Kind()))
+ }
+
+ var kind constant.Kind
+ var valType *types.Type
- switch constTypeOf(typ) {
+ if typ.IsTypeParam() {
+ kind = v.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:
+ valType = types.Types[types.TINT64]
+ case constant.Float:
+ valType = types.Types[types.TFLOAT64]
+ case constant.Complex:
+ valType = types.Types[types.TCOMPLEX128]
+ }
+ } else {
+ ir.AssertValidTypeForConst(typ, v)
+ kind = constTypeOf(typ)
+ valType = typ
+ }
+
+ // Each type has only one admissible constant representation, so we could
+ // type switch directly on v.Kind() here. However, switching on the type
+ // (in the non-typeparam case) increases symmetry with import logic and
+ // provides a useful consistency check.
+
+ switch kind {
case constant.Bool:
w.bool(constant.BoolVal(v))
case constant.String:
w.string(constant.StringVal(v))
case constant.Int:
- w.mpint(v, typ)
+ w.mpint(v, valType)
case constant.Float:
- w.mpfloat(v, typ)
+ w.mpfloat(v, valType)
case constant.Complex:
- w.mpfloat(constant.Real(v), typ)
- w.mpfloat(constant.Imag(v), typ)
+ w.mpfloat(constant.Real(v), valType)
+ w.mpfloat(constant.Imag(v), valType)
}
}
@@ -1185,10 +1410,20 @@ func (w *exportWriter) funcExt(n *ir.Name) {
}
}
- // Inline body.
+ // Write out inline body or body of a generic function/method.
+ if n.Type().HasTParam() && n.Func.Body != nil && n.Func.Inl == nil {
+ base.FatalfAt(n.Pos(), "generic function is not marked inlineable")
+ }
if n.Func.Inl != nil {
w.uint64(1 + uint64(n.Func.Inl.Cost))
- if n.Func.ExportInline() {
+ w.bool(n.Func.Inl.CanDelayResults)
+ if n.Func.ExportInline() || n.Type().HasTParam() {
+ if n.Type().HasTParam() {
+ // If this generic function/method is from another
+ // package, but we didn't use for instantiation in
+ // this package, we may not yet have imported it.
+ ImportedBody(n.Func)
+ }
w.p.doInline(n)
}
@@ -1270,10 +1505,23 @@ func (w *exportWriter) node(n ir.Node) {
}
}
-// Caution: stmt will emit more than one node for statement nodes n that have a non-empty
-// n.Ninit and where n cannot have a natural init section (such as in "if", "for", etc.).
+func isNonEmptyAssign(n ir.Node) bool {
+ switch n.Op() {
+ case ir.OAS:
+ if n.(*ir.AssignStmt).Y != nil {
+ return true
+ }
+ case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
+ return true
+ }
+ return false
+}
+
+// Caution: stmt will emit more than one node for statement nodes n that have a
+// non-empty n.Ninit and where n is not a non-empty assignment or a node with a natural init
+// section (such as in "if", "for", etc.).
func (w *exportWriter) stmt(n ir.Node) {
- if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) {
+ if len(n.Init()) > 0 && !ir.StmtWithInit(n.Op()) && !isNonEmptyAssign(n) && n.Op() != ir.ORANGE {
// can't use stmtList here since we don't want the final OEND
for _, n := range n.Init() {
w.stmt(n)
@@ -1309,8 +1557,10 @@ func (w *exportWriter) stmt(n ir.Node) {
if n.Y != nil {
w.op(ir.OAS)
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.expr(n.X)
w.expr(n.Y)
+ w.bool(n.Def)
}
case ir.OASOP:
@@ -1331,8 +1581,10 @@ func (w *exportWriter) stmt(n ir.Node) {
w.op(ir.OAS2)
}
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.exprList(n.Lhs)
w.exprList(n.Rhs)
+ w.bool(n.Def)
case ir.ORETURN:
n := n.(*ir.ReturnStmt)
@@ -1370,6 +1622,7 @@ func (w *exportWriter) stmt(n ir.Node) {
n := n.(*ir.RangeStmt)
w.op(ir.ORANGE)
w.pos(n.Pos())
+ w.stmtList(n.Init())
w.exprsOrNil(n.Key, n.Value)
w.expr(n.X)
w.stmtList(n.Body)
@@ -1432,7 +1685,12 @@ func (w *exportWriter) commList(cases []*ir.CommClause) {
w.uint64(uint64(len(cases)))
for _, cas := range cases {
w.pos(cas.Pos())
- w.node(cas.Comm)
+ defaultCase := cas.Comm == nil
+ w.bool(defaultCase)
+ if !defaultCase {
+ // Only call w.node for non-default cause (cas.Comm is non-nil)
+ w.node(cas.Comm)
+ }
w.stmtList(cas.Body)
}
}
@@ -1460,7 +1718,9 @@ func (w *exportWriter) expr(n ir.Node) {
// (somewhat closely following the structure of exprfmt in fmt.go)
case ir.ONIL:
n := n.(*ir.NilExpr)
- if !n.Type().HasNil() {
+ // If n is a typeparam, it will have already been checked
+ // for proper use by the types2 typechecker.
+ if !n.Type().IsTypeParam() && !n.Type().HasNil() {
base.Fatalf("unexpected type for nil: %v", n.Type())
}
w.op(ir.ONIL)
@@ -1469,7 +1729,11 @@ func (w *exportWriter) expr(n ir.Node) {
case ir.OLITERAL:
w.op(ir.OLITERAL)
- w.pos(n.Pos())
+ if ir.HasUniquePos(n) {
+ w.pos(n.Pos())
+ } else {
+ w.pos(src.NoXPos)
+ }
w.value(n.Type(), n.Val())
case ir.ONAME:
@@ -1477,6 +1741,8 @@ func (w *exportWriter) expr(n ir.Node) {
n := n.(*ir.Name)
if (n.Class == ir.PEXTERN || n.Class == ir.PFUNC) && !ir.IsBlank(n) {
w.op(ir.ONONAME)
+ // Indicate that this is not an OKEY entry.
+ w.bool(false)
w.qualifiedIdent(n)
if go117ExportTypes {
w.typ(n.Type())
@@ -1488,15 +1754,51 @@ func (w *exportWriter) expr(n ir.Node) {
// We don't need a type here, as the type will be provided at the
// declaration of n.
w.op(ir.ONAME)
+
+ // This handles the case where we haven't yet transformed a call
+ // to a builtin, so we must write out the builtin as a name in the
+ // builtin package.
+ isBuiltin := n.BuiltinOp != ir.OXXX
+ w.bool(isBuiltin)
+ if isBuiltin {
+ w.bool(n.Sym().Pkg == types.UnsafePkg)
+ w.string(n.Sym().Name)
+ break
+ }
w.localName(n)
- // case OPACK, ONONAME:
+ case ir.ONONAME:
+ w.op(ir.ONONAME)
+ // This can only be for OKEY nodes in generic functions. Mark it
+ // as a key entry.
+ w.bool(true)
+ s := n.Sym()
+ w.string(s.Name)
+ w.pkg(s.Pkg)
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
+
+ // case OPACK:
// should have been resolved by typechecking - handled by default case
case ir.OTYPE:
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)
@@ -1562,7 +1864,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.typ(n.Type())
w.fieldList(n.List) // special handling of field names
- case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
+ case ir.OCOMPLIT, ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
n := n.(*ir.CompLitExpr)
if go117ExportTypes {
w.op(n.Op())
@@ -1585,12 +1887,11 @@ func (w *exportWriter) expr(n ir.Node) {
// case OSTRUCTKEY:
// unreachable - handled in case OSTRUCTLIT by elemList
- case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
+ case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
n := n.(*ir.SelectorExpr)
if go117ExportTypes {
- if n.Op() == ir.OXDOT {
- base.Fatalf("shouldn't encounter XDOT in new exporter")
- }
+ // For go117ExportTypes, we usually see all ops except
+ // OXDOT, but we can see OXDOT for generic functions.
w.op(n.Op())
} else {
w.op(ir.OXDOT)
@@ -1600,11 +1901,16 @@ func (w *exportWriter) expr(n ir.Node) {
w.exoticSelector(n.Sel)
if go117ExportTypes {
w.exoticType(n.Type())
- if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER {
+ if n.Op() == ir.OXDOT {
+ // n.Selection for method references will be
+ // reconstructed during import.
+ w.bool(n.Selection != nil)
+ } else if n.Op() == ir.ODOT || n.Op() == ir.ODOTPTR || n.Op() == ir.ODOTINTER {
w.exoticField(n.Selection)
}
- // n.Selection is not required for OMETHEXPR, ODOTMETH, and OCALLPART. It will
- // be reconstructed during import.
+ // n.Selection is not required for OMETHEXPR, ODOTMETH, and OMETHVALUE. It will
+ // be reconstructed during import. n.Selection is computed during
+ // transformDot() for OXDOT.
}
case ir.ODOTTYPE, ir.ODOTTYPE2:
@@ -1618,6 +1924,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 {
@@ -1629,7 +1943,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.X)
w.expr(n.Index)
if go117ExportTypes {
- w.typ(n.Type())
+ w.exoticType(n.Type())
if n.Op() == ir.OINDEXMAP {
w.bool(n.Assigned)
}
@@ -1677,7 +1991,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.op(ir.OEND)
}
- case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
+ case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
n := n.(*ir.ConvExpr)
if go117ExportTypes {
w.op(n.Op())
@@ -1732,7 +2046,6 @@ func (w *exportWriter) expr(n ir.Node) {
w.bool(n.IsDDD)
if go117ExportTypes {
w.exoticType(n.Type())
- w.uint64(uint64(n.Use))
}
case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
@@ -1759,8 +2072,16 @@ func (w *exportWriter) expr(n ir.Node) {
w.op(ir.OEND)
}
+ case ir.OLINKSYMOFFSET:
+ n := n.(*ir.LinksymOffsetExpr)
+ w.op(ir.OLINKSYMOFFSET)
+ w.pos(n.Pos())
+ w.string(n.Linksym.Name)
+ w.uint64(uint64(n.Offset_))
+ w.typ(n.Type())
+
// unary expressions
- case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
n := n.(*ir.UnaryExpr)
w.op(n.Op())
w.pos(n.Pos())
@@ -1796,7 +2117,7 @@ func (w *exportWriter) expr(n ir.Node) {
// binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
- ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
+ ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
n := n.(*ir.BinaryExpr)
w.op(n.Op())
w.pos(n.Pos())
@@ -1829,6 +2150,28 @@ func (w *exportWriter) expr(n ir.Node) {
// if exporting, DCLCONST should just be removed as its usage
// has already been replaced with literals
+ case ir.OFUNCINST:
+ n := n.(*ir.InstExpr)
+ w.op(ir.OFUNCINST)
+ w.pos(n.Pos())
+ w.expr(n.X)
+ w.uint64(uint64(len(n.Targs)))
+ for _, targ := range n.Targs {
+ w.typ(targ.Type())
+ }
+ if go117ExportTypes {
+ w.typ(n.Type())
+ }
+
+ case ir.OSELRECV2:
+ n := n.(*ir.AssignListStmt)
+ w.op(ir.OSELRECV2)
+ w.pos(n.Pos())
+ w.stmtList(n.Init())
+ w.exprList(n.Lhs)
+ w.exprList(n.Rhs)
+ w.bool(n.Def)
+
default:
base.Fatalf("cannot export %v (%d) node\n"+
"\t==> please file an issue and assign to gri@", n.Op(), int(n.Op()))
@@ -1864,11 +2207,8 @@ func (w *exportWriter) fieldList(list ir.Nodes) {
for _, n := range list {
n := n.(*ir.StructKeyExpr)
w.pos(n.Pos())
- w.selector(n.Field)
+ w.exoticField(n.Field)
w.expr(n.Value)
- if go117ExportTypes {
- w.uint64(uint64(n.Offset))
- }
}
}
@@ -1902,8 +2242,15 @@ func (w *exportWriter) localIdent(s *types.Sym) {
return
}
- // TODO(mdempsky): Fix autotmp hack.
- if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") {
+ // The name of autotmp variables isn't important; they just need to
+ // be unique. To stabilize the export data, simply write out "$" as
+ // a marker and let the importer generate its own unique name.
+ if strings.HasPrefix(name, ".autotmp_") {
+ w.string("$autotmp")
+ return
+ }
+
+ if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, LocalDictName) {
base.Fatalf("unexpected dot in identifier: %v", name)
}
@@ -1939,3 +2286,6 @@ func (w *intWriter) uint64(x uint64) {
// information (e.g. length field for OSLICELIT).
const go117ExportTypes = true
const Go117ExportTypes = go117ExportTypes
+
+// The name used for dictionary parameters or local variables.
+const LocalDictName = ".dict"
diff --git a/src/cmd/compile/internal/typecheck/iimport.go b/src/cmd/compile/internal/typecheck/iimport.go
index 37f5a7bba0ac9a351c2373c969e8dce025b57185..09f87df5803eec2bfac3b2c1c00b1947a6ff3d59 100644
--- a/src/cmd/compile/internal/typecheck/iimport.go
+++ b/src/cmd/compile/internal/typecheck/iimport.go
@@ -11,7 +11,6 @@ import (
"encoding/binary"
"fmt"
"go/constant"
- "io"
"math/big"
"os"
"strings"
@@ -19,8 +18,6 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
- "cmd/internal/bio"
- "cmd/internal/goobj"
"cmd/internal/obj"
"cmd/internal/src"
)
@@ -84,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 {
@@ -94,7 +112,7 @@ func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset
}
type intReader struct {
- *bio.Reader
+ *strings.Reader
pkg *types.Pkg
}
@@ -116,33 +134,30 @@ func (r *intReader) uint64() uint64 {
return i
}
-func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintType) {
- ird := &intReader{in, pkg}
+func ReadImports(pkg *types.Pkg, data string) {
+ ird := &intReader{strings.NewReader(data), pkg}
version := ird.uint64()
- if version != iexportVersion {
+ switch version {
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
+ default:
base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
base.ErrorExit()
}
- sLen := ird.uint64()
- dLen := ird.uint64()
-
- // Map string (and data) section into memory as a single large
- // string. This reduces heap fragmentation and allows
- // returning individual substrings very efficiently.
- data, err := mapFile(in.File(), in.Offset(), int64(sLen+dLen))
- if err != nil {
- base.Errorf("import %q: mapping input: %v", pkg.Path, err)
- base.ErrorExit()
- }
- stringData := data[:sLen]
- declData := data[sLen:]
+ sLen := int64(ird.uint64())
+ dLen := int64(ird.uint64())
- in.MustSeek(int64(sLen+dLen), os.SEEK_CUR)
+ // TODO(mdempsky): Replace os.SEEK_CUR with io.SeekCurrent after
+ // #44505 is fixed.
+ whence, _ := ird.Seek(0, os.SEEK_CUR)
+ stringData := data[whence : whence+sLen]
+ declData := data[whence+sLen : whence+sLen+dLen]
+ ird.Seek(sLen+dLen, os.SEEK_CUR)
p := &iimporter{
- ipkg: pkg,
+ exportVersion: version,
+ ipkg: pkg,
pkgCache: map[uint64]*types.Pkg{},
posBaseCache: map[uint64]*src.PosBase{},
@@ -178,7 +193,7 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT
}
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 {
@@ -192,7 +207,7 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT
pkg := p.pkgAt(ird.uint64())
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 := inlineImporter[s]; !ok {
@@ -200,18 +215,11 @@ func ReadImports(pkg *types.Pkg, in *bio.Reader) (fingerprint goobj.FingerprintT
}
}
}
-
- // Fingerprint.
- _, err = io.ReadFull(in, fingerprint[:])
- if err != nil {
- base.Errorf("import %s: error reading fingerprint", pkg.Path)
- base.ErrorExit()
- }
- return fingerprint
}
type iimporter struct {
- ipkg *types.Pkg
+ exportVersion uint64
+ ipkg *types.Pkg
pkgCache map[uint64]*types.Pkg
posBaseCache map[uint64]*src.PosBase
@@ -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
@@ -273,6 +297,7 @@ type importReader struct {
// Slice of all dcls for function, including any interior closures
allDcls []*ir.Name
allClosureVars []*ir.Name
+ autotmpgen int
}
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
@@ -287,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()) }
@@ -302,37 +328,49 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
case 'A':
typ := r.typ()
- return importalias(r.p.ipkg, pos, sym, typ)
+ return importalias(pos, sym, typ)
case 'C':
typ := r.typ()
val := r.value(typ)
- n := importconst(r.p.ipkg, pos, sym, typ, val)
+ n := importconst(pos, sym, typ, val)
r.constExt(n)
return n
- case 'F':
- typ := r.signature(nil)
+ case 'F', 'G':
+ var tparams []*types.Field
+ if tag == 'G' {
+ tparams = r.tparamList()
+ }
+ typ := r.signature(nil, tparams)
- n := importfunc(r.p.ipkg, pos, sym, typ)
+ n := importfunc(pos, sym, typ)
r.funcExt(n)
return n
- case 'T':
+ case 'T', 'U':
// Types can be recursive. We need to setup a stub
// declaration before recursing.
- n := importtype(r.p.ipkg, pos, sym)
+ n := importtype(pos, sym)
t := n.Type()
+ if tag == 'U' {
+ rparams := r.typeList()
+ t.SetRParams(rparams)
+ }
// We also need to defer width calculations until
// after the underlying type has been assigned.
types.DeferCheckSize()
+ deferDoInst()
underlying := r.typ()
t.SetUnderlying(underlying)
- types.ResumeCheckSize()
if underlying.IsInterface() {
+ // Finish up all type instantiations and CheckSize calls
+ // now that a top-level type is fully constructed.
+ resumeDoInst()
+ types.ResumeCheckSize()
r.typeExt(t)
return n
}
@@ -342,7 +380,7 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
mpos := r.pos()
msym := r.selector()
recv := r.param()
- mtyp := r.signature(recv)
+ mtyp := r.signature(recv, nil)
// MethodSym already marked m.Sym as a function.
m := ir.NewNameAt(mpos, ir.MethodSym(recv.Type, msym))
@@ -358,16 +396,50 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
}
t.Methods().Set(ms)
+ // Finish up all instantiations and CheckSize calls now
+ // that a top-level type is fully constructed.
+ resumeDoInst()
+ types.ResumeCheckSize()
+
r.typeExt(t)
for _, m := range ms {
r.methExt(m)
}
return n
+ case 'P':
+ if r.p.exportVersion < iexportVersionGenerics {
+ base.Fatalf("unexpected type param type")
+ }
+ if sym.Def != nil {
+ // Make sure we use the same type param type for the same
+ // name, whether it is created during types1-import or
+ // this types2-to-types1 translation.
+ return sym.Def.(*ir.Name)
+ }
+ // The typeparam index is set at the point where the containing type
+ // param list is imported.
+ t := types.NewTypeParam(sym, 0)
+ // Nname needed to save the pos.
+ nname := ir.NewDeclNameAt(pos, ir.OTYPE, sym)
+ sym.Def = nname
+ nname.SetType(t)
+ t.SetNod(nname)
+ 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':
typ := r.typ()
- n := importvar(r.p.ipkg, pos, sym, typ)
+ n := importvar(pos, sym, typ)
r.varExt(n)
return n
@@ -377,27 +449,54 @@ func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
}
}
-func (p *importReader) value(typ *types.Type) constant.Value {
- switch constTypeOf(typ) {
+func (r *importReader) value(typ *types.Type) constant.Value {
+ var kind constant.Kind
+ var valType *types.Type
+
+ 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]
+ case constant.Float:
+ valType = types.Types[types.TFLOAT64]
+ case constant.Complex:
+ valType = types.Types[types.TCOMPLEX128]
+ }
+ } else {
+ kind = constTypeOf(typ)
+ valType = typ
+ }
+
+ switch kind {
case constant.Bool:
- return constant.MakeBool(p.bool())
+ return constant.MakeBool(r.bool())
case constant.String:
- return constant.MakeString(p.string())
+ return constant.MakeString(r.string())
case constant.Int:
var i big.Int
- p.mpint(&i, typ)
+ r.mpint(&i, valType)
return constant.Make(&i)
case constant.Float:
- return p.float(typ)
+ return r.float(valType)
case constant.Complex:
- return makeComplex(p.float(typ), p.float(typ))
+ return makeComplex(r.float(valType), r.float(valType))
}
base.Fatalf("unexpected value type: %v", typ)
panic("unreachable")
}
-func (p *importReader) mpint(x *big.Int, typ *types.Type) {
+func (r *importReader) mpint(x *big.Int, typ *types.Type) {
signed, maxBytes := intSize(typ)
maxSmall := 256 - maxBytes
@@ -408,7 +507,7 @@ func (p *importReader) mpint(x *big.Int, typ *types.Type) {
maxSmall = 256
}
- n, _ := p.ReadByte()
+ n, _ := r.ReadByte()
if uint(n) < maxSmall {
v := int64(n)
if signed {
@@ -429,30 +528,30 @@ func (p *importReader) mpint(x *big.Int, typ *types.Type) {
base.Fatalf("weird decoding: %v, %v => %v", n, signed, v)
}
b := make([]byte, v)
- p.Read(b)
+ r.Read(b)
x.SetBytes(b)
if signed && n&1 != 0 {
x.Neg(x)
}
}
-func (p *importReader) float(typ *types.Type) constant.Value {
+func (r *importReader) float(typ *types.Type) constant.Value {
var mant big.Int
- p.mpint(&mant, typ)
+ r.mpint(&mant, typ)
var f big.Float
f.SetInt(&mant)
if f.Sign() != 0 {
- f.SetMantExp(&f, int(p.int64()))
+ f.SetMantExp(&f, int(r.int64()))
}
return constant.Make(&f)
}
-func (p *importReader) mprat(orig constant.Value) constant.Value {
- if !p.bool() {
+func (r *importReader) mprat(orig constant.Value) constant.Value {
+ if !r.bool() {
return orig
}
var rat big.Rat
- rat.SetString(p.string())
+ rat.SetString(r.string())
return constant.Make(&rat)
}
@@ -462,8 +561,15 @@ func (r *importReader) ident(selector bool) *types.Sym {
return nil
}
pkg := r.currPkg
- if selector && types.IsExported(name) {
- pkg = types.LocalPkg
+ if selector {
+ if types.IsExported(name) {
+ pkg = types.LocalPkg
+ }
+ } else {
+ if name == "$autotmp" {
+ name = autotmpname(r.autotmpgen)
+ r.autotmpgen++
+ }
}
return pkg.Lookup(name)
}
@@ -472,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)
@@ -503,7 +609,14 @@ func (r *importReader) pos() src.XPos {
}
func (r *importReader) typ() *types.Type {
- return r.p.typAt(r.uint64())
+ // If this is a top-level type call, defer type instantiations until the
+ // type is fully constructed.
+ types.DeferCheckSize()
+ deferDoInst()
+ t := r.p.typAt(r.uint64())
+ resumeDoInst()
+ types.ResumeCheckSize()
+ return t
}
func (r *importReader) exoticType() *types.Type {
@@ -538,7 +651,7 @@ func (r *importReader) exoticType() *types.Type {
case exoticTypeRecv:
var rcvr *types.Field
if r.bool() { // isFakeRecv
- rcvr = fakeRecvField()
+ rcvr = types.FakeRecv()
} else {
rcvr = r.exoticParam()
}
@@ -641,7 +754,13 @@ func (p *iimporter) typAt(off uint64) *types.Type {
// are pushed to compile queue, then draining from the queue for compiling.
// During this process, the size calculation is disabled, so it is not safe for
// calculating size during SSA generation anymore. See issue #44732.
- types.CheckSize(t)
+ //
+ // No need to calc sizes for re-instantiated generic types, and
+ // they are not necessarily resolved until the top-level type is
+ // defined (because of recursive types).
+ if t.OrigSym() == nil || !t.HasTParam() {
+ types.CheckSize(t)
+ }
p.typCache[off] = t
}
return t
@@ -680,7 +799,7 @@ func (r *importReader) typ1() *types.Type {
case signatureType:
r.setPkg()
- return r.signature(nil)
+ return r.signature(nil, nil)
case structType:
r.setPkg()
@@ -718,16 +837,64 @@ func (r *importReader) typ1() *types.Type {
for i := range methods {
pos := r.pos()
sym := r.selector()
- typ := r.signature(fakeRecvField())
+ typ := r.signature(types.FakeRecv(), nil)
methods[i] = types.NewField(pos, sym, typ)
}
- t := types.NewInterface(r.currPkg, append(embeddeds, methods...))
+ if len(embeddeds)+len(methods) == 0 {
+ return types.Types[types.TINTER]
+ }
+
+ t := types.NewInterface(r.currPkg, append(embeddeds, methods...), false)
// Ensure we expand the interface in the frontend (#25055).
types.CheckSize(t)
return t
+
+ case typeParamType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ base.Fatalf("unexpected type param type")
+ }
+ // Similar to code for defined types, since we "declared"
+ // typeparams to deal with recursion (typeparam is used within its
+ // own type bound).
+ ident := r.qualifiedIdent()
+ if ident.Sym().Def != nil {
+ return ident.Sym().Def.(*ir.Name).Type()
+ }
+ n := expandDecl(ident)
+ if n.Op() != ir.OTYPE {
+ base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
+ }
+ return n.Type()
+
+ case instanceType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ base.Fatalf("unexpected instantiation type")
+ }
+ pos := r.pos()
+ len := r.uint64()
+ targs := make([]*types.Type, len)
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+ baseType := r.typ()
+ t := Instantiate(pos, baseType, targs)
+ return t
+
+ case unionType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ base.Fatalf("unexpected instantiation type")
+ }
+ nt := int(r.uint64())
+ terms := make([]*types.Type, nt)
+ tildes := make([]bool, nt)
+ for i := range terms {
+ tildes[i] = r.bool()
+ terms[i] = r.typ()
+ }
+ return types.NewUnion(terms, tildes)
}
}
@@ -735,13 +902,42 @@ func (r *importReader) kind() itag {
return itag(r.uint64())
}
-func (r *importReader) signature(recv *types.Field) *types.Type {
+func (r *importReader) signature(recv *types.Field, tparams []*types.Field) *types.Type {
params := r.paramList()
results := r.paramList()
if n := len(params); n > 0 {
params[n-1].SetIsDDD(r.bool())
}
- return types.NewSignature(r.currPkg, recv, nil, params, results)
+ return types.NewSignature(r.currPkg, recv, tparams, params, results)
+}
+
+func (r *importReader) typeList() []*types.Type {
+ n := r.uint64()
+ if n == 0 {
+ return nil
+ }
+ ts := make([]*types.Type, n)
+ for i := range ts {
+ ts[i] = r.typ()
+ if ts[i].IsTypeParam() {
+ ts[i].SetIndex(i)
+ }
+ }
+ return ts
+}
+
+func (r *importReader) tparamList() []*types.Field {
+ n := r.uint64()
+ if n == 0 {
+ return nil
+ }
+ fs := make([]*types.Field, n)
+ for i := range fs {
+ typ := r.typ()
+ typ.SetIndex(i)
+ fs[i] = types.NewField(typ.Pos(), typ.Sym(), typ)
+ }
+ return fs
}
func (r *importReader) paramList() []*types.Field {
@@ -809,7 +1005,9 @@ func (r *importReader) funcExt(n *ir.Name) {
n.Func.ABI = obj.ABI(r.uint64())
- n.SetPragma(ir.PragmaFlag(r.uint64()))
+ // Make sure //go:noinline pragma is imported (so stenciled functions have
+ // same noinline status as the corresponding generic function.)
+ n.Func.Pragma = ir.PragmaFlag(r.uint64())
// Escape analysis.
for _, fs := range &types.RecvsParams {
@@ -821,7 +1019,8 @@ func (r *importReader) funcExt(n *ir.Name) {
// Inline body.
if u := r.uint64(); u > 0 {
n.Func.Inl = &ir.Inline{
- Cost: int32(u - 1),
+ Cost: int32(u - 1),
+ CanDelayResults: r.bool(),
}
n.Func.Endlineno = r.pos()
}
@@ -852,7 +1051,13 @@ func (r *importReader) symIdx(s *types.Sym) {
func (r *importReader) typeExt(t *types.Type) {
t.SetNotInHeap(r.bool())
- i, pi := r.int64(), r.int64()
+ SetBaseTypeIndex(t, r.int64(), r.int64())
+}
+
+func SetBaseTypeIndex(t *types.Type, i, pi int64) {
+ if t.Obj() == nil {
+ base.Fatalf("SetBaseTypeIndex on non-defined type %v", t)
+ }
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
}
@@ -860,6 +1065,7 @@ func (r *importReader) typeExt(t *types.Type) {
// Map imported type T to the index of type descriptor symbols of T and *T,
// so we can use index to reference the symbol.
+// TODO(mdempsky): Store this information directly in the Type's Name.
var typeSymIdx = make(map[*types.Type][2]int64)
func BaseTypeIndex(t *types.Type) int64 {
@@ -936,6 +1142,10 @@ func (r *importReader) funcBody(fn *ir.Func) {
fn.Inl.Body = body
r.curfn = outerfn
+ if base.Flag.W >= 3 {
+ fmt.Printf("Imported for %v", fn)
+ ir.DumpList("", fn.Inl.Body)
+ }
}
func (r *importReader) readNames(fn *ir.Func) []*ir.Name {
@@ -1004,10 +1214,26 @@ func (r *importReader) stmtList() []ir.Node {
if n.Op() == ir.OBLOCK {
n := n.(*ir.BlockStmt)
list = append(list, n.List...)
- } else {
- list = append(list, n)
+ continue
}
-
+ if len(list) > 0 {
+ // check for an optional label that can only immediately
+ // precede a for/range/select/switch statement.
+ if last := list[len(list)-1]; last.Op() == ir.OLABEL {
+ label := last.(*ir.LabelStmt).Label
+ switch n.Op() {
+ case ir.OFOR:
+ n.(*ir.ForStmt).Label = label
+ case ir.ORANGE:
+ n.(*ir.RangeStmt).Label = label
+ case ir.OSELECT:
+ n.(*ir.SelectStmt).Label = label
+ case ir.OSWITCH:
+ n.(*ir.SwitchStmt).Label = label
+ }
+ }
+ }
+ list = append(list, n)
}
return list
}
@@ -1032,7 +1258,13 @@ func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
func (r *importReader) commList() []*ir.CommClause {
cases := make([]*ir.CommClause, r.uint64())
for i := range cases {
- cases[i] = ir.NewCommStmt(r.pos(), r.node(), r.stmtList())
+ pos := r.pos()
+ defaultCase := r.bool()
+ var comm ir.Node
+ if !defaultCase {
+ comm = r.node()
+ }
+ cases[i] = ir.NewCommStmt(pos, comm, r.stmtList())
}
return cases
}
@@ -1083,9 +1315,15 @@ func (r *importReader) node() ir.Node {
return n
case ir.ONONAME:
+ isKey := r.bool()
n := r.qualifiedIdent()
if go117ExportTypes {
- n2 := Resolve(n)
+ var n2 ir.Node = n
+ // Key ONONAME entries should not be resolved - they should
+ // stay as identifiers.
+ if !isKey {
+ n2 = Resolve(n)
+ }
typ := r.typ()
if n2.Type() == nil {
n2.SetType(typ)
@@ -1095,6 +1333,14 @@ func (r *importReader) node() ir.Node {
return n
case ir.ONAME:
+ isBuiltin := r.bool()
+ if isBuiltin {
+ pkg := types.BuiltinPkg
+ if r.bool() {
+ pkg = types.UnsafePkg
+ }
+ return pkg.Lookup(r.string()).Def.(*ir.Name)
+ }
return r.localName()
// case OPACK, ONONAME:
@@ -1103,6 +1349,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
@@ -1117,28 +1371,18 @@ func (r *importReader) node() ir.Node {
case ir.OCLOSURE:
//println("Importing CLOSURE")
pos := r.pos()
- typ := r.signature(nil)
+ typ := r.signature(nil, nil)
// All the remaining code below is similar to (*noder).funcLit(), but
// with Dcls and ClosureVars lists already set up
- fn := ir.NewFunc(pos)
- fn.SetIsHiddenClosure(true)
- fn.Nname = ir.NewNameAt(pos, ir.BlankNode.Sym())
- fn.Nname.Func = fn
- fn.Nname.Ntype = ir.TypeNode(typ)
- fn.Nname.Defn = fn
+ fn := ir.NewClosureFunc(pos, true)
fn.Nname.SetType(typ)
cvars := make([]*ir.Name, r.int64())
for i := range cvars {
cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical())
- if go117ExportTypes {
- if cvars[i].Type() != nil || cvars[i].Defn == nil {
- base.Fatalf("bad import of closure variable")
- }
- // Closure variable should have Defn set, which is its captured
- // variable, and it gets the same type as the captured variable.
- cvars[i].SetType(cvars[i].Defn.Type())
+ if go117ExportTypes && cvars[i].Defn == nil {
+ base.Fatalf("bad import of closure variable")
}
}
fn.ClosureVars = cvars
@@ -1159,12 +1403,10 @@ func (r *importReader) node() ir.Node {
ir.FinishCaptureNames(pos, r.curfn, fn)
- clo := ir.NewClosureExpr(pos, fn)
- fn.OClosure = clo
+ clo := fn.OClosure
if go117ExportTypes {
clo.SetType(typ)
}
-
return clo
case ir.OSTRUCTLIT:
@@ -1179,7 +1421,11 @@ func (r *importReader) node() ir.Node {
return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.fieldList())
case ir.OCOMPLIT:
- return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.exprList())
+ pos := r.pos()
+ t := r.typ()
+ n := ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(t), r.exprList())
+ n.SetType(t)
+ return n
case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
if !go117ExportTypes {
@@ -1202,35 +1448,56 @@ func (r *importReader) node() ir.Node {
// case OSTRUCTKEY:
// unreachable - handled in case OSTRUCTLIT by elemList
- case ir.OXDOT:
- // see parser.new_dotname
- if go117ExportTypes {
- base.Fatalf("shouldn't encounter XDOT in new importer")
- }
- return ir.NewSelectorExpr(r.pos(), ir.OXDOT, r.expr(), r.exoticSelector())
-
- case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
- if !go117ExportTypes {
- // unreachable - mapped to case OXDOT by exporter
+ case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
+ // For !go117ExportTypes, we should only see OXDOT.
+ // For go117ExportTypes, we usually see all the other ops, but can see
+ // OXDOT for generic functions.
+ if op != ir.OXDOT && !go117ExportTypes {
goto error
}
pos := r.pos()
expr := r.expr()
sel := r.exoticSelector()
n := ir.NewSelectorExpr(pos, op, expr, sel)
- n.SetType(r.exoticType())
- switch op {
- case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER:
- n.Selection = r.exoticField()
- case ir.ODOTMETH, ir.OCALLPART, ir.OMETHEXPR:
- // These require a Lookup to link to the correct declaration.
- rcvrType := expr.Type()
- typ := n.Type()
- n.Selection = Lookdot(n, rcvrType, 1)
- if op == ir.OCALLPART || op == ir.OMETHEXPR {
- // Lookdot clobbers the opcode and type, undo that.
- n.SetOp(op)
- n.SetType(typ)
+ if go117ExportTypes {
+ n.SetType(r.exoticType())
+ switch op {
+ case ir.OXDOT:
+ hasSelection := r.bool()
+ // We reconstruct n.Selection for method calls on
+ // generic types and method calls due to type param
+ // bounds. Otherwise, n.Selection is nil.
+ if hasSelection {
+ n1 := ir.NewSelectorExpr(pos, op, expr, sel)
+ AddImplicitDots(n1)
+ var m *types.Field
+ if n1.X.Type().IsTypeParam() {
+ genType := n1.X.Type().Bound()
+ m = Lookdot1(n1, sel, genType, genType.AllMethods(), 1)
+ } else {
+ genType := types.ReceiverBaseType(n1.X.Type())
+ if genType.IsInstantiatedGeneric() {
+ genType = genType.OrigSym().Def.Type()
+ }
+ m = Lookdot1(n1, sel, genType, genType.Methods(), 1)
+ }
+ assert(m != nil)
+ n.Selection = m
+ }
+ case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER:
+ n.Selection = r.exoticField()
+ case ir.OMETHEXPR:
+ n = typecheckMethodExpr(n).(*ir.SelectorExpr)
+ case ir.ODOTMETH, ir.OMETHVALUE:
+ // These require a Lookup to link to the correct declaration.
+ rcvrType := expr.Type()
+ typ := n.Type()
+ n.Selection = Lookdot(n, rcvrType, 1)
+ if op == ir.OMETHVALUE {
+ // Lookdot clobbers the opcode and type, undo that.
+ n.SetOp(op)
+ n.SetType(typ)
+ }
}
}
return n
@@ -1243,11 +1510,16 @@ 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 {
n.SetOp(op)
- n.SetType(r.typ())
+ n.SetType(r.exoticType())
if op == ir.OINDEXMAP {
n.Assigned = r.bool()
}
@@ -1267,7 +1539,7 @@ func (r *importReader) node() ir.Node {
}
return n
- case ir.OCONV, ir.OCONVIFACE, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
+ case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
if !go117ExportTypes && op != ir.OCONV {
// unreachable - mapped to OCONV case by exporter
goto error
@@ -1314,11 +1586,10 @@ func (r *importReader) node() ir.Node {
if go117ExportTypes {
n.SetOp(op)
}
- *n.PtrInit() = init
+ n.SetInit(init)
n.IsDDD = r.bool()
if go117ExportTypes {
n.SetType(r.exoticType())
- n.Use = ir.CallUse(r.uint64())
}
return n
@@ -1343,8 +1614,15 @@ func (r *importReader) node() ir.Node {
n.Args.Append(r.exprList()...)
return n
+ case ir.OLINKSYMOFFSET:
+ pos := r.pos()
+ name := r.string()
+ off := r.uint64()
+ typ := r.typ()
+ return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
+
// unary expressions
- case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV:
+ case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
n := ir.NewUnaryExpr(r.pos(), op, r.expr())
if go117ExportTypes {
n.SetType(r.typ())
@@ -1368,7 +1646,7 @@ func (r *importReader) node() ir.Node {
// binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
- ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR:
+ ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
if go117ExportTypes {
n.SetType(r.typ())
@@ -1412,7 +1690,12 @@ func (r *importReader) node() ir.Node {
// unreachable - never exported
case ir.OAS:
- return ir.NewAssignStmt(r.pos(), r.expr(), r.expr())
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignStmt(pos, r.expr(), r.expr())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
case ir.OASOP:
n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
@@ -1429,7 +1712,12 @@ func (r *importReader) node() ir.Node {
// unreachable - mapped to case OAS2 by exporter
goto error
}
- return ir.NewAssignListStmt(r.pos(), op, r.exprList(), r.exprList())
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
case ir.ORETURN:
return ir.NewReturnStmt(r.pos(), r.exprList())
@@ -1443,26 +1731,28 @@ func (r *importReader) node() ir.Node {
case ir.OIF:
pos, init := r.pos(), r.stmtList()
n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.OFOR:
pos, init := r.pos(), r.stmtList()
cond, post := r.exprsOrNil()
n := ir.NewForStmt(pos, nil, cond, post, r.stmtList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.ORANGE:
- pos := r.pos()
+ pos, init := r.pos(), r.stmtList()
k, v := r.exprsOrNil()
- return ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+ n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
+ n.SetInit(init)
+ return n
case ir.OSELECT:
pos := r.pos()
init := r.stmtList()
n := ir.NewSelectStmt(pos, r.commList())
- *n.PtrInit() = init
+ n.SetInit(init)
return n
case ir.OSWITCH:
@@ -1470,7 +1760,7 @@ func (r *importReader) node() ir.Node {
init := r.stmtList()
x, _ := r.exprsOrNil()
n := ir.NewSwitchStmt(pos, x, r.caseList(x))
- *n.PtrInit() = init
+ n.SetInit(init)
return n
// case OCASE:
@@ -1496,6 +1786,31 @@ func (r *importReader) node() ir.Node {
case ir.OEND:
return nil
+ case ir.OFUNCINST:
+ pos := r.pos()
+ x := r.expr()
+ ntargs := r.uint64()
+ var targs []ir.Node
+ if ntargs > 0 {
+ targs = make([]ir.Node, ntargs)
+ for i := range targs {
+ targs[i] = ir.TypeNode(r.typ())
+ }
+ }
+ n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
+ if go117ExportTypes {
+ n.SetType(r.typ())
+ }
+ return n
+
+ case ir.OSELRECV2:
+ pos := r.pos()
+ init := r.stmtList()
+ n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList())
+ n.SetInit(init)
+ n.Def = r.bool()
+ return n
+
default:
base.Fatalf("cannot import %v (%d) node\n"+
"\t==> please file an issue and assign to gri@", op, int(op))
@@ -1517,11 +1832,7 @@ func (r *importReader) op() ir.Op {
func (r *importReader) fieldList() []ir.Node {
list := make([]ir.Node, r.uint64())
for i := range list {
- x := ir.NewStructKeyExpr(r.pos(), r.selector(), r.expr())
- if go117ExportTypes {
- x.Offset = int64(r.uint64())
- }
- list[i] = x
+ list[i] = ir.NewStructKeyExpr(r.pos(), r.exoticField(), r.expr())
}
return list
}
@@ -1544,3 +1855,147 @@ func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
}
return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
}
+
+// NewIncompleteNamedType returns a TFORW type t with name specified by sym, such
+// that t.nod and sym.Def are set correctly. If there are any RParams for the type,
+// they should be set soon after creating the TFORW type, before creating the
+// underlying type. That ensures that the HasTParam and HasShape flags will be set
+// properly, in case this type is part of some mutually recursive type.
+func NewIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type {
+ name := ir.NewDeclNameAt(pos, ir.OTYPE, sym)
+ forw := types.NewNamed(name)
+ name.SetType(forw)
+ sym.Def = name
+ return forw
+}
+
+// Instantiate creates a new named type which is the instantiation of the base
+// named generic type, with the specified type args.
+func Instantiate(pos src.XPos, baseType *types.Type, targs []*types.Type) *types.Type {
+ baseSym := baseType.Sym()
+ if strings.Index(baseSym.Name, "[") >= 0 {
+ base.Fatalf("arg to Instantiate is not a base generic type")
+ }
+ name := InstTypeName(baseSym.Name, targs)
+ instSym := baseSym.Pkg.Lookup(name)
+ if instSym.Def != nil {
+ // May match existing type from previous import or
+ // types2-to-types1 conversion.
+ t := instSym.Def.Type()
+ if t.Kind() != types.TFORW {
+ return t
+ }
+ // Or, we have started creating this type in (*TSubster).Typ, but its
+ // underlying type was not completed yet, so we need to add this type
+ // to deferredInstStack, if not already there.
+ found := false
+ for _, t2 := range deferredInstStack {
+ if t2 == t {
+ found = true
+ break
+ }
+ }
+ if !found {
+ deferredInstStack = append(deferredInstStack, t)
+ }
+ return t
+ }
+
+ t := NewIncompleteNamedType(baseType.Pos(), instSym)
+ t.SetRParams(targs)
+ t.SetOrigSym(baseSym)
+
+ // baseType may still be TFORW or its methods may not be fully filled in
+ // (since we are in the middle of importing it). So, delay call to
+ // substInstType until we get back up to the top of the current top-most
+ // type import.
+ deferredInstStack = append(deferredInstStack, t)
+
+ return t
+}
+
+var deferredInstStack []*types.Type
+var deferInst int
+
+// deferDoInst defers substitution on instantiated types until we are at the
+// top-most defined type, so the base types are fully defined.
+func deferDoInst() {
+ deferInst++
+}
+
+func resumeDoInst() {
+ if deferInst == 1 {
+ for len(deferredInstStack) > 0 {
+ t := deferredInstStack[0]
+ deferredInstStack = deferredInstStack[1:]
+ substInstType(t, t.OrigSym().Def.(*ir.Name).Type(), t.RParams())
+ }
+ }
+ deferInst--
+}
+
+// doInst creates a new instantiation type (which will be added to
+// deferredInstStack for completion later) for an incomplete type encountered
+// during a type substitution for an instantiation. This is needed for
+// instantiations of mutually recursive types.
+func doInst(t *types.Type) *types.Type {
+ assert(t.Kind() == types.TFORW)
+ return Instantiate(t.Pos(), t.OrigSym().Def.(*ir.Name).Type(), t.RParams())
+}
+
+// substInstType completes the instantiation of a generic type by doing a
+// substitution on the underlying type itself and any methods. t is the
+// instantiation being created, baseType is the base generic type, and targs are
+// the type arguments that baseType is being instantiated with.
+func substInstType(t *types.Type, baseType *types.Type, targs []*types.Type) {
+ assert(t.Kind() == types.TFORW)
+ subst := Tsubster{
+ Tparams: baseType.RParams(),
+ Targs: targs,
+ SubstForwFunc: doInst,
+ }
+ t.SetUnderlying(subst.Typ(baseType.Underlying()))
+
+ newfields := make([]*types.Field, baseType.Methods().Len())
+ for i, f := range baseType.Methods().Slice() {
+ if !f.IsMethod() || types.IsInterfaceMethod(f.Type) {
+ // Do a normal substitution if this is a non-method (which
+ // means this must be an interface used as a constraint) or
+ // an interface method.
+ t2 := subst.Typ(f.Type)
+ newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+ continue
+ }
+ recvType := f.Type.Recv().Type
+ if recvType.IsPtr() {
+ recvType = recvType.Elem()
+ }
+ // Substitute in the method using the type params used in the
+ // method (not the type params in the definition of the generic type).
+ msubst := Tsubster{
+ Tparams: recvType.RParams(),
+ Targs: targs,
+ SubstForwFunc: doInst,
+ }
+ t2 := msubst.Typ(f.Type)
+ oldsym := f.Nname.Sym()
+ newsym := MakeFuncInstSym(oldsym, targs, true, true)
+ var nname *ir.Name
+ if newsym.Def != nil {
+ nname = newsym.Def.(*ir.Name)
+ } else {
+ nname = ir.NewNameAt(f.Pos, newsym)
+ nname.SetType(t2)
+ ir.MarkFunc(nname)
+ newsym.Def = nname
+ }
+ newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+ newfields[i].Nname = nname
+ }
+ t.Methods().Set(newfields)
+ if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TINTER && t.Methods().Len() > 0 {
+ // Generate all the methods for a new fully-instantiated,
+ // non-interface, non-shape type.
+ NeedInstType(t)
+ }
+}
diff --git a/src/cmd/compile/internal/typecheck/stmt.go b/src/cmd/compile/internal/typecheck/stmt.go
index 922a01bfbe9aa7d6dea6ab34e03e5955ef883105..9a02c1752caf13f6c89e26866e01ba553a767e62 100644
--- a/src/cmd/compile/internal/typecheck/stmt.go
+++ b/src/cmd/compile/internal/typecheck/stmt.go
@@ -172,6 +172,10 @@ assignOK:
r := r.(*ir.TypeAssertExpr)
stmt.SetOp(ir.OAS2DOTTYPE)
r.SetOp(ir.ODOTTYPE2)
+ case ir.ODYNAMICDOTTYPE:
+ r := r.(*ir.DynamicTypeAssertExpr)
+ stmt.SetOp(ir.OAS2DOTTYPE)
+ r.SetOp(ir.ODYNAMICDOTTYPE2)
default:
break assignOK
}
@@ -201,7 +205,6 @@ assignOK:
stmt := stmt.(*ir.AssignListStmt)
stmt.SetOp(ir.OAS2FUNC)
r := rhs[0].(*ir.CallExpr)
- r.Use = ir.CallUseList
rtyp := r.Type()
mismatched := false
@@ -217,7 +220,7 @@ assignOK:
}
}
if mismatched && !failed {
- rewriteMultiValueCall(stmt, r)
+ RewriteMultiValueCall(stmt, r)
}
return
}
@@ -237,6 +240,15 @@ func plural(n int) string {
return "s"
}
+// tcCheckNil typechecks an OCHECKNIL node.
+func tcCheckNil(n *ir.UnaryExpr) ir.Node {
+ n.X = Expr(n.X)
+ if !n.X.Type().IsPtrShaped() {
+ base.FatalfAt(n.Pos(), "%L is not pointer shaped", n.X)
+ }
+ return n
+}
+
// tcFor typechecks an OFOR node.
func tcFor(n *ir.ForStmt) ir.Node {
Stmts(n.Init())
@@ -383,10 +395,11 @@ func tcSelect(sel *ir.SelectStmt) {
n := Stmt(ncase.Comm)
ncase.Comm = n
oselrecv2 := func(dst, recv ir.Node, def bool) {
- n := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
- n.Def = def
- n.SetTypecheck(1)
- ncase.Comm = n
+ selrecv := ir.NewAssignListStmt(n.Pos(), ir.OSELRECV2, []ir.Node{dst, ir.BlankNode}, []ir.Node{recv})
+ selrecv.Def = def
+ selrecv.SetTypecheck(1)
+ selrecv.SetInit(n.Init())
+ ncase.Comm = selrecv
}
switch n.Op() {
default:
@@ -653,29 +666,18 @@ func tcSwitchType(n *ir.SwitchStmt) {
}
type typeSet struct {
- m map[string][]typeSetEntry
-}
-
-type typeSetEntry struct {
- pos src.XPos
- typ *types.Type
+ m map[string]src.XPos
}
func (s *typeSet) add(pos src.XPos, typ *types.Type) {
if s.m == nil {
- s.m = make(map[string][]typeSetEntry)
+ s.m = make(map[string]src.XPos)
}
- // LongString does not uniquely identify types, so we need to
- // disambiguate collisions with types.Identical.
- // TODO(mdempsky): Add a method that *is* unique.
- ls := typ.LongString()
- prevs := s.m[ls]
- for _, prev := range prevs {
- if types.Identical(typ, prev.typ) {
- base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev.pos))
- return
- }
+ ls := typ.LinkString()
+ if prev, ok := s.m[ls]; ok {
+ base.ErrorfAt(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, base.FmtPos(prev))
+ return
}
- s.m[ls] = append(prevs, typeSetEntry{pos, typ})
+ s.m[ls] = pos
}
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go
index 9ee7a94b1f24af5bea061042a8d471c8c4758b69..5b5b04371517516c8af7e3a5d79f39cfa4b9b38d 100644
--- a/src/cmd/compile/internal/typecheck/subr.go
+++ b/src/cmd/compile/internal/typecheck/subr.go
@@ -5,6 +5,7 @@
package typecheck
import (
+ "bytes"
"fmt"
"sort"
"strconv"
@@ -13,6 +14,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
+ "cmd/internal/objabi"
"cmd/internal/src"
)
@@ -158,7 +160,7 @@ func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr {
case path != nil:
// rebuild elided dots
for c := len(path) - 1; c >= 0; c-- {
- dot := ir.NewSelectorExpr(base.Pos, ir.ODOT, n.X, path[c].field.Sym)
+ dot := ir.NewSelectorExpr(n.Pos(), ir.ODOT, n.X, path[c].field.Sym)
dot.SetImplicit(true)
dot.SetType(path[c].field.Type)
n.X = dot
@@ -351,10 +353,14 @@ func Assignop(src, dst *types.Type) (ir.Op, string) {
if types.Identical(src, dst) {
return ir.OCONVNOP, ""
}
+ return Assignop1(src, dst)
+}
- // 2. src and dst have identical underlying types
- // and either src or dst is not a named type or
- // both are empty interface types.
+func Assignop1(src, dst *types.Type) (ir.Op, string) {
+ // 2. src and dst have identical underlying types and
+ // a. either src or dst is not a named type, or
+ // b. both are empty interface types, or
+ // c. at least one is a gcshape type.
// For assignable but different non-empty interface types,
// we want to recompute the itab. Recomputing the itab ensures
// that itabs are unique (thus an interface with a compile-time
@@ -371,21 +377,24 @@ func Assignop(src, dst *types.Type) (ir.Op, string) {
// which need to have their itab updated.
return ir.OCONVNOP, ""
}
+ if src.IsShape() || dst.IsShape() {
+ // Conversion between a shape type and one of the types
+ // it represents also needs no conversion.
+ return ir.OCONVNOP, ""
+ }
}
// 3. dst is an interface type and src implements dst.
if dst.IsInterface() && src.Kind() != types.TNIL {
var missing, have *types.Field
var ptr int
+ if src.IsShape() {
+ // Shape types implement things they have already
+ // been typechecked to implement, even if they
+ // don't have the methods for them.
+ return ir.OCONVIFACE, ""
+ }
if implements(src, dst, &missing, &have, &ptr) {
- // Call NeedITab/ITabAddr so that (src, dst)
- // gets added to itabs early, which allows
- // us to de-virtualize calls through this
- // type/interface pair later. See CompileITabs in reflect.go
- if types.IsDirectIface(src) && !dst.IsEmptyInterface() {
- NeedITab(src, dst)
- }
-
return ir.OCONVIFACE, ""
}
@@ -722,13 +731,30 @@ func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field,
return m, followptr
}
+// implements reports whether t implements the interface iface. t can be
+// an interface, a type parameter, or a concrete type. If implements returns
+// false, it stores a method of iface that is not implemented in *m. If the
+// method name matches but the type is wrong, it additionally stores the type
+// of the method (on t) in *samename.
func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
t0 := t
if t == nil {
return false
}
- if t.IsInterface() {
+ if t.IsInterface() || t.IsTypeParam() {
+ if t.IsTypeParam() {
+ // If t is a simple type parameter T, its type and underlying is the same.
+ // If t is a type definition:'type P[T any] T', its type is P[T] and its
+ // underlying is T. Therefore we use 't.Underlying() != t' to distinguish them.
+ if t.Underlying() != t {
+ CalcMethods(t)
+ } else {
+ // A typeparam satisfies an interface if its type bound
+ // has all the methods of that interface.
+ t = t.Bound()
+ }
+ }
i := 0
tms := t.AllMethods().Slice()
for _, im := range iface.AllMethods().Slice() {
@@ -874,3 +900,563 @@ var slist []symlink
type symlink struct {
field *types.Field
}
+
+// TypesOf converts a list of nodes to a list
+// of types of those nodes.
+func TypesOf(x []ir.Node) []*types.Type {
+ r := make([]*types.Type, len(x))
+ for i, n := range x {
+ r[i] = n.Type()
+ }
+ return r
+}
+
+// addTargs writes out the targs to buffer b as a comma-separated list enclosed by
+// brackets.
+func addTargs(b *bytes.Buffer, targs []*types.Type) {
+ b.WriteByte('[')
+ for i, targ := range targs {
+ if i > 0 {
+ b.WriteByte(',')
+ }
+ // Make sure that type arguments (including type params), are
+ // uniquely specified. LinkString() eliminates all spaces
+ // and includes the package path (local package path is "" before
+ // linker substitution).
+ tstring := targ.LinkString()
+ b.WriteString(tstring)
+ }
+ b.WriteString("]")
+}
+
+// InstTypeName creates a name for an instantiated type, based on the name of the
+// generic type and the type args.
+func InstTypeName(name string, targs []*types.Type) string {
+ b := bytes.NewBufferString(name)
+ addTargs(b, targs)
+ return b.String()
+}
+
+// makeInstName1 returns the name of the generic function instantiated with the
+// given types, which can have type params or shapes, or be concrete types. name is
+// the name of the generic function or method.
+func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string {
+ b := bytes.NewBufferString("")
+ i := strings.Index(name, "[")
+ assert(hasBrackets == (i >= 0))
+ if i >= 0 {
+ b.WriteString(name[0:i])
+ } else {
+ b.WriteString(name)
+ }
+ addTargs(b, targs)
+ if i >= 0 {
+ i2 := strings.LastIndex(name[i:], "]")
+ assert(i2 >= 0)
+ b.WriteString(name[i+i2+1:])
+ }
+ return b.String()
+}
+
+// MakeFuncInstSym makes the unique sym for a stenciled generic function or method,
+// based on the name of the function gf and the targs. It replaces any
+// existing bracket type list in the name. MakeInstName asserts that gf has
+// brackets in its name if and only if hasBrackets is true.
+//
+// Names of declared generic functions have no brackets originally, so hasBrackets
+// should be false. Names of generic methods already have brackets, since the new
+// type parameter is specified in the generic type of the receiver (e.g. func
+// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set.
+//
+// The standard naming is something like: 'genFn[int,bool]' for functions and
+// '(*genType[int,bool]).methodName' for methods
+//
+// isMethodNode specifies if the name of a method node is being generated (as opposed
+// to a name of an instantiation of generic function or name of the shape-based
+// function that helps implement a method of an instantiated type). For method nodes
+// on shape types, we prepend "nofunc.", because method nodes for shape types will
+// have no body, and we want to avoid a name conflict with the shape-based function
+// that helps implement the same method for fully-instantiated types.
+func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, isMethodNode, hasBrackets bool) *types.Sym {
+ nm := makeInstName1(gf.Name, targs, hasBrackets)
+ if targs[0].HasShape() && isMethodNode {
+ nm = "nofunc." + nm
+ }
+ return gf.Pkg.Lookup(nm)
+}
+
+func MakeDictSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
+ for _, targ := range targs {
+ if targ.HasTParam() {
+ fmt.Printf("FUNCTION %s\n", gf.Name)
+ for _, targ := range targs {
+ fmt.Printf(" PARAM %+v\n", targ)
+ }
+ panic("dictionary should always have concrete type args")
+ }
+ }
+ name := makeInstName1(gf.Name, targs, hasBrackets)
+ name = fmt.Sprintf("%s.%s", objabi.GlobalDictPrefix, name)
+ return gf.Pkg.Lookup(name)
+}
+
+func assert(p bool) {
+ base.Assert(p)
+}
+
+// List of newly fully-instantiated types who should have their methods generated.
+var instTypeList []*types.Type
+
+// 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.
+func GetInstTypeList() []*types.Type {
+ r := instTypeList
+ 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
+ Targs []*types.Type
+ // If non-nil, the substitution map from name nodes in the generic function to the
+ // name nodes in the new stenciled function.
+ Vars map[*ir.Name]*ir.Name
+ // If non-nil, function to substitute an incomplete (TFORW) type.
+ SubstForwFunc func(*types.Type) *types.Type
+}
+
+// Typ computes the type obtained by substituting any type parameter or shape in t
+// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
+// If t contains no type parameters, the result is t; otherwise the result is a new
+// type. It deals with recursive types by using TFORW types and finding partially or
+// fully created types via sym.Def.
+func (ts *Tsubster) Typ(t *types.Type) *types.Type {
+ // Defer the CheckSize calls until we have fully-defined
+ // (possibly-recursive) top-level type.
+ types.DeferCheckSize()
+ r := ts.typ1(t)
+ types.ResumeCheckSize()
+ return r
+}
+
+func (ts *Tsubster) typ1(t *types.Type) *types.Type {
+ if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TFUNC {
+ // Note: function types need to be copied regardless, as the
+ // types of closures may contain declarations that need
+ // to be copied. See #45738.
+ return t
+ }
+
+ if t.IsTypeParam() || t.IsShape() {
+ for i, tp := range ts.Tparams {
+ if tp == t {
+ return ts.Targs[i]
+ }
+ }
+ // If t is a simple typeparam T, then t has the name/symbol 'T'
+ // and t.Underlying() == t.
+ //
+ // However, consider the type definition: 'type P[T any] T'. We
+ // might use this definition so we can have a variant of type T
+ // that we can add new methods to. Suppose t is a reference to
+ // P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM,
+ // because P[T] is defined as T. If we look at t.Underlying(), it
+ // is different, because the name of t.Underlying() is 'T' rather
+ // than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM.
+ // In this case, we do the needed recursive substitution in the
+ // case statement below.
+ if t.Underlying() == t {
+ // t is a simple typeparam that didn't match anything in tparam
+ return t
+ }
+ // t is a more complex typeparam (e.g. P[T], as above, whose
+ // definition is just T).
+ assert(t.Sym() != nil)
+ }
+
+ var newsym *types.Sym
+ var neededTargs []*types.Type
+ var targsChanged bool
+ var forw *types.Type
+
+ if t.Sym() != nil && (t.HasTParam() || t.HasShape()) {
+ // Need to test for t.HasTParam() again because of special TFUNC case above.
+ // Translate the type params for this type according to
+ // the tparam/targs mapping from subst.
+ neededTargs = make([]*types.Type, len(t.RParams()))
+ for i, rparam := range t.RParams() {
+ neededTargs[i] = ts.typ1(rparam)
+ if !types.IdenticalStrict(neededTargs[i], rparam) {
+ targsChanged = true
+ }
+ }
+ // For a named (defined) type, we have to change the name of the
+ // type as well. We do this first, so we can look up if we've
+ // already seen this type during this substitution or other
+ // definitions/substitutions.
+ genName := genericTypeName(t.Sym())
+ newsym = t.Sym().Pkg.Lookup(InstTypeName(genName, neededTargs))
+ if newsym.Def != nil {
+ // We've already created this instantiated defined type.
+ return newsym.Def.Type()
+ }
+
+ // In order to deal with recursive generic types, create a TFORW
+ // type initially and set the Def field of its sym, so it can be
+ // found if this type appears recursively within the type.
+ forw = NewIncompleteNamedType(t.Pos(), newsym)
+ //println("Creating new type by sub", newsym.Name, forw.HasTParam())
+ forw.SetRParams(neededTargs)
+ // Copy the OrigSym from the re-instantiated type (which is the sym of
+ // the base generic type).
+ assert(t.OrigSym() != nil)
+ forw.SetOrigSym(t.OrigSym())
+ }
+
+ var newt *types.Type
+
+ switch t.Kind() {
+ case types.TTYPEPARAM:
+ if t.Sym() == newsym && !targsChanged {
+ // The substitution did not change the type.
+ return t
+ }
+ // Substitute the underlying typeparam (e.g. T in P[T], see
+ // the example describing type P[T] above).
+ newt = ts.typ1(t.Underlying())
+ assert(newt != t)
+
+ case types.TARRAY:
+ elem := t.Elem()
+ newelem := ts.typ1(elem)
+ if newelem != elem || targsChanged {
+ newt = types.NewArray(newelem, t.NumElem())
+ }
+
+ case types.TPTR:
+ elem := t.Elem()
+ newelem := ts.typ1(elem)
+ if newelem != elem || targsChanged {
+ newt = types.NewPtr(newelem)
+ }
+
+ case types.TSLICE:
+ elem := t.Elem()
+ newelem := ts.typ1(elem)
+ if newelem != elem || targsChanged {
+ newt = types.NewSlice(newelem)
+ }
+
+ case types.TSTRUCT:
+ newt = ts.tstruct(t, targsChanged)
+ if newt == t {
+ newt = nil
+ }
+
+ case types.TFUNC:
+ newrecvs := ts.tstruct(t.Recvs(), false)
+ newparams := ts.tstruct(t.Params(), false)
+ newresults := ts.tstruct(t.Results(), false)
+ // Translate the tparams of a signature.
+ newtparams := ts.tstruct(t.TParams(), false)
+ if newrecvs != t.Recvs() || newparams != t.Params() ||
+ newresults != t.Results() || newtparams != t.TParams() || targsChanged {
+ // If any types have changed, then the all the fields of
+ // of recv, params, and results must be copied, because they have
+ // offset fields that are dependent, and so must have an
+ // independent copy for each new signature.
+ var newrecv *types.Field
+ if newrecvs.NumFields() > 0 {
+ if newrecvs == t.Recvs() {
+ newrecvs = ts.tstruct(t.Recvs(), true)
+ }
+ newrecv = newrecvs.Field(0)
+ }
+ if newparams == t.Params() {
+ newparams = ts.tstruct(t.Params(), true)
+ }
+ if newresults == t.Results() {
+ newresults = ts.tstruct(t.Results(), true)
+ }
+ var tparamfields []*types.Field
+ if newtparams.HasTParam() {
+ tparamfields = newtparams.FieldSlice()
+ } else {
+ // Completely remove the tparams from the resulting
+ // signature, if the tparams are now concrete types.
+ tparamfields = nil
+ }
+ newt = types.NewSignature(t.Pkg(), newrecv, tparamfields,
+ newparams.FieldSlice(), newresults.FieldSlice())
+ }
+
+ case types.TINTER:
+ newt = ts.tinter(t, targsChanged)
+ if newt == t {
+ newt = nil
+ }
+
+ case types.TMAP:
+ newkey := ts.typ1(t.Key())
+ newval := ts.typ1(t.Elem())
+ if newkey != t.Key() || newval != t.Elem() || targsChanged {
+ newt = types.NewMap(newkey, newval)
+ }
+
+ case types.TCHAN:
+ elem := t.Elem()
+ newelem := ts.typ1(elem)
+ if newelem != elem || targsChanged {
+ newt = types.NewChan(newelem, t.ChanDir())
+ }
+ case types.TFORW:
+ if ts.SubstForwFunc != nil {
+ return ts.SubstForwFunc(forw)
+ } else {
+ assert(false)
+ }
+ case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
+ types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
+ types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
+ newt = t.Underlying()
+ case types.TUNION:
+ nt := t.NumTerms()
+ newterms := make([]*types.Type, nt)
+ tildes := make([]bool, nt)
+ changed := false
+ for i := 0; i < nt; i++ {
+ term, tilde := t.Term(i)
+ tildes[i] = tilde
+ newterms[i] = ts.typ1(term)
+ if newterms[i] != term {
+ changed = true
+ }
+ }
+ if changed {
+ newt = types.NewUnion(newterms, tildes)
+ }
+ default:
+ panic(fmt.Sprintf("Bad type in (*TSubster).Typ: %v", t.Kind()))
+ }
+ if newt == nil {
+ // Even though there were typeparams in the type, there may be no
+ // change if this is a function type for a function call (which will
+ // have its own tparams/targs in the function instantiation).
+ return t
+ }
+
+ if forw != nil {
+ forw.SetUnderlying(newt)
+ newt = forw
+ }
+
+ if !newt.HasTParam() && !newt.IsFuncArgStruct() {
+ // Calculate the size of any new types created. These will be
+ // deferred until the top-level ts.Typ() or g.typ() (if this is
+ // called from g.fillinMethods()).
+ types.CheckSize(newt)
+ }
+
+ if t.Kind() != types.TINTER && t.Methods().Len() > 0 {
+ // Fill in the method info for the new type.
+ var newfields []*types.Field
+ newfields = make([]*types.Field, t.Methods().Len())
+ for i, f := range t.Methods().Slice() {
+ t2 := ts.typ1(f.Type)
+ oldsym := f.Nname.Sym()
+ newsym := MakeFuncInstSym(oldsym, ts.Targs, true, true)
+ var nname *ir.Name
+ if newsym.Def != nil {
+ nname = newsym.Def.(*ir.Name)
+ } else {
+ nname = ir.NewNameAt(f.Pos, newsym)
+ nname.SetType(t2)
+ ir.MarkFunc(nname)
+ newsym.Def = nname
+ }
+ newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+ newfields[i].Nname = nname
+ }
+ newt.Methods().Set(newfields)
+ if !newt.HasTParam() && !newt.HasShape() {
+ // Generate all the methods for a new fully-instantiated type.
+
+ NeedInstType(newt)
+ }
+ }
+ return newt
+}
+
+// tstruct substitutes type params in types of the fields of a structure type. For
+// each field, tstruct copies the Nname, and translates it if Nname is in
+// ts.vars. To always force the creation of a new (top-level) struct,
+// regardless of whether anything changed with the types or names of the struct's
+// fields, set force to true.
+func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
+ if t.NumFields() == 0 {
+ if t.HasTParam() || t.HasShape() {
+ // For an empty struct, we need to return a new type,
+ // since it may now be fully instantiated (HasTParam
+ // becomes false).
+ return types.NewStruct(t.Pkg(), nil)
+ }
+ return t
+ }
+ var newfields []*types.Field
+ if force {
+ newfields = make([]*types.Field, t.NumFields())
+ }
+ for i, f := range t.Fields().Slice() {
+ t2 := ts.typ1(f.Type)
+ if (t2 != f.Type || f.Nname != nil) && newfields == nil {
+ newfields = make([]*types.Field, t.NumFields())
+ for j := 0; j < i; j++ {
+ newfields[j] = t.Field(j)
+ }
+ }
+ if newfields != nil {
+ newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+ newfields[i].Embedded = f.Embedded
+ newfields[i].Note = f.Note
+ if f.IsDDD() {
+ newfields[i].SetIsDDD(true)
+ }
+ if f.Nointerface() {
+ newfields[i].SetNointerface(true)
+ }
+ if f.Nname != nil && ts.Vars != nil {
+ v := ts.Vars[f.Nname.(*ir.Name)]
+ if v != nil {
+ // This is the case where we are
+ // translating the type of the function we
+ // are substituting, so its dcls are in
+ // the subst.ts.vars table, and we want to
+ // change to reference the new dcl.
+ newfields[i].Nname = v
+ } else {
+ // This is the case where we are
+ // translating the type of a function
+ // reference inside the function we are
+ // substituting, so we leave the Nname
+ // value as is.
+ newfields[i].Nname = f.Nname
+ }
+ }
+ }
+ }
+ if newfields != nil {
+ news := types.NewStruct(t.Pkg(), newfields)
+ news.StructType().Funarg = t.StructType().Funarg
+ return news
+ }
+ return t
+
+}
+
+// tinter substitutes type params in types of the methods of an interface type.
+func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type {
+ if t.Methods().Len() == 0 {
+ if t.HasTParam() {
+ // 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, false)
+ }
+ return t
+ }
+ var newfields []*types.Field
+ if force {
+ newfields = make([]*types.Field, t.Methods().Len())
+ }
+ for i, f := range t.Methods().Slice() {
+ t2 := ts.typ1(f.Type)
+ if (t2 != f.Type || f.Nname != nil) && newfields == nil {
+ newfields = make([]*types.Field, t.Methods().Len())
+ for j := 0; j < i; j++ {
+ newfields[j] = t.Methods().Index(j)
+ }
+ }
+ if newfields != nil {
+ newfields[i] = types.NewField(f.Pos, f.Sym, t2)
+ }
+ }
+ if newfields != nil {
+ return types.NewInterface(t.Pkg(), newfields, t.IsImplicit())
+ }
+ return t
+}
+
+// genericSym returns the name of the base generic type for the type named by
+// sym. It simply returns the name obtained by removing everything after the
+// first bracket ("[").
+func genericTypeName(sym *types.Sym) string {
+ return sym.Name[0:strings.Index(sym.Name, "[")]
+}
+
+// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
+// be used in place of the input type and still generate identical code.
+// No methods are added - all methods calls directly on a shape should
+// be done by converting to an interface using the dictionary.
+//
+// For now, we only consider two types to have the same shape, if they have exactly
+// the same underlying type or they are both pointer types.
+//
+// Shape types are also distinguished by the index of the type in a type param/arg
+// list. We need to do this so we can distinguish and substitute properly for two
+// type params in the same function that have the same shape for a particular
+// instantiation.
+func Shapify(t *types.Type, index int) *types.Type {
+ assert(!t.IsShape())
+ // Map all types with the same underlying type to the same shape.
+ u := t.Underlying()
+
+ // All pointers have the same shape.
+ // TODO: Make unsafe.Pointer the same shape as normal pointers.
+ // 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()
+ }
+
+ if shapeMap == nil {
+ shapeMap = map[int]map[*types.Type]*types.Type{}
+ }
+ submap := shapeMap[index]
+ if submap == nil {
+ submap = map[*types.Type]*types.Type{}
+ shapeMap[index] = submap
+ }
+ if s := submap[u]; s != nil {
+ return s
+ }
+
+ // LinkString specifies the type uniquely, but has no spaces.
+ nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
+ sym := types.ShapePkg.Lookup(nm)
+ if sym.Def != nil {
+ // Use any existing type with the same name
+ submap[u] = sym.Def.Type()
+ return submap[u]
+ }
+ name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
+ s := types.NewNamed(name)
+ sym.Def = name
+ s.SetUnderlying(u)
+ s.SetIsShape(true)
+ s.SetHasShape(true)
+ name.SetType(s)
+ name.SetTypecheck(1)
+ submap[u] = s
+ return s
+}
+
+var shapeMap map[int]map[*types.Type]*types.Type
diff --git a/src/cmd/compile/internal/typecheck/syms.go b/src/cmd/compile/internal/typecheck/syms.go
index f29af82db2cc25670a60ef4192fd5623a4ee4806..ed3aaecc5a2a2a777cd748d22eca98f4b65f2ce1 100644
--- a/src/cmd/compile/internal/typecheck/syms.go
+++ b/src/cmd/compile/internal/typecheck/syms.go
@@ -75,9 +75,9 @@ func InitRuntime() {
typ := typs[d.typ]
switch d.tag {
case funcTag:
- importfunc(ir.Pkgs.Runtime, src.NoXPos, sym, typ)
+ importfunc(src.NoXPos, sym, typ)
case varTag:
- importvar(ir.Pkgs.Runtime, src.NoXPos, sym, typ)
+ importvar(src.NoXPos, sym, typ)
default:
base.Fatalf("unhandled declaration tag %v", d.tag)
}
diff --git a/src/cmd/compile/internal/typecheck/type.go b/src/cmd/compile/internal/typecheck/type.go
index af694c2d94a30fb4a5bf1a74e3afd1a48fddfaa8..c4c1ef58cadb103a91c1a0e79d3c1aa6acd4cbda 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/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 359f66236969fb4e34867ba35ea9d56b9480f5ba..42970f6a5e279fa4a133c2d56d946d7d3dbfe9b2 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -13,6 +13,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
+ "cmd/internal/src"
)
// Function collecting autotmps generated during typechecking,
@@ -24,7 +25,6 @@ var inimport bool // set during import
var TypecheckAllowed bool
var (
- NeedITab = func(t, itype *types.Type) {}
NeedRuntimeType = func(*types.Type) {}
)
@@ -35,18 +35,10 @@ func Stmt(n ir.Node) ir.Node { return typecheck(n, ctxStmt) }
func Exprs(exprs []ir.Node) { typecheckslice(exprs, ctxExpr) }
func Stmts(stmts []ir.Node) { typecheckslice(stmts, ctxStmt) }
-func Call(call *ir.CallExpr) {
- t := call.X.Type()
- if t == nil {
- panic("misuse of Call")
- }
- ctx := ctxStmt
- if t.NumResults() > 0 {
- ctx = ctxExpr | ctxMultiOK
- }
- if typecheck(call, ctx) != call {
- panic("bad typecheck")
- }
+func Call(pos src.XPos, callee ir.Node, args []ir.Node, dots bool) ir.Node {
+ call := ir.NewCallExpr(pos, ir.OCALL, callee, args)
+ call.IsDDD = dots
+ return typecheck(call, ctxStmt|ctxExpr)
}
func Callee(n ir.Node) ir.Node {
@@ -59,8 +51,8 @@ func FuncBody(n *ir.Func) {
Stmts(n.Body)
CheckUnused(n)
CheckReturn(n)
- if base.Errors() > errorsBefore {
- n.Body = nil // type errors; do not compile
+ if ir.IsBlank(n.Nname) || base.Errors() > errorsBefore {
+ n.Body = nil // blank function or type errors; do not compile
}
}
@@ -777,6 +769,10 @@ func typecheck1(n ir.Node, top int) ir.Node {
n := n.(*ir.CallExpr)
return tcRecover(n)
+ case ir.ORECOVERFP:
+ n := n.(*ir.CallExpr)
+ return tcRecoverFP(n)
+
case ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr)
return tcUnsafeAdd(n)
@@ -787,11 +783,7 @@ func typecheck1(n ir.Node, top int) ir.Node {
case ir.OCLOSURE:
n := n.(*ir.ClosureExpr)
- tcClosure(n, top)
- if n.Type() == nil {
- return n
- }
- return n
+ return tcClosure(n, top)
case ir.OITAB:
n := n.(*ir.UnaryExpr)
@@ -814,6 +806,14 @@ func typecheck1(n ir.Node, top int) ir.Node {
n.SetType(types.Types[types.TUINTPTR])
return n
+ case ir.OGETCALLERPC, ir.OGETCALLERSP:
+ n := n.(*ir.CallExpr)
+ if len(n.Args) != 0 {
+ base.FatalfAt(n.Pos(), "unexpected arguments: %v", n)
+ }
+ n.SetType(types.Types[types.TUINTPTR])
+ return n
+
case ir.OCONVNOP:
n := n.(*ir.ConvExpr)
n.X = Expr(n.X)
@@ -879,8 +879,13 @@ func typecheck1(n ir.Node, top int) ir.Node {
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
+ n.Call = typecheck(n.Call, ctxStmt|ctxExpr).(*ir.CallExpr)
return n
+ case ir.OCHECKNIL:
+ n := n.(*ir.UnaryExpr)
+ return tcCheckNil(n)
+
case ir.OSELECT:
tcSelect(n.(*ir.SelectStmt))
return n
@@ -951,12 +956,12 @@ func typecheckargs(n ir.InitNode) {
}
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
- rewriteMultiValueCall(n, list[0])
+ RewriteMultiValueCall(n, list[0])
}
-// rewriteMultiValueCall rewrites multi-valued f() to use temporaries,
+// RewriteMultiValueCall rewrites multi-valued f() to use temporaries,
// so the backend wouldn't need to worry about tuple-valued expressions.
-func rewriteMultiValueCall(n ir.InitNode, call ir.Node) {
+func RewriteMultiValueCall(n ir.InitNode, call ir.Node) {
// If we're outside of function context, then this call will
// be executed during the generated init function. However,
// init.go hasn't yet created it. Instead, associate the
@@ -1732,11 +1737,6 @@ func CheckMapKeys() {
mapqueue = nil
}
-// TypeGen tracks the number of function-scoped defined types that
-// have been declared. It's used to generate unique linker symbols for
-// their runtime type descriptors.
-var TypeGen int32
-
func typecheckdeftype(n *ir.Name) {
if base.EnableTrace && base.Flag.LowerT {
defer tracePrint("typecheckdeftype", n)(nil)
@@ -1744,8 +1744,7 @@ func typecheckdeftype(n *ir.Name) {
t := types.NewNamed(n)
if n.Curfn != nil {
- TypeGen++
- t.Vargen = TypeGen
+ t.SetVargen()
}
if n.Pragma()&ir.NotInHeap != 0 {
@@ -1913,11 +1912,6 @@ func typecheckdef(n *ir.Name) {
n.SetDiag(true)
goto ret
}
- // For package-level type aliases, set n.Sym.Def so we can identify
- // it as a type alias during export. See also #31959.
- if n.Curfn == nil {
- n.Sym().Def = n.Ntype
- }
}
break
}
diff --git a/src/cmd/compile/internal/typecheck/universe.go b/src/cmd/compile/internal/typecheck/universe.go
index de185ab94471d640c834341e2a38432ff43acc09..0254d96e6825e5adac1735e78df950694e0d62b7 100644
--- a/src/cmd/compile/internal/typecheck/universe.go
+++ b/src/cmd/compile/internal/typecheck/universe.go
@@ -29,37 +29,6 @@ var (
okforarith [types.NTYPE]bool
)
-var basicTypes = [...]struct {
- name string
- etype types.Kind
-}{
- {"int8", types.TINT8},
- {"int16", types.TINT16},
- {"int32", types.TINT32},
- {"int64", types.TINT64},
- {"uint8", types.TUINT8},
- {"uint16", types.TUINT16},
- {"uint32", types.TUINT32},
- {"uint64", types.TUINT64},
- {"float32", types.TFLOAT32},
- {"float64", types.TFLOAT64},
- {"complex64", types.TCOMPLEX64},
- {"complex128", types.TCOMPLEX128},
- {"bool", types.TBOOL},
- {"string", types.TSTRING},
-}
-
-var typedefs = [...]struct {
- name string
- etype types.Kind
- sameas32 types.Kind
- sameas64 types.Kind
-}{
- {"int", types.TINT, types.TINT32, types.TINT64},
- {"uint", types.TUINT, types.TUINT32, types.TUINT64},
- {"uintptr", types.TUINTPTR, types.TUINT32, types.TUINT64},
-}
-
var builtinFuncs = [...]struct {
name string
op ir.Op
@@ -94,77 +63,12 @@ var unsafeFuncs = [...]struct {
// InitUniverse initializes the universe block.
func InitUniverse() {
- if types.PtrSize == 0 {
- base.Fatalf("typeinit before betypeinit")
- }
-
- types.SlicePtrOffset = 0
- types.SliceLenOffset = types.Rnd(types.SlicePtrOffset+int64(types.PtrSize), int64(types.PtrSize))
- types.SliceCapOffset = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize))
- types.SliceSize = types.Rnd(types.SliceCapOffset+int64(types.PtrSize), int64(types.PtrSize))
-
- // string is same as slice wo the cap
- types.StringSize = types.Rnd(types.SliceLenOffset+int64(types.PtrSize), int64(types.PtrSize))
-
- for et := types.Kind(0); et < types.NTYPE; et++ {
- types.SimType[et] = et
- }
-
- types.Types[types.TANY] = types.New(types.TANY)
- types.Types[types.TINTER] = types.NewInterface(types.LocalPkg, nil)
-
- defBasic := func(kind types.Kind, pkg *types.Pkg, name string) *types.Type {
- sym := pkg.Lookup(name)
+ types.InitTypes(func(sym *types.Sym, typ *types.Type) types.Object {
n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, sym)
- t := types.NewBasic(kind, n)
- n.SetType(t)
+ n.SetType(typ)
sym.Def = n
- if kind != types.TANY {
- types.CalcSize(t)
- }
- return t
- }
-
- for _, s := range &basicTypes {
- types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name)
- }
-
- for _, s := range &typedefs {
- sameas := s.sameas32
- if types.PtrSize == 8 {
- sameas = s.sameas64
- }
- types.SimType[s.etype] = sameas
-
- types.Types[s.etype] = defBasic(s.etype, types.BuiltinPkg, s.name)
- }
-
- // We create separate byte and rune types for better error messages
- // rather than just creating type alias *types.Sym's for the uint8 and
- // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false.
- // TODO(gri) Should we get rid of this special case (at the cost
- // of less informative error messages involving bytes and runes)?
- // (Alternatively, we could introduce an OTALIAS node representing
- // type aliases, albeit at the cost of having to deal with it everywhere).
- types.ByteType = defBasic(types.TUINT8, types.BuiltinPkg, "byte")
- types.RuneType = defBasic(types.TINT32, types.BuiltinPkg, "rune")
-
- // error type
- s := types.BuiltinPkg.Lookup("error")
- n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, s)
- types.ErrorType = types.NewNamed(n)
- types.ErrorType.SetUnderlying(makeErrorInterface())
- n.SetType(types.ErrorType)
- s.Def = n
- types.CalcSize(types.ErrorType)
-
- types.Types[types.TUNSAFEPTR] = defBasic(types.TUNSAFEPTR, ir.Pkgs.Unsafe, "Pointer")
-
- // simple aliases
- types.SimType[types.TMAP] = types.TPTR
- types.SimType[types.TCHAN] = types.TPTR
- types.SimType[types.TFUNC] = types.TPTR
- types.SimType[types.TUNSAFEPTR] = types.TPTR
+ return n
+ })
for _, s := range &builtinFuncs {
s2 := types.BuiltinPkg.Lookup(s.name)
@@ -174,13 +78,13 @@ func InitUniverse() {
}
for _, s := range &unsafeFuncs {
- s2 := ir.Pkgs.Unsafe.Lookup(s.name)
+ s2 := types.UnsafePkg.Lookup(s.name)
def := NewName(s2)
def.BuiltinOp = s.op
s2.Def = def
}
- s = types.BuiltinPkg.Lookup("true")
+ s := types.BuiltinPkg.Lookup("true")
s.Def = ir.NewConstAt(src.NoXPos, s, types.UntypedBool, constant.MakeBool(true))
s = types.BuiltinPkg.Lookup("false")
@@ -190,7 +94,6 @@ func InitUniverse() {
types.BlankSym = s
s.Block = -100
s.Def = NewName(s)
- types.Types[types.TBLANK] = types.New(types.TBLANK)
ir.AsNode(s.Def).SetType(types.Types[types.TBLANK])
ir.BlankNode = ir.AsNode(s.Def)
ir.BlankNode.SetTypecheck(1)
@@ -198,10 +101,8 @@ func InitUniverse() {
s = types.BuiltinPkg.Lookup("_")
s.Block = -100
s.Def = NewName(s)
- types.Types[types.TBLANK] = types.New(types.TBLANK)
ir.AsNode(s.Def).SetType(types.Types[types.TBLANK])
- types.Types[types.TNIL] = types.New(types.TNIL)
s = types.BuiltinPkg.Lookup("nil")
nnil := NodNil()
nnil.(*ir.NilExpr).SetSym(s)
@@ -210,19 +111,6 @@ func InitUniverse() {
s = types.BuiltinPkg.Lookup("iota")
s.Def = ir.NewIota(base.Pos, s)
- for et := types.TINT8; et <= types.TUINT64; et++ {
- types.IsInt[et] = true
- }
- types.IsInt[types.TINT] = true
- types.IsInt[types.TUINT] = true
- types.IsInt[types.TUINTPTR] = true
-
- types.IsFloat[types.TFLOAT32] = true
- types.IsFloat[types.TFLOAT64] = true
-
- types.IsComplex[types.TCOMPLEX64] = true
- types.IsComplex[types.TCOMPLEX128] = true
-
// initialize okfor
for et := types.Kind(0); et < types.NTYPE; et++ {
if types.IsInt[et] || et == types.TIDEAL {
@@ -320,22 +208,6 @@ func InitUniverse() {
// special
okfor[ir.OCAP] = okforcap[:]
okfor[ir.OLEN] = okforlen[:]
-
- // comparison
- iscmp[ir.OLT] = true
- iscmp[ir.OGT] = true
- iscmp[ir.OGE] = true
- iscmp[ir.OLE] = true
- iscmp[ir.OEQ] = true
- iscmp[ir.ONE] = true
-}
-
-func makeErrorInterface() *types.Type {
- sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, nil, []*types.Field{
- types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]),
- })
- method := types.NewField(src.NoXPos, Lookup("Error"), sig)
- return types.NewInterface(types.NoPkg, []*types.Field{method})
}
// DeclareUniverse makes the universe block visible within the current package.
diff --git a/src/cmd/compile/internal/types/alg.go b/src/cmd/compile/internal/types/alg.go
index 2c2700f345730e2b0a99d472aedc5962953ca78a..f5675c66b4f4ec679e039fc3c26037fe652cad87 100644
--- a/src/cmd/compile/internal/types/alg.go
+++ b/src/cmd/compile/internal/types/alg.go
@@ -165,7 +165,7 @@ func IsPaddedField(t *Type, i int) bool {
if !t.IsStruct() {
base.Fatalf("IsPaddedField called non-struct %v", t)
}
- end := t.Width
+ end := t.width
if i+1 < t.NumFields() {
end = t.Field(i + 1).Offset
}
diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go
index 8b988952a78d2c689714ddfe2d61260fcdb75e19..3198a1f53c8674c6234d6d2fcaa1eb402718a36f 100644
--- a/src/cmd/compile/internal/types/fmt.go
+++ b/src/cmd/compile/internal/types/fmt.go
@@ -23,6 +23,9 @@ var BuiltinPkg *Pkg
// LocalPkg is the package being compiled.
var LocalPkg *Pkg
+// UnsafePkg is package unsafe.
+var UnsafePkg *Pkg
+
// BlankSym is the blank (_) symbol.
var BlankSym *Sym
@@ -61,7 +64,7 @@ var NumImport = make(map[string]int)
// The default is regular Go syntax (fmtGo).
// fmtDebug is like fmtGo but for debugging dumps and prints the type kind too.
// fmtTypeID and fmtTypeIDName are for generating various unique representations
-// of types used in hashes and the linker.
+// of types used in hashes, the linker, and function/method instantiations.
type fmtMode int
const (
@@ -137,11 +140,17 @@ func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
}
func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
+ name := s.Name
if q := pkgqual(s.Pkg, verb, mode); q != "" {
b.WriteString(q)
b.WriteByte('.')
+ if mode == fmtTypeIDName {
+ // If name is a generic instantiation, it might have local package placeholders
+ // in it. Replace those placeholders with the package name. See issue 49547.
+ name = strings.Replace(name, LocalPkg.Prefix, q, -1)
+ }
}
- b.WriteString(s.Name)
+ b.WriteString(name)
}
// pkgqual returns the qualifier that should be used for printing
@@ -239,17 +248,37 @@ func (t *Type) String() string {
return tconv(t, 0, fmtGo)
}
-// ShortString generates a short description of t.
-// It is used in autogenerated method names, reflection,
-// and itab names.
-func (t *Type) ShortString() string {
+// LinkString returns an unexpanded string description of t, suitable
+// for use in link symbols. "Unexpanded" here means that the
+// description uses `"".` to qualify identifiers from the current
+// package, and "expansion" refers to the renaming step performed by
+// the linker to replace these qualifiers with proper `path/to/pkg.`
+// qualifiers.
+//
+// After expansion, the description corresponds to type identity. That
+// is, for any pair of types t1 and t2, Identical(t1, t2) and
+// expand(t1.LinkString()) == expand(t2.LinkString()) report the same
+// value.
+//
+// Within a single compilation unit, LinkString always returns the
+// same unexpanded description for identical types. Thus it's safe to
+// use as a map key to implement a type-identity-keyed map. However,
+// make sure all LinkString calls used for this purpose happen within
+// the same compile process; the string keys are not stable across
+// multiple processes.
+func (t *Type) LinkString() string {
return tconv(t, 0, fmtTypeID)
}
-// LongString generates a complete description of t.
-// It is useful for reflection,
-// or when a unique fingerprint or hash of a type is required.
-func (t *Type) LongString() string {
+// NameString generates a user-readable, mostly unique string
+// description of t. NameString always returns the same description
+// for identical types, even across compilation units.
+//
+// NameString qualifies identifiers by package name, so it has
+// collisions when different packages share the same names and
+// identifiers. It also does not distinguish function-scope defined
+// types from package-scoped defined types or from each other.
+func (t *Type) NameString() string {
return tconv(t, 0, fmtTypeIDName)
}
@@ -278,7 +307,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
return
}
if t.Kind() == TSSA {
- b.WriteString(t.Extra.(string))
+ b.WriteString(t.extra.(string))
return
}
if t.Kind() == TTUPLE {
@@ -289,7 +318,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
}
if t.Kind() == TRESULTS {
- tys := t.Extra.(*Results).Types
+ tys := t.extra.(*Results).Types
for i, et := range tys {
if i > 0 {
b.WriteByte(',')
@@ -299,8 +328,8 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
return
}
- if t == ByteType || t == RuneType {
- // in %-T mode collapse rune and byte with their originals.
+ if t == AnyType || t == ByteType || t == RuneType {
+ // in %-T mode collapse predeclared aliases with their originals.
switch mode {
case fmtTypeIDName, fmtTypeID:
t = Types[t.Kind()]
@@ -316,31 +345,34 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
// Unless the 'L' flag was specified, if the type has a name, just print that name.
if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
- switch mode {
- case fmtTypeID, fmtTypeIDName:
- if verb == 'S' {
- if t.Vargen != 0 {
- sconv2(b, t.Sym(), 'S', mode)
- fmt.Fprintf(b, "·%d", t.Vargen)
- return
- }
- sconv2(b, t.Sym(), 'S', mode)
- return
- }
+ // Default to 'v' if verb is invalid.
+ if verb != 'S' {
+ verb = 'v'
+ }
- if mode == fmtTypeIDName {
- sconv2(b, t.Sym(), 'v', fmtTypeIDName)
- return
+ // In unified IR, function-scope defined types will have a ·N
+ // suffix embedded directly in their Name. Trim this off for
+ // non-fmtTypeID modes.
+ sym := t.Sym()
+ if mode != fmtTypeID {
+ i := len(sym.Name)
+ for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' {
+ i--
}
-
- if t.Sym().Pkg == LocalPkg && t.Vargen != 0 {
- sconv2(b, t.Sym(), 'v', mode)
- fmt.Fprintf(b, "·%d", t.Vargen)
- return
+ const dot = "·"
+ if i >= len(dot) && sym.Name[i-len(dot):i] == dot {
+ sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]}
}
}
-
- sconv2(b, t.Sym(), 'v', mode)
+ sconv2(b, sym, verb, mode)
+
+ // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName
+ // output too. It seems like it should, but that mode is currently
+ // used in string representation used by reflection, which is
+ // user-visible and doesn't expect this.
+ if mode == fmtTypeID && t.vargen != 0 {
+ fmt.Fprintf(b, "·%d", t.vargen)
+ }
return
}
@@ -567,6 +599,18 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
b.WriteString(fmt.Sprintf("%p", t))
}
+ case TUNION:
+ for i := 0; i < t.NumTerms(); i++ {
+ if i > 0 {
+ b.WriteString("|")
+ }
+ elem, tilde := t.Term(i)
+ if tilde {
+ b.WriteString("~")
+ }
+ tconv2(b, elem, 0, mode, visited)
+ }
+
case Txxx:
b.WriteString("Txxx")
@@ -671,7 +715,7 @@ func FmtConst(v constant.Value, sharp bool) string {
// TypeHash computes a hash value for type t to use in type switch statements.
func TypeHash(t *Type) uint32 {
- p := t.LongString()
+ p := t.NameString()
// Using MD5 is overkill, but reduces accidental collisions.
h := md5.Sum([]byte(p))
diff --git a/src/cmd/compile/internal/types/identity.go b/src/cmd/compile/internal/types/identity.go
index dde9f5185687ad6869d0961f85928df97e8a133b..a164b84da929b3259396cc0b7410c405cc7c9406 100644
--- a/src/cmd/compile/internal/types/identity.go
+++ b/src/cmd/compile/internal/types/identity.go
@@ -4,16 +4,30 @@
package types
-// Identical reports whether t1 and t2 are identical types, following
-// the spec rules. Receiver parameter types are ignored.
+const (
+ identIgnoreTags = 1 << iota
+ identStrict
+)
+
+// Identical reports whether t1 and t2 are identical types, following the spec rules.
+// Receiver parameter types are ignored. Named (defined) types are only equal if they
+// are pointer-equal - i.e. there must be a unique types.Type for each specific named
+// type. Also, a type containing a shape type is considered identical to another type
+// (shape or not) if their underlying types are the same, or they are both pointers.
func Identical(t1, t2 *Type) bool {
- return identical(t1, t2, true, nil)
+ return identical(t1, t2, 0, nil)
}
// IdenticalIgnoreTags is like Identical, but it ignores struct tags
// for struct identity.
func IdenticalIgnoreTags(t1, t2 *Type) bool {
- return identical(t1, t2, false, nil)
+ return identical(t1, t2, identIgnoreTags, nil)
+}
+
+// IdenticalStrict is like Identical, but matches types exactly, without the
+// exception for shapes.
+func IdenticalStrict(t1, t2 *Type) bool {
+ return identical(t1, t2, identStrict, nil)
}
type typePair struct {
@@ -21,7 +35,7 @@ type typePair struct {
t2 *Type
}
-func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) bool {
+func identical(t1, t2 *Type, flags int, assumedEqual map[typePair]struct{}) bool {
if t1 == t2 {
return true
}
@@ -29,6 +43,14 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
return false
}
if t1.sym != nil || t2.sym != nil {
+ if flags&identStrict == 0 && (t1.HasShape() || t2.HasShape()) {
+ switch t1.kind {
+ case TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, TUINT64, TINT, TUINT, TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64, TBOOL, TSTRING, TPTR, TUNSAFEPTR:
+ return true
+ }
+ // fall through to unnamed type comparison for complex types.
+ goto cont
+ }
// Special case: we keep byte/uint8 and rune/int32
// separate for error messages. Treat them as equal.
switch t1.kind {
@@ -36,10 +58,18 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
return (t1 == Types[TUINT8] || t1 == ByteType) && (t2 == Types[TUINT8] || t2 == ByteType)
case TINT32:
return (t1 == Types[TINT32] || t1 == RuneType) && (t2 == Types[TINT32] || t2 == RuneType)
+ case TINTER:
+ // Make sure named any type matches any empty interface
+ // (but not a shape type, if identStrict).
+ if flags&identStrict != 0 {
+ return t1 == AnyType && t2.IsEmptyInterface() && !t2.HasShape() || t2 == AnyType && t1.IsEmptyInterface() && !t1.HasShape()
+ }
+ return t1 == AnyType && t2.IsEmptyInterface() || t2 == AnyType && t1.IsEmptyInterface()
default:
return false
}
}
+cont:
// Any cyclic type must go through a named type, and if one is
// named, it is only identical to the other if they are the
@@ -66,7 +96,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
}
for i, f1 := range t1.AllMethods().Slice() {
f2 := t2.AllMethods().Index(i)
- if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.Sym != f2.Sym || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
}
@@ -78,10 +108,10 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
}
for i, f1 := range t1.FieldSlice() {
f2 := t2.Field(i)
- if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.Sym != f2.Sym || f1.Embedded != f2.Embedded || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
- if cmpTags && f1.Note != f2.Note {
+ if (flags&identIgnoreTags) == 0 && f1.Note != f2.Note {
return false
}
}
@@ -99,7 +129,7 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
}
for i, f1 := range fs1 {
f2 := fs2[i]
- if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, cmpTags, assumedEqual) {
+ if f1.IsDDD() != f2.IsDDD() || !identical(f1.Type, f2.Type, flags, assumedEqual) {
return false
}
}
@@ -117,10 +147,10 @@ func identical(t1, t2 *Type, cmpTags bool, assumedEqual map[typePair]struct{}) b
}
case TMAP:
- if !identical(t1.Key(), t2.Key(), cmpTags, assumedEqual) {
+ if !identical(t1.Key(), t2.Key(), flags, assumedEqual) {
return false
}
}
- return identical(t1.Elem(), t2.Elem(), cmpTags, assumedEqual)
+ return identical(t1.Elem(), t2.Elem(), flags, assumedEqual)
}
diff --git a/src/cmd/compile/internal/types/kind_string.go b/src/cmd/compile/internal/types/kind_string.go
index ae24a58b9219d483b710e69af65ce892bf7759e8..3e6a8bc064edf4cf0306d959eda9d07ca05346a5 100644
--- a/src/cmd/compile/internal/types/kind_string.go
+++ b/src/cmd/compile/internal/types/kind_string.go
@@ -38,20 +38,21 @@ func _() {
_ = x[TSTRING-27]
_ = x[TUNSAFEPTR-28]
_ = x[TTYPEPARAM-29]
- _ = x[TIDEAL-30]
- _ = x[TNIL-31]
- _ = x[TBLANK-32]
- _ = x[TFUNCARGS-33]
- _ = x[TCHANARGS-34]
- _ = x[TSSA-35]
- _ = x[TTUPLE-36]
- _ = x[TRESULTS-37]
- _ = x[NTYPE-38]
+ _ = x[TUNION-30]
+ _ = x[TIDEAL-31]
+ _ = x[TNIL-32]
+ _ = x[TBLANK-33]
+ _ = x[TFUNCARGS-34]
+ _ = x[TCHANARGS-35]
+ _ = x[TSSA-36]
+ _ = x[TTUPLE-37]
+ _ = x[TRESULTS-38]
+ _ = x[NTYPE-39]
}
-const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
+const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMUNIONIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
-var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 170, 175, 183, 191, 194, 199, 206, 211}
+var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 172, 175, 180, 188, 196, 199, 204, 211, 216}
func (i Kind) String() string {
if i >= Kind(len(_Kind_index)-1) {
diff --git a/src/cmd/compile/internal/types/pkg.go b/src/cmd/compile/internal/types/pkg.go
index a6d2e2007b0424d4927ef5cda15fb67d94d27186..0b822a450c9893e669af8d7ed8de93fe99f1c021 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.") && !strings.Contains(path, "/") {
+ // 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
@@ -137,7 +144,3 @@ func CleanroomDo(f func()) {
f()
pkgMap = saved
}
-
-func IsDotAlias(sym *Sym) bool {
- return sym.Def != nil && sym.Def.Sym() != sym
-}
diff --git a/src/cmd/compile/internal/types/size.go b/src/cmd/compile/internal/types/size.go
index f0e695ab964ac532dbeb3ad2bfdcae2e1e8479c2..fb6accdc642f74acf21f57ea663729db2b536428 100644
--- a/src/cmd/compile/internal/types/size.go
+++ b/src/cmd/compile/internal/types/size.go
@@ -90,6 +90,26 @@ func expandiface(t *Type) {
methods = append(methods, m)
}
+ {
+ methods := t.Methods().Slice()
+ sort.SliceStable(methods, func(i, j int) bool {
+ mi, mj := methods[i], methods[j]
+
+ // Sort embedded types by type name (if any).
+ if mi.Sym == nil && mj.Sym == nil {
+ return mi.Type.Sym().Less(mj.Type.Sym())
+ }
+
+ // Sort methods before embedded types.
+ if mi.Sym == nil || mj.Sym == nil {
+ return mi.Sym != nil
+ }
+
+ // Sort methods by symbol name.
+ return mi.Sym.Less(mj.Sym)
+ })
+ }
+
for _, m := range t.Methods().Slice() {
if m.Sym == nil {
continue
@@ -104,8 +124,17 @@ func expandiface(t *Type) {
continue
}
+ if m.Type.IsUnion() {
+ continue
+ }
+
+ // In 1.18, embedded types can be anything. In Go 1.17, we disallow
+ // embedding anything other than interfaces.
if !m.Type.IsInterface() {
- base.ErrorfAt(m.Pos, "interface contains embedded non-interface %v", m.Type)
+ if AllowsGoVersion(t.Pkg(), 1, 18) {
+ continue
+ }
+ base.ErrorfAt(m.Pos, "interface contains embedded non-interface, non-union %v", m.Type)
m.SetBroke(true)
t.SetBroke(true)
// Add to fields so that error messages
@@ -120,10 +149,15 @@ func expandiface(t *Type) {
// (including broken ones, if any) and add to t's
// method set.
for _, t1 := range m.Type.AllMethods().Slice() {
- // Use m.Pos rather than t1.Pos to preserve embedding position.
f := NewField(m.Pos, t1.Sym, t1.Type)
addMethod(f, false)
+
+ // Clear position after typechecking, for consistency with types2.
+ f.Pos = src.NoXPos
}
+
+ // Clear position after typechecking, for consistency with types2.
+ m.Pos = src.NoXPos
}
sort.Sort(MethodsByName(methods))
@@ -155,19 +189,19 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
}
CalcSize(f.Type)
- if int32(f.Type.Align) > maxalign {
- maxalign = int32(f.Type.Align)
+ if int32(f.Type.align) > maxalign {
+ maxalign = int32(f.Type.align)
}
- if f.Type.Align > 0 {
- o = Rnd(o, int64(f.Type.Align))
+ if f.Type.align > 0 {
+ o = Rnd(o, int64(f.Type.align))
}
if isStruct { // For receiver/args/results, do not set, it depends on ABI
f.Offset = o
}
- w := f.Type.Width
+ w := f.Type.width
if w < 0 {
- base.Fatalf("invalid width %d", f.Type.Width)
+ base.Fatalf("invalid width %d", f.Type.width)
}
if w == 0 {
lastzero = o
@@ -197,10 +231,10 @@ func calcStructOffset(errtype *Type, t *Type, o int64, flag int) int64 {
if flag != 0 {
o = Rnd(o, int64(maxalign))
}
- t.Align = uint8(maxalign)
+ t.align = uint8(maxalign)
// type width only includes back to first field's offset
- t.Width = o - starto
+ t.width = o - starto
return o
}
@@ -316,14 +350,14 @@ func CalcSize(t *Type) {
return
}
- if t.Width == -2 {
+ if t.width == -2 {
reportTypeLoop(t)
- t.Width = 0
- t.Align = 1
+ t.width = 0
+ t.align = 1
return
}
- if t.WidthCalculated() {
+ if t.widthCalculated() {
return
}
@@ -338,7 +372,7 @@ func CalcSize(t *Type) {
// break infinite recursion if the broken recursive type
// is referenced again
- if t.Broke() && t.Width == 0 {
+ if t.Broke() && t.width == 0 {
return
}
@@ -350,8 +384,8 @@ func CalcSize(t *Type) {
base.Pos = pos
}
- t.Width = -2
- t.Align = 0 // 0 means use t.Width, below
+ t.width = -2
+ t.align = 0 // 0 means use t.Width, below
et := t.Kind()
switch et {
@@ -383,15 +417,15 @@ func CalcSize(t *Type) {
case TINT64, TUINT64, TFLOAT64:
w = 8
- t.Align = uint8(RegSize)
+ t.align = uint8(RegSize)
case TCOMPLEX64:
w = 8
- t.Align = 4
+ t.align = 4
case TCOMPLEX128:
w = 16
- t.Align = uint8(RegSize)
+ t.align = uint8(RegSize)
case TPTR:
w = int64(PtrSize)
@@ -402,24 +436,35 @@ func CalcSize(t *Type) {
case TINTER: // implemented as 2 pointers
w = 2 * int64(PtrSize)
- t.Align = uint8(PtrSize)
+ t.align = uint8(PtrSize)
expandiface(t)
+ case TUNION:
+ // Always part of an interface for now, so size/align don't matter.
+ // Pretend a union is represented like an interface.
+ w = 2 * int64(PtrSize)
+ t.align = uint8(PtrSize)
+
case TCHAN: // implemented as pointer
w = int64(PtrSize)
CheckSize(t.Elem())
- // make fake type to check later to
- // trigger channel argument check.
+ // Make fake type to trigger channel element size check after
+ // any top-level recursive type has been completed.
t1 := NewChanArgs(t)
CheckSize(t1)
case TCHANARGS:
t1 := t.ChanArgs()
CalcSize(t1) // just in case
- if t1.Elem().Width >= 1<<16 {
- base.ErrorfAt(typePos(t1), "channel element type too large (>64kB)")
+ // Make sure size of t1.Elem() is calculated at this point. We can
+ // use CalcSize() here rather than CheckSize(), because the top-level
+ // (possibly recursive) type will have been calculated before the fake
+ // chanargs is handled.
+ CalcSize(t1.Elem())
+ if t1.Elem().width >= 1<<16 {
+ base.Errorf("channel element type too large (>64kB)")
}
w = 1 // anything will do
@@ -441,7 +486,7 @@ func CalcSize(t *Type) {
base.Fatalf("early CalcSize string")
}
w = StringSize
- t.Align = uint8(PtrSize)
+ t.align = uint8(PtrSize)
case TARRAY:
if t.Elem() == nil {
@@ -449,14 +494,14 @@ func CalcSize(t *Type) {
}
CalcSize(t.Elem())
- if t.Elem().Width != 0 {
- cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().Width)
+ if t.Elem().width != 0 {
+ cap := (uint64(MaxWidth) - 1) / uint64(t.Elem().width)
if uint64(t.NumElem()) > cap {
- base.ErrorfAt(typePos(t), "type %L larger than address space", t)
+ base.Errorf("type %L larger than address space", t)
}
}
- w = t.NumElem() * t.Elem().Width
- t.Align = t.Elem().Align
+ w = t.NumElem() * t.Elem().width
+ t.align = t.Elem().align
case TSLICE:
if t.Elem() == nil {
@@ -464,7 +509,7 @@ func CalcSize(t *Type) {
}
w = SliceSize
CheckSize(t.Elem())
- t.Align = uint8(PtrSize)
+ t.align = uint8(PtrSize)
case TSTRUCT:
if t.IsFuncArgStruct() {
@@ -486,11 +531,11 @@ func CalcSize(t *Type) {
w = calcStructOffset(t1, t1.Recvs(), 0, 0)
w = calcStructOffset(t1, t1.Params(), w, RegSize)
w = calcStructOffset(t1, t1.Results(), w, RegSize)
- t1.Extra.(*Func).Argwid = w
+ t1.extra.(*Func).Argwid = w
if w%int64(RegSize) != 0 {
base.Warn("bad type %v %d\n", t1, w)
}
- t.Align = 1
+ t.align = 1
case TTYPEPARAM:
// TODO(danscales) - remove when we eliminate the need
@@ -499,15 +544,15 @@ func CalcSize(t *Type) {
}
if PtrSize == 4 && w != int64(int32(w)) {
- base.ErrorfAt(typePos(t), "type %v too large", t)
+ base.Errorf("type %v too large", t)
}
- t.Width = w
- if t.Align == 0 {
+ t.width = w
+ if t.align == 0 {
if w == 0 || w > 8 || w&(w-1) != 0 {
base.Fatalf("invalid alignment for %v", t)
}
- t.Align = uint8(w)
+ t.align = uint8(w)
}
base.Pos = lno
@@ -519,7 +564,19 @@ func CalcSize(t *Type) {
// filling in s.Width and s.Align,
// even if size calculation is otherwise disabled.
func CalcStructSize(s *Type) {
- s.Width = calcStructOffset(s, s, 0, 1) // sets align
+ s.width = calcStructOffset(s, s, 0, 1) // sets align
+}
+
+// RecalcSize is like CalcSize, but recalculates t's size even if it
+// has already been calculated before. It does not recalculate other
+// types.
+func RecalcSize(t *Type) {
+ t.align = 0
+ CalcSize(t)
+}
+
+func (t *Type) widthCalculated() bool {
+ return t.align > 0
}
// when a type's width should be known, we call CheckSize
@@ -582,17 +639,23 @@ func ResumeCheckSize() {
// PtrDataSize returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data.
+//
+// PtrDataSize is only defined for actual Go types. It's an error to
+// use it on compiler-internal types (e.g., TSSA, TRESULTS).
func PtrDataSize(t *Type) int64 {
- if !t.HasPointers() {
+ switch t.Kind() {
+ case TBOOL, TINT8, TUINT8, TINT16, TUINT16, TINT32,
+ TUINT32, TINT64, TUINT64, TINT, TUINT,
+ TUINTPTR, TCOMPLEX64, TCOMPLEX128, TFLOAT32, TFLOAT64:
return 0
- }
- switch t.Kind() {
- case TPTR,
- TUNSAFEPTR,
- TFUNC,
- TCHAN,
- TMAP:
+ case TPTR:
+ if t.Elem().NotInHeap() {
+ return 0
+ }
+ return int64(PtrSize)
+
+ case TUNSAFEPTR, TFUNC, TCHAN, TMAP:
return int64(PtrSize)
case TSTRING:
@@ -606,24 +669,32 @@ func PtrDataSize(t *Type) int64 {
return 2 * int64(PtrSize)
case TSLICE:
+ if t.Elem().NotInHeap() {
+ return 0
+ }
// struct { byte *array; uintgo len; uintgo cap; }
return int64(PtrSize)
case TARRAY:
- // haspointers already eliminated t.NumElem() == 0.
- return (t.NumElem()-1)*t.Elem().Width + PtrDataSize(t.Elem())
+ if t.NumElem() == 0 {
+ return 0
+ }
+ // t.NumElem() > 0
+ size := PtrDataSize(t.Elem())
+ if size == 0 {
+ return 0
+ }
+ return (t.NumElem()-1)*t.Elem().Size() + size
case TSTRUCT:
- // Find the last field that has pointers.
- var lastPtrField *Field
+ // Find the last field that has pointers, if any.
fs := t.Fields().Slice()
for i := len(fs) - 1; i >= 0; i-- {
- if fs[i].Type.HasPointers() {
- lastPtrField = fs[i]
- break
+ if size := PtrDataSize(fs[i].Type); size > 0 {
+ return fs[i].Offset + size
}
}
- return lastPtrField.Offset + PtrDataSize(lastPtrField.Type)
+ return 0
default:
base.Fatalf("PtrDataSize: unexpected type, %v", t)
diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go
index 702893874214874080dfa1a148048303279050d9..d37c1730581c38be38a36d8e067d66deac34b94d 100644
--- a/src/cmd/compile/internal/types/sizeof_test.go
+++ b/src/cmd/compile/internal/types/sizeof_test.go
@@ -21,12 +21,12 @@ func TestSizeof(t *testing.T) {
_64bit uintptr // size on 64bit platforms
}{
{Sym{}, 44, 72},
- {Type{}, 60, 104},
+ {Type{}, 64, 112},
{Map{}, 20, 40},
{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/sort.go b/src/cmd/compile/internal/types/sort.go
index dc59b064153282cd0808a0fd3d2b4d51cd98680b..765c070cd94193c4c37db0f4415d1ee3ea06b031 100644
--- a/src/cmd/compile/internal/types/sort.go
+++ b/src/cmd/compile/internal/types/sort.go
@@ -4,11 +4,16 @@
package types
-// MethodsByName sorts methods by symbol.
+// MethodsByName sorts methods by name.
type MethodsByName []*Field
-func (x MethodsByName) Len() int { return len(x) }
+func (x MethodsByName) Len() int { return len(x) }
+func (x MethodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x MethodsByName) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) }
-func (x MethodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+// EmbeddedsByName sorts embedded types by name.
+type EmbeddedsByName []*Field
-func (x MethodsByName) Less(i, j int) bool { return x[i].Sym.Less(x[j].Sym) }
+func (x EmbeddedsByName) Len() int { return len(x) }
+func (x EmbeddedsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x EmbeddedsByName) Less(i, j int) bool { return x[i].Type.Sym().Less(x[j].Type.Sym()) }
diff --git a/src/cmd/compile/internal/types/sym.go b/src/cmd/compile/internal/types/sym.go
index 534cf7e2376d726e9d3cb3568de4b4d7c5ea9125..fb642f52f881418302738b26d3665f8d2040441f 100644
--- a/src/cmd/compile/internal/types/sym.go
+++ b/src/cmd/compile/internal/types/sym.go
@@ -110,6 +110,14 @@ func (a *Sym) Less(b *Sym) bool {
return false
}
+ // Nil before non-nil.
+ if a == nil {
+ return true
+ }
+ if b == nil {
+ return false
+ }
+
// Exported symbols before non-exported.
ea := IsExported(a.Name)
eb := IsExported(b.Name)
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 1a9aa6916a2faca5a12a999226cea51cef0c4234..7d22e2da234e6fd8b3d075ff956fe198888cb22e 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -8,6 +8,7 @@ import (
"cmd/compile/internal/base"
"cmd/internal/src"
"fmt"
+ "strings"
"sync"
)
@@ -26,12 +27,6 @@ type TypeObject interface {
TypeDefn() *Type // for "type T Defn", returns Defn
}
-// A VarObject is an Object representing a function argument, variable, or struct field.
-type VarObject interface {
- Object
- RecordFrameOffset(int64) // save frame offset
-}
-
//go:generate stringer -type Kind -trimprefix T type.go
// Kind describes a kind of type.
@@ -73,6 +68,7 @@ const (
TSTRING
TUNSAFEPTR
TTYPEPARAM
+ TUNION
// pseudo-types for literals
TIDEAL // untyped numeric constants
@@ -110,32 +106,38 @@ const (
// It also stores pointers to several special types:
// - Types[TANY] is the placeholder "any" type recognized by SubstArgTypes.
// - Types[TBLANK] represents the blank variable's type.
+// - Types[TINTER] is the canonical "interface{}" type.
// - Types[TNIL] represents the predeclared "nil" value's type.
// - Types[TUNSAFEPTR] is package unsafe's Pointer type.
var Types [NTYPE]*Type
var (
- // Predeclared alias types. Kept separate for better error messages.
+ // Predeclared alias types. These are actually created as distinct
+ // defined types for better error messages, but are then specially
+ // treated as identical to their respective underlying types.
+ AnyType *Type
ByteType *Type
RuneType *Type
// Predeclared error interface type.
ErrorType *Type
+ // Predeclared comparable interface type.
+ ComparableType *Type
// Types to represent untyped string and boolean constants.
- UntypedString = New(TSTRING)
- UntypedBool = New(TBOOL)
+ UntypedString = newType(TSTRING)
+ UntypedBool = newType(TBOOL)
// Types to represent untyped numeric constants.
- UntypedInt = New(TIDEAL)
- UntypedRune = New(TIDEAL)
- UntypedFloat = New(TIDEAL)
- UntypedComplex = New(TIDEAL)
+ UntypedInt = newType(TIDEAL)
+ UntypedRune = newType(TIDEAL)
+ UntypedFloat = newType(TIDEAL)
+ UntypedComplex = newType(TIDEAL)
)
// A Type represents a Go type.
type Type struct {
- // Extra contains extra etype-specific fields.
+ // extra contains extra etype-specific fields.
// As an optimization, those etype-specific structs which contain exactly
// one pointer-shaped field are stored as values rather than pointers when possible.
//
@@ -151,11 +153,11 @@ type Type struct {
// TARRAY: *Array
// TSLICE: Slice
// TSSA: string
- // TTYPEPARAM: *Interface (though we may not need to store/use the Interface info)
- Extra interface{}
+ // TTYPEPARAM: *Typeparam
+ extra interface{}
- // Width is the width of this Type in bytes.
- Width int64 // valid if Align > 0
+ // width is the width of this Type in bytes.
+ width int64 // valid if Align > 0
// list of base methods (excluding embedding)
methods Fields
@@ -174,20 +176,27 @@ type Type struct {
}
sym *Sym // symbol containing name, for named types
- Vargen int32 // unique name for OTYPE/ONAME
+ vargen int32 // unique name for OTYPE/ONAME
kind Kind // kind of type
- Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
+ align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
flags bitset8
// For defined (named) generic types, a pointer to the list of type params
- // (in order) of this type that need to be instantiated. For
- // fully-instantiated generic types, this is the targs used to instantiate
- // them (which are used when generating the corresponding instantiated
- // methods). rparams is only set for named types that are generic or are
- // fully-instantiated from a generic type, and is otherwise set to nil.
+ // (in order) of this type that need to be instantiated. For instantiated
+ // generic types, this is the targs used to instantiate them. These targs
+ // may be typeparams (for re-instantiated types such as Value[T2]) or
+ // concrete types (for fully instantiated types such as Value[int]).
+ // rparams is only set for named types that are generic or are fully
+ // instantiated from a generic type, and is otherwise set to nil.
+ // TODO(danscales): choose a better name.
rparams *[]*Type
+
+ // For an instantiated generic type, the symbol for the base generic type.
+ // This backpointer is useful, because the base type is the type that has
+ // the method bodies.
+ origSym *Sym
}
func (*Type) CanBeAnSSAAux() {}
@@ -199,6 +208,8 @@ const (
typeDeferwidth // width computation has been deferred and type is on deferredTypeStack
typeRecur
typeHasTParam // there is a typeparam somewhere in the type (generic function or type)
+ typeIsShape // represents a set of closely related types, for generics
+ typeHasShape // there is a shape somewhere in the type
)
func (t *Type) NotInHeap() bool { return t.flags&typeNotInHeap != 0 }
@@ -207,13 +218,21 @@ func (t *Type) Noalg() bool { return t.flags&typeNoalg != 0 }
func (t *Type) Deferwidth() bool { return t.flags&typeDeferwidth != 0 }
func (t *Type) Recur() bool { return t.flags&typeRecur != 0 }
func (t *Type) HasTParam() bool { return t.flags&typeHasTParam != 0 }
+func (t *Type) IsShape() bool { return t.flags&typeIsShape != 0 }
+func (t *Type) HasShape() bool { return t.flags&typeHasShape != 0 }
func (t *Type) SetNotInHeap(b bool) { t.flags.set(typeNotInHeap, b) }
func (t *Type) SetBroke(b bool) { t.flags.set(typeBroke, b) }
func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) }
func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) }
func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
-func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b) }
+
+// Generic types should never have alg functions.
+func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b); t.flags.set(typeNoalg, b) }
+
+// Should always do SetHasShape(true) when doing SeIsShape(true).
+func (t *Type) SetIsShape(b bool) { t.flags.set(typeIsShape, b) }
+func (t *Type) SetHasShape(b bool) { t.flags.set(typeHasShape, b) }
// Kind returns the kind of type t.
func (t *Type) Kind() Kind { return t.kind }
@@ -222,6 +241,11 @@ func (t *Type) Kind() Kind { return t.kind }
func (t *Type) Sym() *Sym { return t.sym }
func (t *Type) SetSym(sym *Sym) { t.sym = sym }
+// OrigSym returns the name of the original generic type that t is an
+// instantiation of, if any.
+func (t *Type) OrigSym() *Sym { return t.origSym }
+func (t *Type) SetOrigSym(sym *Sym) { t.origSym = sym }
+
// Underlying returns the underlying type of type t.
func (t *Type) Underlying() *Type { return t.underlying }
@@ -255,9 +279,6 @@ func (t *Type) SetRParams(rparams []*Type) {
base.Fatalf("Setting nil or zero-length rparams")
}
t.rparams = &rparams
- if t.HasTParam() {
- return
- }
// HasTParam should be set if any rparam is or has a type param. This is
// to handle the case of a generic type which doesn't reference any of its
// type params (e.g. most commonly, an empty struct).
@@ -266,9 +287,33 @@ func (t *Type) SetRParams(rparams []*Type) {
t.SetHasTParam(true)
break
}
+ if rparam.HasShape() {
+ t.SetHasShape(true)
+ break
+ }
}
}
+// IsBaseGeneric returns true if t is a generic type (not reinstantiated with
+// another type params or fully instantiated.
+func (t *Type) IsBaseGeneric() bool {
+ return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") < 0
+}
+
+// IsInstantiatedGeneric returns t if t ia generic type that has been
+// reinstantiated with new typeparams (i.e. is not fully instantiated).
+func (t *Type) IsInstantiatedGeneric() bool {
+ return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") >= 0 &&
+ t.HasTParam()
+}
+
+// IsFullyInstantiated reports whether t is a fully instantiated generic type; i.e. an
+// instantiated generic type where all type arguments are non-generic or fully
+// instantiated generic types.
+func (t *Type) IsFullyInstantiated() bool {
+ return len(t.RParams()) > 0 && !t.HasTParam()
+}
+
// NoPkg is a nil *Pkg value for clarity.
// It's intended for use when constructing types that aren't exported
// and thus don't need to be associated with any package.
@@ -283,11 +328,11 @@ var NoPkg *Pkg = nil
func (t *Type) Pkg() *Pkg {
switch t.kind {
case TFUNC:
- return t.Extra.(*Func).pkg
+ return t.extra.(*Func).pkg
case TSTRUCT:
- return t.Extra.(*Struct).pkg
+ return t.extra.(*Struct).pkg
case TINTER:
- return t.Extra.(*Interface).pkg
+ return t.extra.(*Interface).pkg
default:
base.Fatalf("Pkg: unexpected kind: %v", t)
return nil
@@ -307,7 +352,7 @@ type Map struct {
// MapType returns t's extra map-specific fields.
func (t *Type) MapType() *Map {
t.wantEtype(TMAP)
- return t.Extra.(*Map)
+ return t.extra.(*Map)
}
// Forward contains Type fields specific to forward types.
@@ -319,7 +364,7 @@ type Forward struct {
// ForwardType returns t's extra forward-type-specific fields.
func (t *Type) ForwardType() *Forward {
t.wantEtype(TFORW)
- return t.Extra.(*Forward)
+ return t.extra.(*Forward)
}
// Func contains Type fields specific to func types.
@@ -340,7 +385,7 @@ type Func struct {
// FuncType returns t's extra func-specific fields.
func (t *Type) FuncType() *Func {
t.wantEtype(TFUNC)
- return t.Extra.(*Func)
+ return t.extra.(*Func)
}
// StructType contains Type fields specific to struct types.
@@ -369,12 +414,25 @@ const (
// StructType returns t's extra struct-specific fields.
func (t *Type) StructType() *Struct {
t.wantEtype(TSTRUCT)
- return t.Extra.(*Struct)
+ return t.extra.(*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.
+type Typeparam struct {
+ index int // type parameter index in source order, starting at 0
+ bound *Type
+}
+
+// Union contains Type fields specific to union types.
+type Union struct {
+ terms []*Type
+ tildes []bool // whether terms[i] is of form ~T
}
// Ptr contains Type fields specific to pointer types.
@@ -401,7 +459,7 @@ type Chan struct {
// ChanType returns t's extra channel-specific fields.
func (t *Type) ChanType() *Chan {
t.wantEtype(TCHAN)
- return t.Extra.(*Chan)
+ return t.extra.(*Chan)
}
type Tuple struct {
@@ -467,7 +525,7 @@ func (f *Field) SetNointerface(b bool) { f.flags.set(fieldNointerface, b) }
// End returns the offset of the first byte immediately after this field.
func (f *Field) End() int64 {
- return f.Offset + f.Type.Width
+ return f.Offset + f.Type.width
}
// IsMethod reports whether f represents a method rather than a struct field.
@@ -527,38 +585,40 @@ func (f *Fields) Append(s ...*Field) {
}
// New returns a new Type of the specified kind.
-func New(et Kind) *Type {
+func newType(et Kind) *Type {
t := &Type{
kind: et,
- Width: BADWIDTH,
+ width: BADWIDTH,
}
t.underlying = t
// TODO(josharian): lazily initialize some of these?
switch t.kind {
case TMAP:
- t.Extra = new(Map)
+ t.extra = new(Map)
case TFORW:
- t.Extra = new(Forward)
+ t.extra = new(Forward)
case TFUNC:
- t.Extra = new(Func)
+ t.extra = new(Func)
case TSTRUCT:
- t.Extra = new(Struct)
+ t.extra = new(Struct)
case TINTER:
- t.Extra = new(Interface)
+ t.extra = new(Interface)
case TPTR:
- t.Extra = Ptr{}
+ t.extra = Ptr{}
case TCHANARGS:
- t.Extra = ChanArgs{}
+ t.extra = ChanArgs{}
case TFUNCARGS:
- t.Extra = FuncArgs{}
+ t.extra = FuncArgs{}
case TCHAN:
- t.Extra = new(Chan)
+ t.extra = new(Chan)
case TTUPLE:
- t.Extra = new(Tuple)
+ t.extra = new(Tuple)
case TRESULTS:
- t.Extra = new(Results)
+ t.extra = new(Results)
case TTYPEPARAM:
- t.Extra = new(Interface)
+ t.extra = new(Typeparam)
+ case TUNION:
+ t.extra = new(Union)
}
return t
}
@@ -568,12 +628,15 @@ func NewArray(elem *Type, bound int64) *Type {
if bound < 0 {
base.Fatalf("NewArray: invalid bound %v", bound)
}
- t := New(TARRAY)
- t.Extra = &Array{Elem: elem, Bound: bound}
+ t := newType(TARRAY)
+ t.extra = &Array{Elem: elem, Bound: bound}
t.SetNotInHeap(elem.NotInHeap())
if elem.HasTParam() {
t.SetHasTParam(true)
}
+ if elem.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
@@ -583,43 +646,55 @@ func NewSlice(elem *Type) *Type {
if t.Elem() != elem {
base.Fatalf("elem mismatch")
}
+ if elem.HasTParam() != t.HasTParam() || elem.HasShape() != t.HasShape() {
+ base.Fatalf("Incorrect HasTParam/HasShape flag for cached slice type")
+ }
return t
}
- t := New(TSLICE)
- t.Extra = Slice{Elem: elem}
+ t := newType(TSLICE)
+ t.extra = Slice{Elem: elem}
elem.cache.slice = t
if elem.HasTParam() {
t.SetHasTParam(true)
}
+ if elem.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
// NewChan returns a new chan Type with direction dir.
func NewChan(elem *Type, dir ChanDir) *Type {
- t := New(TCHAN)
+ t := newType(TCHAN)
ct := t.ChanType()
ct.Elem = elem
ct.Dir = dir
if elem.HasTParam() {
t.SetHasTParam(true)
}
+ if elem.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
func NewTuple(t1, t2 *Type) *Type {
- t := New(TTUPLE)
- t.Extra.(*Tuple).first = t1
- t.Extra.(*Tuple).second = t2
+ t := newType(TTUPLE)
+ t.extra.(*Tuple).first = t1
+ t.extra.(*Tuple).second = t2
if t1.HasTParam() || t2.HasTParam() {
t.SetHasTParam(true)
}
+ if t1.HasShape() || t2.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
func newResults(types []*Type) *Type {
- t := New(TRESULTS)
- t.Extra.(*Results).Types = types
+ t := newType(TRESULTS)
+ t.extra.(*Results).Types = types
return t
}
@@ -631,20 +706,23 @@ func NewResults(types []*Type) *Type {
}
func newSSA(name string) *Type {
- t := New(TSSA)
- t.Extra = name
+ t := newType(TSSA)
+ t.extra = name
return t
}
// NewMap returns a new map Type with key type k and element (aka value) type v.
func NewMap(k, v *Type) *Type {
- t := New(TMAP)
+ t := newType(TMAP)
mt := t.MapType()
mt.Key = k
mt.Elem = v
if k.HasTParam() || v.HasTParam() {
t.SetHasTParam(true)
}
+ if k.HasShape() || v.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
@@ -663,39 +741,39 @@ func NewPtr(elem *Type) *Type {
if t.Elem() != elem {
base.Fatalf("NewPtr: elem mismatch")
}
- if elem.HasTParam() {
- // Extra check when reusing the cache, since the elem
- // might have still been undetermined (i.e. a TFORW type)
- // when this entry was cached.
- t.SetHasTParam(true)
+ if elem.HasTParam() != t.HasTParam() || elem.HasShape() != t.HasShape() {
+ base.Fatalf("Incorrect HasTParam/HasShape flag for cached pointer type")
}
return t
}
- t := New(TPTR)
- t.Extra = Ptr{Elem: elem}
- t.Width = int64(PtrSize)
- t.Align = uint8(PtrSize)
+ t := newType(TPTR)
+ t.extra = Ptr{Elem: elem}
+ t.width = int64(PtrSize)
+ t.align = uint8(PtrSize)
if NewPtrCacheEnabled {
elem.cache.ptr = t
}
if elem.HasTParam() {
t.SetHasTParam(true)
}
+ if elem.HasShape() {
+ t.SetHasShape(true)
+ }
return t
}
// NewChanArgs returns a new TCHANARGS type for channel type c.
func NewChanArgs(c *Type) *Type {
- t := New(TCHANARGS)
- t.Extra = ChanArgs{T: c}
+ t := newType(TCHANARGS)
+ t.extra = ChanArgs{T: c}
return t
}
// NewFuncArgs returns a new TFUNCARGS type for func type f.
func NewFuncArgs(f *Type) *Type {
- t := New(TFUNCARGS)
- t.Extra = FuncArgs{T: f}
+ t := newType(TFUNCARGS)
+ t.extra = FuncArgs{T: f}
return t
}
@@ -734,28 +812,28 @@ func SubstAny(t *Type, types *[]*Type) *Type {
elem := SubstAny(t.Elem(), types)
if elem != t.Elem() {
t = t.copy()
- t.Extra = Ptr{Elem: elem}
+ t.extra = Ptr{Elem: elem}
}
case TARRAY:
elem := SubstAny(t.Elem(), types)
if elem != t.Elem() {
t = t.copy()
- t.Extra.(*Array).Elem = elem
+ t.extra.(*Array).Elem = elem
}
case TSLICE:
elem := SubstAny(t.Elem(), types)
if elem != t.Elem() {
t = t.copy()
- t.Extra = Slice{Elem: elem}
+ t.extra = Slice{Elem: elem}
}
case TCHAN:
elem := SubstAny(t.Elem(), types)
if elem != t.Elem() {
t = t.copy()
- t.Extra.(*Chan).Elem = elem
+ t.extra.(*Chan).Elem = elem
}
case TMAP:
@@ -763,8 +841,8 @@ func SubstAny(t *Type, types *[]*Type) *Type {
elem := SubstAny(t.Elem(), types)
if key != t.Key() || elem != t.Elem() {
t = t.copy()
- t.Extra.(*Map).Key = key
- t.Extra.(*Map).Elem = elem
+ t.extra.(*Map).Key = key
+ t.extra.(*Map).Elem = elem
}
case TFUNC:
@@ -805,26 +883,28 @@ func (t *Type) copy() *Type {
// copy any *T Extra fields, to avoid aliasing
switch t.kind {
case TMAP:
- x := *t.Extra.(*Map)
- nt.Extra = &x
+ x := *t.extra.(*Map)
+ nt.extra = &x
case TFORW:
- x := *t.Extra.(*Forward)
- nt.Extra = &x
+ x := *t.extra.(*Forward)
+ nt.extra = &x
case TFUNC:
- x := *t.Extra.(*Func)
- nt.Extra = &x
+ x := *t.extra.(*Func)
+ nt.extra = &x
case TSTRUCT:
- x := *t.Extra.(*Struct)
- nt.Extra = &x
+ x := *t.extra.(*Struct)
+ nt.extra = &x
case TINTER:
- x := *t.Extra.(*Interface)
- nt.Extra = &x
+ x := *t.extra.(*Interface)
+ nt.extra = &x
case TCHAN:
- x := *t.Extra.(*Chan)
- nt.Extra = &x
+ x := *t.extra.(*Chan)
+ nt.extra = &x
case TARRAY:
- x := *t.Extra.(*Array)
- nt.Extra = &x
+ x := *t.extra.(*Array)
+ nt.extra = &x
+ case TTYPEPARAM:
+ base.Fatalf("typeparam types cannot be copied")
case TTUPLE, TSSA, TRESULTS:
base.Fatalf("ssa types cannot be copied")
}
@@ -891,7 +971,7 @@ var ParamsResults = [2]func(*Type) *Type{
// Key returns the key type of map type t.
func (t *Type) Key() *Type {
t.wantEtype(TMAP)
- return t.Extra.(*Map).Key
+ return t.extra.(*Map).Key
}
// Elem returns the type of elements of t.
@@ -899,15 +979,15 @@ func (t *Type) Key() *Type {
func (t *Type) Elem() *Type {
switch t.kind {
case TPTR:
- return t.Extra.(Ptr).Elem
+ return t.extra.(Ptr).Elem
case TARRAY:
- return t.Extra.(*Array).Elem
+ return t.extra.(*Array).Elem
case TSLICE:
- return t.Extra.(Slice).Elem
+ return t.extra.(Slice).Elem
case TCHAN:
- return t.Extra.(*Chan).Elem
+ return t.extra.(*Chan).Elem
case TMAP:
- return t.Extra.(*Map).Elem
+ return t.extra.(*Map).Elem
}
base.Fatalf("Type.Elem %s", t.kind)
return nil
@@ -916,18 +996,18 @@ func (t *Type) Elem() *Type {
// ChanArgs returns the channel type for TCHANARGS type t.
func (t *Type) ChanArgs() *Type {
t.wantEtype(TCHANARGS)
- return t.Extra.(ChanArgs).T
+ return t.extra.(ChanArgs).T
}
// FuncArgs returns the func type for TFUNCARGS type t.
func (t *Type) FuncArgs() *Type {
t.wantEtype(TFUNCARGS)
- return t.Extra.(FuncArgs).T
+ return t.extra.(FuncArgs).T
}
-// IsFuncArgStruct reports whether t is a struct representing function parameters.
+// IsFuncArgStruct reports whether t is a struct representing function parameters or results.
func (t *Type) IsFuncArgStruct() bool {
- return t.kind == TSTRUCT && t.Extra.(*Struct).Funarg != FunargNone
+ return t.kind == TSTRUCT && t.extra.(*Struct).Funarg != FunargNone
}
// Methods returns a pointer to the base methods (excluding embedding) for type t.
@@ -958,7 +1038,7 @@ func (t *Type) SetAllMethods(fs []*Field) {
// Fields returns the fields of struct type t.
func (t *Type) Fields() *Fields {
t.wantEtype(TSTRUCT)
- return &t.Extra.(*Struct).fields
+ return &t.extra.(*Struct).fields
}
// Field returns the i'th field of struct type t.
@@ -980,7 +1060,7 @@ func (t *Type) SetFields(fields []*Field) {
// Rather than try to track and invalidate those,
// enforce that SetFields cannot be called once
// t's width has been calculated.
- if t.WidthCalculated() {
+ if t.widthCalculated() {
base.Fatalf("SetFields of %v: width previously calculated", t)
}
t.wantEtype(TSTRUCT)
@@ -1004,15 +1084,11 @@ func (t *Type) SetInterface(methods []*Field) {
t.Methods().Set(methods)
}
-func (t *Type) WidthCalculated() bool {
- return t.Align > 0
-}
-
// ArgWidth returns the total aligned argument size for a function.
// It includes the receiver, parameters, and results.
func (t *Type) ArgWidth() int64 {
t.wantEtype(TFUNC)
- return t.Extra.(*Func).Argwid
+ return t.extra.(*Func).Argwid
}
func (t *Type) Size() int64 {
@@ -1023,12 +1099,12 @@ func (t *Type) Size() int64 {
return 0
}
CalcSize(t)
- return t.Width
+ return t.width
}
func (t *Type) Alignment() int64 {
CalcSize(t)
- return int64(t.Align)
+ return int64(t.align)
}
func (t *Type) SimpleString() string {
@@ -1133,6 +1209,12 @@ func (t *Type) cmp(x *Type) Cmp {
if (t == Types[RuneType.kind] || t == RuneType) && (x == Types[RuneType.kind] || x == RuneType) {
return CMPeq
}
+
+ case TINTER:
+ // Make sure named any type matches any empty interface.
+ if t == AnyType && x.IsEmptyInterface() || x == AnyType && t.IsEmptyInterface() {
+ return CMPeq
+ }
}
}
@@ -1142,8 +1224,8 @@ func (t *Type) cmp(x *Type) Cmp {
if x.sym != nil {
// Syms non-nil, if vargens match then equal.
- if t.Vargen != x.Vargen {
- return cmpForNe(t.Vargen < x.Vargen)
+ if t.vargen != x.vargen {
+ return cmpForNe(t.vargen < x.vargen)
}
return CMPeq
}
@@ -1155,8 +1237,8 @@ func (t *Type) cmp(x *Type) Cmp {
return CMPeq
case TSSA:
- tname := t.Extra.(string)
- xname := x.Extra.(string)
+ tname := t.extra.(string)
+ xname := x.extra.(string)
// desire fast sorting, not pretty sorting.
if len(tname) == len(xname) {
if tname == xname {
@@ -1173,16 +1255,16 @@ func (t *Type) cmp(x *Type) Cmp {
return CMPlt
case TTUPLE:
- xtup := x.Extra.(*Tuple)
- ttup := t.Extra.(*Tuple)
+ xtup := x.extra.(*Tuple)
+ ttup := t.extra.(*Tuple)
if c := ttup.first.Compare(xtup.first); c != CMPeq {
return c
}
return ttup.second.Compare(xtup.second)
case TRESULTS:
- xResults := x.Extra.(*Results)
- tResults := t.Extra.(*Results)
+ xResults := x.extra.(*Results)
+ tResults := t.extra.(*Results)
xl, tl := len(xResults.Types), len(tResults.Types)
if tl != xl {
if tl < xl {
@@ -1436,6 +1518,14 @@ func (t *Type) IsInterface() bool {
return t.kind == TINTER
}
+func (t *Type) IsUnion() bool {
+ return t.kind == TUNION
+}
+
+func (t *Type) IsTypeParam() bool {
+ return t.kind == TTYPEPARAM
+}
+
// IsEmptyInterface reports whether t is an empty interface type.
func (t *Type) IsEmptyInterface() bool {
return t.IsInterface() && t.AllMethods().Len() == 0
@@ -1461,7 +1551,7 @@ func (t *Type) PtrTo() *Type {
func (t *Type) NumFields() int {
if t.kind == TRESULTS {
- return len(t.Extra.(*Results).Types)
+ return len(t.extra.(*Results).Types)
}
return t.Fields().Len()
}
@@ -1469,15 +1559,15 @@ func (t *Type) FieldType(i int) *Type {
if t.kind == TTUPLE {
switch i {
case 0:
- return t.Extra.(*Tuple).first
+ return t.extra.(*Tuple).first
case 1:
- return t.Extra.(*Tuple).second
+ return t.extra.(*Tuple).second
default:
panic("bad tuple index")
}
}
if t.kind == TRESULTS {
- return t.Extra.(*Results).Types[i]
+ return t.extra.(*Results).Types[i]
}
return t.Field(i).Type
}
@@ -1490,7 +1580,7 @@ func (t *Type) FieldName(i int) string {
func (t *Type) NumElem() int64 {
t.wantEtype(TARRAY)
- return t.Extra.(*Array).Bound
+ return t.extra.(*Array).Bound
}
type componentsIncludeBlankFields bool
@@ -1552,15 +1642,15 @@ func (t *Type) SoleComponent() *Type {
// The direction will be one of Crecv, Csend, or Cboth.
func (t *Type) ChanDir() ChanDir {
t.wantEtype(TCHAN)
- return t.Extra.(*Chan).Dir
+ return t.extra.(*Chan).Dir
}
func (t *Type) IsMemory() bool {
- if t == TypeMem || t.kind == TTUPLE && t.Extra.(*Tuple).second == TypeMem {
+ if t == TypeMem || t.kind == TTUPLE && t.extra.(*Tuple).second == TypeMem {
return true
}
if t.kind == TRESULTS {
- if types := t.Extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem {
+ if types := t.extra.(*Results).Types; len(types) > 0 && types[len(types)-1] == TypeMem {
return true
}
}
@@ -1589,56 +1679,7 @@ func (t *Type) IsUntyped() bool {
// HasPointers reports whether t contains a heap pointer.
// Note that this function ignores pointers to go:notinheap types.
func (t *Type) HasPointers() bool {
- switch t.kind {
- case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64,
- TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL, TSSA:
- return false
-
- case TARRAY:
- if t.NumElem() == 0 { // empty array has no pointers
- return false
- }
- return t.Elem().HasPointers()
-
- case TSTRUCT:
- for _, t1 := range t.Fields().Slice() {
- if t1.Type.HasPointers() {
- return true
- }
- }
- return false
-
- case TPTR, TSLICE:
- return !t.Elem().NotInHeap()
-
- case TTUPLE:
- ttup := t.Extra.(*Tuple)
- return ttup.first.HasPointers() || ttup.second.HasPointers()
-
- case TRESULTS:
- types := t.Extra.(*Results).Types
- for _, et := range types {
- if et.HasPointers() {
- return true
- }
- }
- return false
- }
-
- return true
-}
-
-// Tie returns 'T' if t is a concrete type,
-// 'I' if t is an interface type, and 'E' if t is an empty interface type.
-// It is used to build calls to the conv* and assert* runtime routines.
-func (t *Type) Tie() byte {
- if t.IsEmptyInterface() {
- return 'E'
- }
- if t.IsInterface() {
- return 'I'
- }
- return 'T'
+ return PtrDataSize(t) > 0
}
var recvType *Type
@@ -1646,11 +1687,15 @@ var recvType *Type
// FakeRecvType returns the singleton type used for interface method receivers.
func FakeRecvType() *Type {
if recvType == nil {
- recvType = NewPtr(New(TSTRUCT))
+ recvType = NewPtr(newType(TSTRUCT))
}
return recvType
}
+func FakeRecv() *Field {
+ return NewField(src.NoXPos, nil, FakeRecvType())
+}
+
var (
// TSSA types. HasPointers assumes these are pointer-free.
TypeInvalid = newSSA("invalid")
@@ -1666,10 +1711,14 @@ var (
// type should be set later via SetUnderlying(). References to the type are
// maintained until the type is filled in, so those references can be updated when
// the type is complete.
-func NewNamed(obj Object) *Type {
- t := New(TFORW)
+func NewNamed(obj TypeObject) *Type {
+ t := newType(TFORW)
t.sym = obj.Sym()
t.nod = obj
+ if t.sym.Pkg == ShapePkg {
+ t.SetIsShape(true)
+ t.SetHasShape(true)
+ }
return t
}
@@ -1681,6 +1730,25 @@ func (t *Type) Obj() Object {
return nil
}
+// typeGen tracks the number of function-scoped defined types that
+// have been declared. It's used to generate unique linker symbols for
+// their runtime type descriptors.
+var typeGen int32
+
+// SetVargen assigns a unique generation number to type t, which must
+// be a defined type declared within function scope. The generation
+// number is used to distinguish it from other similarly spelled
+// defined types from the same package.
+//
+// TODO(mdempsky): Come up with a better solution.
+func (t *Type) SetVargen() {
+ base.Assertf(t.Sym() != nil, "SetVargen on anonymous type %v", t)
+ base.Assertf(t.vargen == 0, "type %v already has Vargen %v", t, t.vargen)
+
+ typeGen++
+ t.vargen = typeGen
+}
+
// SetUnderlying sets the underlying type. SetUnderlying automatically updates any
// types that were waiting for this type to be completed.
func (t *Type) SetUnderlying(underlying *Type) {
@@ -1694,9 +1762,9 @@ func (t *Type) SetUnderlying(underlying *Type) {
// TODO(mdempsky): Fix Type rekinding.
t.kind = underlying.kind
- t.Extra = underlying.Extra
- t.Width = underlying.Width
- t.Align = underlying.Align
+ t.extra = underlying.extra
+ t.width = underlying.width
+ t.align = underlying.align
t.underlying = underlying.underlying
if underlying.NotInHeap() {
@@ -1708,6 +1776,9 @@ func (t *Type) SetUnderlying(underlying *Type) {
if underlying.HasTParam() {
t.SetHasTParam(true)
}
+ if underlying.HasShape() {
+ t.SetHasShape(true)
+ }
// spec: "The declared type does not inherit any methods bound
// to the existing type, but the method set of an interface
@@ -1739,9 +1810,18 @@ func fieldsHasTParam(fields []*Field) bool {
return false
}
+func fieldsHasShape(fields []*Field) bool {
+ for _, f := range fields {
+ if f.Type != nil && f.Type.HasShape() {
+ return true
+ }
+ }
+ return false
+}
+
// NewBasic returns a new basic type of the given kind.
-func NewBasic(kind Kind, obj Object) *Type {
- t := New(kind)
+func newBasic(kind Kind, obj Object) *Type {
+ t := newType(kind)
t.sym = obj.Sym()
t.nod = obj
return t
@@ -1749,8 +1829,8 @@ 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 {
- t := New(TINTER)
+func NewInterface(pkg *Pkg, methods []*Field, implicit bool) *Type {
+ t := newType(TINTER)
t.SetInterface(methods)
for _, f := range methods {
// f.Type could be nil for a broken interface declaration
@@ -1758,22 +1838,101 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type {
t.SetHasTParam(true)
break
}
+ if f.Type != nil && f.Type.HasShape() {
+ t.SetHasShape(true)
+ break
+ }
}
if anyBroke(methods) {
t.SetBroke(true)
}
- t.Extra.(*Interface).pkg = pkg
+ t.extra.(*Interface).pkg = pkg
+ t.extra.(*Interface).implicit = implicit
return t
}
-// NewTypeParam returns a new type param.
-func NewTypeParam(pkg *Pkg) *Type {
- t := New(TTYPEPARAM)
- t.Extra.(*Interface).pkg = pkg
+// NewTypeParam returns a new type param with the specified sym (package and name)
+// and specified index within the typeparam list.
+func NewTypeParam(sym *Sym, index int) *Type {
+ t := newType(TTYPEPARAM)
+ t.sym = sym
+ t.extra.(*Typeparam).index = index
t.SetHasTParam(true)
return t
}
+// Index returns the index of the type param within its param list.
+func (t *Type) Index() int {
+ t.wantEtype(TTYPEPARAM)
+ return t.extra.(*Typeparam).index
+}
+
+// SetIndex sets the index of the type param within its param list.
+func (t *Type) SetIndex(i int) {
+ t.wantEtype(TTYPEPARAM)
+ t.extra.(*Typeparam).index = i
+}
+
+// SetBound sets the bound of a typeparam.
+func (t *Type) SetBound(bound *Type) {
+ t.wantEtype(TTYPEPARAM)
+ t.extra.(*Typeparam).bound = bound
+}
+
+// Bound returns the bound of a typeparam.
+func (t *Type) Bound() *Type {
+ t.wantEtype(TTYPEPARAM)
+ 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 {
+ t := newType(TUNION)
+ if len(terms) != len(tildes) {
+ base.Fatalf("Mismatched terms and tildes for NewUnion")
+ }
+ t.extra.(*Union).terms = terms
+ t.extra.(*Union).tildes = tildes
+ nt := len(terms)
+ for i := 0; i < nt; i++ {
+ if terms[i].HasTParam() {
+ t.SetHasTParam(true)
+ }
+ if terms[i].HasShape() {
+ t.SetHasShape(true)
+ }
+ }
+ return t
+}
+
+// NumTerms returns the number of terms in a union type.
+func (t *Type) NumTerms() int {
+ t.wantEtype(TUNION)
+ return len(t.extra.(*Union).terms)
+}
+
+// Term returns ith term of a union type as (term, tilde). If tilde is true, term
+// represents ~T, rather than just T.
+func (t *Type) Term(i int) (*Type, bool) {
+ t.wantEtype(TUNION)
+ u := t.extra.(*Union)
+ return u.terms[i], u.tildes[i]
+}
+
const BOGUS_FUNARG_OFFSET = -1000000000
func unzeroFieldOffsets(f []*Field) {
@@ -1790,7 +1949,7 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ
recvs = []*Field{recv}
}
- t := New(TFUNC)
+ t := newType(TFUNC)
ft := t.FuncType()
funargs := func(fields []*Field, funarg Funarg) *Type {
@@ -1817,21 +1976,27 @@ func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Typ
fieldsHasTParam(results) {
t.SetHasTParam(true)
}
+ if fieldsHasShape(recvs) || fieldsHasShape(params) || fieldsHasShape(results) {
+ t.SetHasShape(true)
+ }
return t
}
// NewStruct returns a new struct with the given fields.
func NewStruct(pkg *Pkg, fields []*Field) *Type {
- t := New(TSTRUCT)
+ t := newType(TSTRUCT)
t.SetFields(fields)
if anyBroke(fields) {
t.SetBroke(true)
}
- t.Extra.(*Struct).pkg = pkg
+ t.extra.(*Struct).pkg = pkg
if fieldsHasTParam(fields) {
t.SetHasTParam(true)
}
+ if fieldsHasShape(fields) {
+ t.SetHasShape(true)
+ }
return t
}
@@ -2028,7 +2193,7 @@ func TypeSymLookup(name string) *Sym {
}
func TypeSymName(t *Type) string {
- name := t.ShortString()
+ name := t.LinkString()
// Use a separate symbol name for Noalg types for #17752.
if TypeHasNoAlg(t) {
name = "noalg." + name
@@ -2044,3 +2209,5 @@ var (
)
var SimType [NTYPE]Kind
+
+var ShapePkg = NewPkg("go.shape", "go.shape")
diff --git a/src/cmd/compile/internal/types/type_test.go b/src/cmd/compile/internal/types/type_test.go
index fe3f380b21f2d994ec77cd2c1d7be471d202b28b..1fd05b3f5e8c7677beebde97c47b622f5ac0431c 100644
--- a/src/cmd/compile/internal/types/type_test.go
+++ b/src/cmd/compile/internal/types/type_test.go
@@ -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 types_test
+package types
import (
- "cmd/compile/internal/types"
"testing"
)
func TestSSACompare(t *testing.T) {
- a := []*types.Type{
- types.TypeInvalid,
- types.TypeMem,
- types.TypeFlags,
- types.TypeVoid,
- types.TypeInt128,
+ a := []*Type{
+ TypeInvalid,
+ TypeMem,
+ TypeFlags,
+ TypeVoid,
+ TypeInt128,
}
for _, x := range a {
for _, y := range a {
c := x.Compare(y)
- if x == y && c != types.CMPeq || x != y && c == types.CMPeq {
- t.Errorf("%s compare %s == %d\n", x.Extra, y.Extra, c)
+ if x == y && c != CMPeq || x != y && c == CMPeq {
+ t.Errorf("%s compare %s == %d\n", x.extra, y.extra, c)
}
}
}
diff --git a/src/cmd/compile/internal/types/universe.go b/src/cmd/compile/internal/types/universe.go
new file mode 100644
index 0000000000000000000000000000000000000000..54b04bda2239c142eadbed33a112f8c7f5b4215f
--- /dev/null
+++ b/src/cmd/compile/internal/types/universe.go
@@ -0,0 +1,159 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/internal/src"
+)
+
+var basicTypes = [...]struct {
+ name string
+ etype Kind
+}{
+ {"int8", TINT8},
+ {"int16", TINT16},
+ {"int32", TINT32},
+ {"int64", TINT64},
+ {"uint8", TUINT8},
+ {"uint16", TUINT16},
+ {"uint32", TUINT32},
+ {"uint64", TUINT64},
+ {"float32", TFLOAT32},
+ {"float64", TFLOAT64},
+ {"complex64", TCOMPLEX64},
+ {"complex128", TCOMPLEX128},
+ {"bool", TBOOL},
+ {"string", TSTRING},
+}
+
+var typedefs = [...]struct {
+ name string
+ etype Kind
+ sameas32 Kind
+ sameas64 Kind
+}{
+ {"int", TINT, TINT32, TINT64},
+ {"uint", TUINT, TUINT32, TUINT64},
+ {"uintptr", TUINTPTR, TUINT32, TUINT64},
+}
+
+func InitTypes(defTypeName func(sym *Sym, typ *Type) Object) {
+ if PtrSize == 0 {
+ base.Fatalf("typeinit before betypeinit")
+ }
+
+ SlicePtrOffset = 0
+ SliceLenOffset = Rnd(SlicePtrOffset+int64(PtrSize), int64(PtrSize))
+ SliceCapOffset = Rnd(SliceLenOffset+int64(PtrSize), int64(PtrSize))
+ SliceSize = Rnd(SliceCapOffset+int64(PtrSize), int64(PtrSize))
+
+ // string is same as slice wo the cap
+ StringSize = Rnd(SliceLenOffset+int64(PtrSize), int64(PtrSize))
+
+ for et := Kind(0); et < NTYPE; et++ {
+ SimType[et] = et
+ }
+
+ Types[TANY] = newType(TANY) // note: an old placeholder type, NOT the new builtin 'any' alias for interface{}
+ Types[TINTER] = NewInterface(LocalPkg, nil, false)
+ CheckSize(Types[TINTER])
+
+ defBasic := func(kind Kind, pkg *Pkg, name string) *Type {
+ typ := newType(kind)
+ obj := defTypeName(pkg.Lookup(name), typ)
+ typ.sym = obj.Sym()
+ typ.nod = obj
+ if kind != TANY {
+ CheckSize(typ)
+ }
+ return typ
+ }
+
+ for _, s := range &basicTypes {
+ Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name)
+ }
+
+ for _, s := range &typedefs {
+ sameas := s.sameas32
+ if PtrSize == 8 {
+ sameas = s.sameas64
+ }
+ SimType[s.etype] = sameas
+
+ Types[s.etype] = defBasic(s.etype, BuiltinPkg, s.name)
+ }
+
+ // We create separate byte and rune types for better error messages
+ // rather than just creating type alias *Sym's for the uint8 and
+ // int32 Hence, (bytetype|runtype).Sym.isAlias() is false.
+ // TODO(gri) Should we get rid of this special case (at the cost
+ // of less informative error messages involving bytes and runes)?
+ // NOTE(rsc): No, the error message quality is important.
+ // (Alternatively, we could introduce an OTALIAS node representing
+ // type aliases, albeit at the cost of having to deal with it everywhere).
+ ByteType = defBasic(TUINT8, BuiltinPkg, "byte")
+ RuneType = defBasic(TINT32, BuiltinPkg, "rune")
+
+ // error type
+ DeferCheckSize()
+ ErrorType = defBasic(TFORW, BuiltinPkg, "error")
+ ErrorType.SetUnderlying(makeErrorInterface())
+ ResumeCheckSize()
+
+ // comparable type (interface)
+ DeferCheckSize()
+ ComparableType = defBasic(TFORW, BuiltinPkg, "comparable")
+ ComparableType.SetUnderlying(makeComparableInterface())
+ ResumeCheckSize()
+
+ // any type (interface)
+ DeferCheckSize()
+ AnyType = defBasic(TFORW, BuiltinPkg, "any")
+ AnyType.SetUnderlying(NewInterface(BuiltinPkg, []*Field{}, false))
+ ResumeCheckSize()
+
+ if base.Flag.G == 0 {
+ ComparableType.Sym().Def = nil
+ }
+
+ Types[TUNSAFEPTR] = defBasic(TUNSAFEPTR, UnsafePkg, "Pointer")
+
+ Types[TBLANK] = newType(TBLANK)
+ Types[TNIL] = newType(TNIL)
+
+ // simple aliases
+ SimType[TMAP] = TPTR
+ SimType[TCHAN] = TPTR
+ SimType[TFUNC] = TPTR
+ SimType[TUNSAFEPTR] = TPTR
+
+ for et := TINT8; et <= TUINT64; et++ {
+ IsInt[et] = true
+ }
+ IsInt[TINT] = true
+ IsInt[TUINT] = true
+ IsInt[TUINTPTR] = true
+
+ IsFloat[TFLOAT32] = true
+ IsFloat[TFLOAT64] = true
+
+ IsComplex[TCOMPLEX64] = true
+ IsComplex[TCOMPLEX128] = true
+}
+
+func makeErrorInterface() *Type {
+ sig := NewSignature(NoPkg, FakeRecv(), nil, nil, []*Field{
+ NewField(src.NoXPos, nil, Types[TSTRING]),
+ })
+ method := NewField(src.NoXPos, LocalPkg.Lookup("Error"), sig)
+ return NewInterface(NoPkg, []*Field{method}, false)
+}
+
+// makeComparableInterface makes the the predefined "comparable" interface in the
+// built-in package. It has a unique name, but no methods.
+func makeComparableInterface() *Type {
+ return NewInterface(NoPkg, nil, false)
+}
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
index 2939dcc0bdf27356ae1c5a99abfd44c836004060..ed5bced643428dc852cb4eb6fc134132e7e1da2a 100644
--- a/src/cmd/compile/internal/types2/api.go
+++ b/src/cmd/compile/internal/types2/api.go
@@ -55,6 +55,15 @@ func (err Error) FullError() string {
return fmt.Sprintf("%s: %s", err.Pos, err.Full)
}
+// An ArgumentError holds an error associated with an argument index.
+type ArgumentError struct {
+ Index int
+ Err error
+}
+
+func (e *ArgumentError) Error() string { return e.Err.Error() }
+func (e *ArgumentError) Unwrap() error { return e.Err }
+
// An Importer resolves import paths to Packages.
//
// CAUTION: This interface does not support the import of locally
@@ -96,6 +105,10 @@ type ImporterFrom interface {
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
+ // Context is the context used for resolving global identifiers. If nil, the
+ // type checker will initialize this field with a newly created context.
+ Context *Context
+
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
// empty; an empty string indicates the latest language version.
@@ -189,13 +202,27 @@ type Info struct {
// identifier z in a variable declaration 'var z int' is found
// only in the Defs map, and identifiers denoting packages in
// qualified identifiers are collected in the Uses map.
+ //
+ // For binary expressions representing unions in constraint
+ // position or type elements in interfaces, a union type is
+ // recorded for the top-level expression only. For instance,
+ // given the constraint a|b|c, the union type for (a|b)|c
+ // is recorded, but not the union type for a|b.
Types map[syntax.Expr]TypeAndValue
- // Inferred maps calls of parameterized functions that use
- // type inference to the inferred type arguments and signature
- // of the function called. The recorded "call" expression may be
- // an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
- Inferred map[syntax.Expr]Inferred
+ // Instances maps identifiers denoting parameterized types or functions to
+ // their type arguments and instantiated type.
+ //
+ // For example, Instances will map the identifier for 'T' in the type
+ // instantiation T[int, string] to the type arguments [int, string] and
+ // resulting instantiated *Named type. Given a parameterized function
+ // func F[A any](A), Instances will map the identifier for 'F' in the call
+ // expression F(int(1)) to the inferred type arguments [int], and resulting
+ // instantiated *Signature.
+ //
+ // Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
+ // results in an equivalent of Instances[id].Type.
+ Instances map[*syntax.Name]Instance
// Defs maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
@@ -352,11 +379,13 @@ func (tv TypeAndValue) HasOk() bool {
return tv.mode == commaok || tv.mode == mapindex
}
-// Inferred reports the inferred type arguments and signature
-// for a parameterized function call that uses type inference.
-type Inferred struct {
- Targs []Type
- Sig *Signature
+// Instance reports the type arguments and instantiated type for type and
+// function instantiations. For type instantiations, Type will be of dynamic
+// type *Named. For function instantiations, Type will be of dynamic type
+// *Signature.
+type Instance struct {
+ TypeArgs *TypeList
+ Type Type
}
// An Initializer describes a package-level variable, or a list of variables in case
@@ -412,23 +441,31 @@ 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.
func Implements(V Type, T *Interface) bool {
- f, _ := MissingMethod(V, T, true)
- return f == nil
+ if T.Empty() {
+ // All types (even Typ[Invalid]) implement the empty interface.
+ return true
+ }
+ // Checker.implements suppresses errors for invalid types, so we need special
+ // handling here.
+ if V.Underlying() == Typ[Invalid] {
+ return false
+ }
+ return (*Checker)(nil).implements(V, T, nil) == nil
}
// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
- return (*Checker)(nil).identical(x, y)
+ return identical(x, y, true, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
- return (*Checker)(nil).identicalIgnoreTags(x, y)
+ return identical(x, y, false, nil)
}
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
index 873390c1e9280d6b961ad02592e52e16f39096c3..fc8b5cd4ee01edbf48e13a1d8368e2f1ebbef273 100644
--- a/src/cmd/compile/internal/types2/api_test.go
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -7,6 +7,7 @@ package types2_test
import (
"bytes"
"cmd/compile/internal/syntax"
+ "errors"
"fmt"
"internal/testenv"
"reflect"
@@ -17,25 +18,14 @@ import (
. "cmd/compile/internal/types2"
)
-func unimplemented() {
- panic("unimplemented")
-}
-
-// genericPkg is a source prefix for packages that contain generic code.
-const genericPkg = "package generic_"
-
// brokenPkg is a source prefix for packages that are not expected to parse
// or type-check cleanly. They are always parsed assuming that they contain
// generic code.
const brokenPkg = "package broken_"
func parseSrc(path, src string) (*syntax.File, error) {
- var mode syntax.Mode
- if strings.HasPrefix(src, genericPkg) || strings.HasPrefix(src, brokenPkg) {
- mode = syntax.AllowGenerics
- }
errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
- return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, mode)
+ return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, syntax.AllowGenerics)
}
func pkgFor(path, source string, info *Info) (*Package, error) {
@@ -149,6 +139,7 @@ func TestValuesInfo(t *testing.T) {
{`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
{`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341
+ {`package g1; var(j int32; s int; n = 1.0< 0 {
+ if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
}
@@ -82,7 +86,11 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
reason := ""
if ok, _ := x.assignableTo(check, T, &reason); !ok {
if check.conf.CompilerErrorMessages {
- check.errorf(x, "incompatible type: cannot use %s as %s value", x, T)
+ if reason != "" {
+ check.errorf(x, "cannot use %s as type %s in %s:\n\t%s", x, T, context, reason)
+ } else {
+ check.errorf(x, "cannot use %s as type %s in %s", x, T, context)
+ }
} else {
if reason != "" {
check.errorf(x, "cannot use %s as %s value in %s: %s", x, T, context, reason)
@@ -153,6 +161,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
check.assignment(x, lhs.typ, context)
if x.mode == invalid {
+ lhs.used = true // avoid follow-on "declared but not used" errors
return nil
}
@@ -161,7 +170,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
- check.useLHS(lhs)
+ check.use(lhs)
return nil
}
@@ -212,9 +221,6 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
return nil
case variable, mapindex:
// ok
- case nilvalue:
- check.error(&z, "cannot assign to nil") // default would print "untyped nil"
- return nil
default:
if sel, ok := z.expr.(*syntax.SelectorExpr); ok {
var op operand
@@ -236,14 +242,89 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
return x.typ
}
-// If returnPos is valid, initVars is called to type-check the assignment of
-// return expressions, and returnPos is the position of the return statement.
-func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) {
- rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsKnown())
+// operandTypes returns the list of types for the given operands.
+func operandTypes(list []*operand) (res []Type) {
+ for _, x := range list {
+ res = append(res, x.typ)
+ }
+ return res
+}
+
+// varTypes returns the list of types for the given variables.
+func varTypes(list []*Var) (res []Type) {
+ for _, x := range list {
+ res = append(res, x.typ)
+ }
+ return res
+}
+
+// typesSummary returns a string of the form "(t1, t2, ...)" where the
+// ti's are user-friendly string representations for the given types.
+// If variadic is set and the last type is a slice, its string is of
+// the form "...E" where E is the slice's element type.
+func (check *Checker) typesSummary(list []Type, variadic bool) string {
+ var res []string
+ for i, t := range list {
+ var s string
+ switch {
+ case t == nil:
+ fallthrough // should not happend but be cautious
+ case t == Typ[Invalid]:
+ s = ""
+ case isUntyped(t):
+ if isNumeric(t) {
+ // Do not imply a specific type requirement:
+ // "have number, want float64" is better than
+ // "have untyped int, want float64" or
+ // "have int, want float64".
+ s = "number"
+ } else {
+ // If we don't have a number, omit the "untyped" qualifier
+ // for compactness.
+ s = strings.Replace(t.(*Basic).name, "untyped ", "", -1)
+ }
+ case variadic && i == len(list)-1:
+ s = check.sprintf("...%s", t.(*Slice).elem)
+ }
+ if s == "" {
+ s = check.sprintf("%s", t)
+ }
+ res = append(res, s)
+ }
+ return "(" + strings.Join(res, ", ") + ")"
+}
+
+func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
+ measure := func(x int, unit string) string {
+ s := fmt.Sprintf("%d %s", x, unit)
+ if x != 1 {
+ s += "s"
+ }
+ return s
+ }
+
+ vars := measure(nvars, "variable")
+ vals := measure(nvals, "value")
+ rhs0 := rhs[0]
+
+ if len(rhs) == 1 {
+ if call, _ := unparen(rhs0).(*syntax.CallExpr); call != nil {
+ check.errorf(rhs0, "assignment mismatch: %s but %s returns %s", vars, call.Fun, vals)
+ return
+ }
+ }
+ check.errorf(rhs0, "assignment mismatch: %s but %s", vars, vals)
+}
+
+// If returnStmt != nil, initVars is called to type-check the assignment
+// of return expressions, and returnStmt is the the return statement.
+func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt syntax.Stmt) {
+ rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && returnStmt == nil)
if len(lhs) != len(rhs) {
// invalidate lhs
for _, obj := range lhs {
+ obj.used = true // avoid declared but not used errors
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
@@ -254,16 +335,32 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
return
}
}
- if returnPos.IsKnown() {
- check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
+ if returnStmt != nil {
+ var at poser = returnStmt
+ qualifier := "not enough"
+ if len(rhs) > len(lhs) {
+ at = rhs[len(lhs)].expr // report at first extra value
+ qualifier = "too many"
+ } else if len(rhs) > 0 {
+ at = rhs[len(rhs)-1].expr // report at last value
+ }
+ var err error_
+ err.errorf(at, "%s return values", qualifier)
+ err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
+ err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
+ check.report(&err)
return
}
- check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ if check.conf.CompilerErrorMessages {
+ check.assignError(orig_rhs, len(lhs), len(rhs))
+ } else {
+ check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ }
return
}
context := "assignment"
- if returnPos.IsKnown() {
+ if returnStmt != nil {
context = "return statement"
}
@@ -276,8 +373,18 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
return
}
+ ok := true
for i, lhs := range lhs {
- check.initVar(lhs, rhs[i], context)
+ if check.initVar(lhs, rhs[i], context) == nil {
+ ok = false
+ }
+ }
+
+ // avoid follow-on "declared but not used" errors if any initialization failed
+ if !ok {
+ for _, lhs := range lhs {
+ lhs.used = true
+ }
}
}
@@ -285,14 +392,18 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
if len(lhs) != len(rhs) {
- check.useLHS(lhs...)
+ check.use(lhs...)
// don't report an error if we already reported one
for _, x := range rhs {
if x.mode == invalid {
return
}
}
- check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ if check.conf.CompilerErrorMessages {
+ check.assignError(orig_rhs, len(lhs), len(rhs))
+ } else {
+ check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ }
return
}
@@ -305,8 +416,26 @@ func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
return
}
+ ok := true
for i, lhs := range lhs {
- check.assignVar(lhs, rhs[i])
+ if check.assignVar(lhs, rhs[i]) == nil {
+ ok = false
+ }
+ }
+
+ // avoid follow-on "declared but not used" errors if any assignment failed
+ if !ok {
+ // don't call check.use to avoid re-evaluation of the lhs expressions
+ for _, lhs := range lhs {
+ if name, _ := unparen(lhs).(*syntax.Name); name != nil {
+ if obj := check.lookup(name.Value); obj != nil {
+ // see comment in assignVar
+ if v, _ := obj.(*Var); v != nil && v.pkg == check.pkg {
+ v.used = true
+ }
+ }
+ }
+ }
}
}
@@ -337,7 +466,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
for i, lhs := range lhs {
ident, _ := lhs.(*syntax.Name)
if ident == nil {
- check.useLHS(lhs)
+ check.use(lhs)
check.errorf(lhs, "non-name %s on left side of :=", lhs)
hasErr = true
continue
@@ -385,7 +514,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
}
}
- check.initVars(lhsVars, rhs, nopos)
+ check.initVars(lhsVars, rhs, nil)
// process function literals in rhs expressions before scope changes
check.processDelayed(top)
diff --git a/src/cmd/compile/internal/types2/basic.go b/src/cmd/compile/internal/types2/basic.go
new file mode 100644
index 0000000000000000000000000000000000000000..2fd973cafbc5e66022c8455e9073237e15de2c64
--- /dev/null
+++ b/src/cmd/compile/internal/types2/basic.go
@@ -0,0 +1,82 @@
+// Copyright 2011 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
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+ Invalid BasicKind = iota // type is invalid
+
+ // predeclared types
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ String
+ UnsafePointer
+
+ // types for untyped values
+ UntypedBool
+ UntypedInt
+ UntypedRune
+ UntypedFloat
+ UntypedComplex
+ UntypedString
+ UntypedNil
+
+ // aliases
+ Byte = Uint8
+ Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+ IsBoolean BasicInfo = 1 << iota
+ IsInteger
+ IsUnsigned
+ IsFloat
+ IsComplex
+ IsString
+ IsUntyped
+
+ IsOrdered = IsInteger | IsFloat | IsString
+ IsNumeric = IsInteger | IsFloat | IsComplex
+ IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+ kind BasicKind
+ info BasicInfo
+ name string
+}
+
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+func (b *Basic) Underlying() Type { return b }
+func (b *Basic) String() string { return TypeString(b, nil) }
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index f90e06f226791ddb7630c8b5c6d79788378bf486..fcf02a697591ac20864a70d98fe54e6d8a2acbff 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -46,7 +46,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
default:
// make argument getter
xlist, _ := check.exprList(call.ArgList, false)
- arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
+ arg = func(x *operand, i int) { *x = *xlist[i] }
nargs = len(xlist)
// evaluate first argument, if present
if nargs > 0 {
@@ -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, _ := structuralType(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
@@ -129,7 +129,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
arg(&x, i)
xlist = append(xlist, &x)
}
- check.arguments(call, sig, nil, xlist) // discard result (we know the result type)
+ check.arguments(call, sig, nil, xlist, nil) // discard result (we know the result type)
// ok to continue even if check.arguments reported errors
x.mode = value
@@ -144,7 +144,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
mode := invalid
var typ Type
var val constant.Value
- switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
+ switch typ = arrayPtrDeref(under(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
@@ -178,9 +178,12 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
mode = value
}
- case *Sum:
- if t.is(func(t Type) bool {
- switch t := under(t).(type) {
+ case *Interface:
+ if !isTypeParam(x.typ) {
+ break
+ }
+ if t.typeSet().underIs(func(t Type) bool {
+ switch t := arrayPtrDeref(t).(type) {
case *Basic:
if isString(t) && id == _Len {
return true
@@ -212,19 +215,23 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Close:
// close(c)
- c := asChan(x.typ)
- if c == nil {
- check.errorf(x, invalidArg+"%s is not a channel", x)
- return
- }
- if c.dir == RecvOnly {
- check.errorf(x, invalidArg+"%s must not be a receive-only channel", x)
+ if !underIs(x.typ, func(u Type) bool {
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.errorf(x, invalidOp+"cannot close non-channel %s", x)
+ return false
+ }
+ if uch.dir == RecvOnly {
+ check.errorf(x, invalidOp+"cannot close receive-only channel %s", x)
+ return false
+ }
+ return true
+ }) {
return
}
-
x.mode = novalue
if check.Types != nil {
- check.recordBuiltinType(call.Fun, makeSig(nil, c))
+ check.recordBuiltinType(call.Fun, makeSig(nil, x.typ))
}
case _Complex:
@@ -281,14 +288,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
}
// both argument types must be identical
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
check.errorf(x, invalidOp+"%v (mismatched types %s and %s)", call, x.typ, y.typ)
return
}
// 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(!isTypeParam(typ))
+ if t, _ := under(typ).(*Basic); t != nil {
switch t.kind {
case Float32:
return Typ[Complex64]
@@ -321,33 +330,26 @@ 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, _ := structuralType(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
+ src0 := structuralString(y.typ)
+ if src0 != nil && isString(src0) {
+ src0 = NewSlice(universeByte)
}
+ src, _ := src0.(*Slice)
if dst == nil || src == nil {
check.errorf(x, invalidArg+"copy expects slice arguments; found %s and %s", x, &y)
return
}
- if !check.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
}
@@ -358,25 +360,40 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
x.typ = Typ[Int]
case _Delete:
- // delete(m, k)
- m := asMap(x.typ)
- if m == nil {
- check.errorf(x, invalidArg+"%s is not a map", x)
+ // delete(map_, key)
+ // map_ must be a map type or a type parameter describing map types.
+ // The key cannot be a type parameter for now.
+ map_ := x.typ
+ var key Type
+ if !underIs(map_, func(u Type) bool {
+ map_, _ := u.(*Map)
+ if map_ == nil {
+ check.errorf(x, invalidArg+"%s is not a map", x)
+ return false
+ }
+ if key != nil && !Identical(map_.key, key) {
+ check.errorf(x, invalidArg+"maps of %s must have identical key types", x)
+ return false
+ }
+ key = map_.key
+ return true
+ }) {
return
}
+
arg(x, 1) // k
if x.mode == invalid {
return
}
- check.assignment(x, m.key, "argument to delete")
+ check.assignment(x, key, "argument to delete")
if x.mode == invalid {
return
}
x.mode = novalue
if check.Types != nil {
- check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
+ check.recordBuiltinType(call.Fun, makeSig(nil, map_, key))
}
case _Imag, _Real:
@@ -405,8 +422,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(!isTypeParam(typ))
+ if t, _ := under(typ).(*Basic); t != nil {
switch t.kind {
case Complex64:
return Typ[Float32]
@@ -451,39 +470,21 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
return
}
- min, max := -1, 10
- var valid func(t Type) bool
- valid = func(t Type) bool {
- var m int
- switch t := optype(t).(type) {
- case *Slice:
- m = 2
- case *Map, *Chan:
- m = 1
- case *Sum:
- return t.is(valid)
- default:
- return false
- }
- if m > min {
- min = m
- }
- if m+1 < max {
- max = m + 1
- }
- return true
- }
-
- if !valid(T) {
+ var min int // minimum number of arguments
+ switch structuralType(T).(type) {
+ case *Slice:
+ min = 2
+ case *Map, *Chan:
+ min = 1
+ case nil:
+ check.errorf(arg0, invalidArg+"cannot make %s: no structural type", arg0)
+ return
+ default:
check.errorf(arg0, invalidArg+"cannot make %s; type must be slice, map, or channel", arg0)
return
}
- if nargs < min || max < nargs {
- if min == max {
- check.errorf(call, "%v expects %d arguments; found %d", call, min, nargs)
- } else {
- check.errorf(call, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
- }
+ if nargs < min || min+1 < nargs {
+ check.errorf(call, invalidOp+"%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
return
}
@@ -580,7 +581,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Add:
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
if !check.allowVersion(check.pkg, 1, 17) {
- check.error(call.Fun, "unsafe.Add requires go1.17 or later")
+ check.versionErrorf(call.Fun, "go1.17", "unsafe.Add")
return
}
@@ -603,19 +604,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
case _Alignof:
// unsafe.Alignof(x T) uintptr
- if asTypeParam(x.typ) != nil {
- check.errorf(call, invalidOp+"unsafe.Alignof undefined for %s", x)
- return
- }
check.assignment(x, nil, "argument to unsafe.Alignof")
if x.mode == invalid {
return
}
- x.mode = constant_
- x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ if hasVarSize(x.typ) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Offsetof:
// unsafe.Offsetof(x T) uintptr, where x must be a selector
@@ -635,7 +639,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
base := derefStructPtr(x.typ)
sel := selx.Sel.Value
- obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
+ obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
check.errorf(x, invalidArg+"%s has no single field %s", base, sel)
@@ -653,39 +657,61 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
return
}
- // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
+ // TODO(gri) Should we pass x.typ instead of base (and have indirect report if derefStructPtr indirected)?
check.recordSelection(selx, FieldVal, base, obj, index, false)
- offs := check.conf.offsetof(base, index)
- x.mode = constant_
- x.val = constant.MakeInt64(offs)
+ // record the selector expression (was bug - issue #47895)
+ {
+ mode := value
+ if x.mode == variable || indirect {
+ mode = variable
+ }
+ check.record(&operand{mode, selx, obj.Type(), nil, 0})
+ }
+
+ // The field offset is considered a variable even if the field is declared before
+ // the part of the struct which is variable-sized. This makes both the rules
+ // simpler and also permits (or at least doesn't prevent) a compiler from re-
+ // arranging struct fields if it wanted to.
+ if hasVarSize(base) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], obj.Type()))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.offsetof(base, index))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Sizeof:
// unsafe.Sizeof(x T) uintptr
- if asTypeParam(x.typ) != nil {
- check.errorf(call, invalidOp+"unsafe.Sizeof undefined for %s", x)
- return
- }
check.assignment(x, nil, "argument to unsafe.Sizeof")
if x.mode == invalid {
return
}
- x.mode = constant_
- x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ if hasVarSize(x.typ) {
+ x.mode = value
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Uintptr], x.typ))
+ }
+ } else {
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ // result is constant - no need to record signature
+ }
x.typ = Typ[Uintptr]
- // result is constant - no need to record signature
case _Slice:
// unsafe.Slice(ptr *T, len IntegerType) []T
if !check.allowVersion(check.pkg, 1, 17) {
- check.error(call.Fun, "unsafe.Slice requires go1.17 or later")
+ check.versionErrorf(call.Fun, "go1.17", "unsafe.Slice")
return
}
- typ := asPointer(x.typ)
+ typ, _ := under(x.typ).(*Pointer)
if typ == nil {
check.errorf(x, invalidArg+"%s is not a pointer", x)
return
@@ -735,7 +761,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
var t operand
x1 := x
for _, arg := range call.ArgList {
- check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
+ check.rawExpr(x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T))
check.dump("%v: %s", posFor(x1), x1)
x1 = &t // use incoming x only for first argument
}
@@ -748,6 +774,25 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
return true
}
+// hasVarSize reports if the size of type t is variable due to type parameters.
+func hasVarSize(t Type) bool {
+ switch u := under(t).(type) {
+ case *Array:
+ return hasVarSize(u.elem)
+ case *Struct:
+ for _, f := range u.fields {
+ if hasVarSize(f.typ) {
+ return true
+ }
+ }
+ case *Interface:
+ return isTypeParam(t)
+ case *Named, *Union:
+ unreachable()
+ }
+ return false
+}
+
// applyTypeFunc applies f to x. If x is a type parameter,
// the result is a type parameter constrained by an new
// interface bound. The type bounds for that interface
@@ -756,13 +801,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// applyTypeFunc returns nil.
// If x is not a type parameter, the result is f(x).
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
- if tp := asTypeParam(x); tp != nil {
+ if tp, _ := x.(*TypeParam); tp != nil {
// Test if t satisfies the requirements for the argument
// type and collect possible result types at the same time.
- var rtypes []Type
- if !tp.Bound().is(func(x Type) bool {
- if r := f(x); r != nil {
- rtypes = append(rtypes, r)
+ var terms []*Term
+ 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
}
return false
@@ -770,16 +818,12 @@ 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
- tpar := NewTypeName(nopos, nil /* = Universe pkg */, "", nil)
- ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
- tsum := NewSum(rtypes)
- ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
+ // 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
+ ptyp.index = tp.index
return ptyp
}
@@ -803,12 +847,11 @@ func makeSig(res Type, args ...Type) *Signature {
return &Signature{params: params, results: result}
}
-// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// arrayPtrDeref returns A if typ is of the form *A and A is an array;
// otherwise it returns typ.
-//
-func implicitArrayDeref(typ Type) Type {
+func arrayPtrDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
- if a := asArray(p.base); a != nil {
+ if a, _ := under(p.base).(*Array); a != nil {
return a
}
}
diff --git a/src/cmd/compile/internal/types2/builtins_test.go b/src/cmd/compile/internal/types2/builtins_test.go
index 82c786b86ea20e5f8775d533b34d7cca8dadb3c1..be5707cdfeac4dce2820226470cf2b1fc16608a4 100644
--- a/src/cmd/compile/internal/types2/builtins_test.go
+++ b/src/cmd/compile/internal/types2/builtins_test.go
@@ -7,6 +7,7 @@ package types2_test
import (
"cmd/compile/internal/syntax"
"fmt"
+ "strings"
"testing"
. "cmd/compile/internal/types2"
@@ -111,12 +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) 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) 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) 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`},
@@ -149,9 +153,14 @@ func TestBuiltinSignatures(t *testing.T) {
}
}
+func parseGenericSrc(path, src string) (*syntax.File, error) {
+ errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
+ return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, syntax.AllowGenerics)
+}
+
func testBuiltinSignature(t *testing.T, name, src0, want string) {
- src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
- f, err := parseSrc("", src)
+ src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _[P any]() { %s }`, src0)
+ f, err := parseGenericSrc("", src)
if err != nil {
t.Errorf("%s: %s", src0, err)
return
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index 6d149340b285b3e6bd8fc241e7dd5a50a8036370..ed8b67c6074c2d572575e04499fa01e52ca115fc 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -15,6 +15,10 @@ import (
// funcInst type-checks a function instantiation inst and returns the result in x.
// The operand x must be the evaluation of inst.X and its type must be a signature.
func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
+ }
+
xlist := unpackExpr(inst.Index)
targs := check.typeList(xlist)
if targs == nil {
@@ -26,7 +30,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
// check number of type arguments (got) vs number of type parameters (want)
sig := x.typ.(*Signature)
- got, want := len(targs), len(sig.tparams)
+ got, want := len(targs), sig.TypeParams().Len()
if !useConstraintTypeInference && got != want || got > want {
check.errorf(xlist[got-1], "got %d type arguments but want %d", got, want)
x.mode = invalid
@@ -34,10 +38,8 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
return
}
- // if we don't have enough type arguments, try type inference
- inferred := false
if got < want {
- targs = check.infer(inst.Pos(), sig.tparams, targs, nil, nil, true)
+ targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil)
if targs == nil {
// error was already reported
x.mode = invalid
@@ -45,27 +47,48 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
return
}
got = len(targs)
- inferred = true
}
assert(got == want)
- // determine argument positions (for error reporting)
- poslist := make([]syntax.Pos, len(xlist))
- for i, x := range xlist {
- poslist[i] = syntax.StartPos(x)
- }
-
// instantiate function signature
- res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
- assert(res.tparams == nil) // signature is not generic anymore
- if inferred {
- check.recordInferred(inst, targs, res)
- }
+ res := check.instantiateSignature(x.Pos(), sig, targs, xlist)
+ assert(res.TypeParams().Len() == 0) // signature is not generic anymore
+ check.recordInstance(inst.X, targs, res)
x.typ = res
x.mode = value
x.expr = inst
}
+func (check *Checker) instantiateSignature(pos syntax.Pos, typ *Signature, targs []Type, xlist []syntax.Expr) (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.bestContext(nil)).(*Signature)
+ assert(len(xlist) <= 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(xlist) {
+ pos = syntax.StartPos(xlist[i])
+ }
+ check.softErrorf(pos, "%s", err)
+ } else {
+ check.mono.recordInstance(check.pkg, pos, tparams, targs, xlist)
+ }
+
+ 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 {
@@ -79,8 +102,9 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
x.expr = iexpr
check.record(x)
} else {
- check.exprOrType(x, call.Fun)
+ check.exprOrType(x, call.Fun, true)
}
+ // x.typ may be generic
switch x.mode {
case invalid:
@@ -90,6 +114,10 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
case typexpr:
// conversion
+ check.nonGeneric(x)
+ if x.mode == invalid {
+ return conversion
+ }
T := x.typ
x.mode = invalid
switch n := len(call.ArgList); n {
@@ -98,10 +126,9 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
case 1:
check.expr(x, call.ArgList[0])
if x.mode != invalid {
- if t := asInterface(T); t != nil {
- check.completeInterface(nopos, t)
- if t.IsConstraint() {
- check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ if t, _ := under(T).(*Interface); t != nil && !isTypeParam(T) {
+ if !t.IsMethodSet() {
+ check.errorf(call, "cannot use interface %s in conversion (contains specific type constraints or is comparable)", T)
break
}
}
@@ -119,6 +146,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
return conversion
case builtin:
+ // no need to check for non-genericity here
id := x.id
if !check.builtin(x, call, id) {
x.mode = invalid
@@ -132,9 +160,11 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
}
// ordinary function/method call
+ // 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, _ := structuralType(x.typ).(*Signature)
if sig == nil {
check.errorf(x, invalidOp+"cannot call non-function %s", x)
x.mode = invalid
@@ -143,9 +173,10 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
}
// evaluate type arguments, if any
+ var xlist []syntax.Expr
var targs []Type
if inst != nil {
- xlist := unpackExpr(inst.Index)
+ xlist = unpackExpr(inst.Index)
targs = check.typeList(xlist)
if targs == nil {
check.use(call.ArgList...)
@@ -156,7 +187,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
assert(len(targs) == len(xlist))
// check number of type arguments (got) vs number of type parameters (want)
- got, want := len(targs), len(sig.tparams)
+ got, want := len(targs), sig.TypeParams().Len()
if got > want {
check.errorf(xlist[want], "got %d type arguments but want %d", got, want)
check.use(call.ArgList...)
@@ -168,7 +199,13 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
// evaluate arguments
args, _ := check.exprList(call.ArgList, false)
- sig = check.arguments(call, sig, targs, args)
+ isGeneric := sig.TypeParams().Len() > 0
+ sig = check.arguments(call, sig, targs, args, xlist)
+
+ if isGeneric && sig.TypeParams().Len() == 0 {
+ // update the recorded type of call.Fun to its instantiated type
+ check.recordTypeAndValue(call.Fun, value, sig, nil)
+ }
// determine result
switch sig.results.Len() {
@@ -190,7 +227,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
// if type inference failed, a parametrized result must be invalidated
// (operands cannot have a parametrized type)
- if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) {
+ if x.mode == value && sig.TypeParams().Len() > 0 && isParameterized(sig.TypeParams().list(), x.typ) {
x.mode = invalid
}
@@ -237,7 +274,8 @@ func (check *Checker) exprList(elist []syntax.Expr, allowCommaOk bool) (xlist []
return
}
-func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []Type, args []*operand) (rsig *Signature) {
+// xlist is the list of type argument expressions supplied in the source code.
+func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []Type, args []*operand, xlist []syntax.Expr) (rsig *Signature) {
rsig = sig
// TODO(gri) try to eliminate this extra verification loop
@@ -308,42 +346,63 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
}
// check argument count
- switch {
- case nargs < npars:
- check.errorf(call, "not enough arguments in call to %s", call.Fun)
- return
- case nargs > npars:
- check.errorf(args[npars], "too many arguments in call to %s", call.Fun) // report at first extra argument
+ if nargs != npars {
+ var at poser = call
+ qualifier := "not enough"
+ if nargs > npars {
+ at = args[npars].expr // report at first extra argument
+ qualifier = "too many"
+ } else if nargs > 0 {
+ at = args[nargs-1].expr // report at last argument
+ }
+ // take care of empty parameter lists represented by nil tuples
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ var err error_
+ err.errorf(at, "%s arguments in call to %s", qualifier, call.Fun)
+ err.errorf(nopos, "have %s", check.typesSummary(operandTypes(args), false))
+ err.errorf(nopos, "want %s", check.typesSummary(varTypes(params), sig.variadic))
+ check.report(&err)
return
}
// infer type arguments and instantiate signature if necessary
- if len(sig.tparams) > 0 {
- // TODO(gri) provide position information for targs so we can feed
- // it to the instantiate call for better error reporting
- targs = check.infer(call.Pos(), sig.tparams, targs, sigParams, args, true)
+ if sig.TypeParams().Len() > 0 {
+ if !check.allowVersion(check.pkg, 1, 18) {
+ if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
+ check.versionErrorf(iexpr.Pos(), "go1.18", "function instantiation")
+ } else {
+ check.versionErrorf(call.Pos(), "go1.18", "implicit function instantiation")
+ }
+ }
+ targs := check.infer(call.Pos(), sig.TypeParams().list(), targs, sigParams, args)
if targs == nil {
return // error already reported
}
// compute result signature
- rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
- assert(rsig.tparams == nil) // signature is not generic anymore
- check.recordInferred(call, targs, rsig)
+ rsig = check.instantiateSignature(call.Pos(), sig, targs, xlist)
+ assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
+ check.recordInstance(call.Fun, targs, rsig)
// Optimization: Only if the parameter list was adjusted do we
// need to compute it from the adjusted list; otherwise we can
// simply use the result signature's parameter list.
if adjusted {
- sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple)
+ sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.TypeParams().list(), targs), nil).(*Tuple)
} else {
sigParams = rsig.params
}
}
// check arguments
- for i, a := range args {
- check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun))
+ if len(args) > 0 {
+ context := check.sprintf("argument to %s", call.Fun)
+ for i, a := range args {
+ check.assignment(a, sigParams.vars[i].typ, context)
+ }
}
return
@@ -461,14 +520,12 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
}
}
- check.exprOrType(x, e.X)
+ check.exprOrType(x, e.X, false)
if x.mode == invalid {
goto Error
}
- check.instantiatedOperand(x)
-
- obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
switch {
case index != nil:
@@ -478,13 +535,12 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
default:
var why string
- if tpar := asTypeParam(x.typ); tpar != nil {
+ if tpar, _ := x.typ.(*TypeParam); tpar != nil {
// Type parameter bounds don't specify fields, so don't mention "field".
- switch obj := tpar.Bound().obj.(type) {
- case nil:
+ if tname := tpar.iface().obj; tname != nil {
+ why = check.sprintf("interface %s has no method %s", tname.name, sel)
+ } else {
why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
- case *TypeName:
- why = check.sprintf("interface %s has no method %s", obj.name, sel)
}
} else {
why = check.sprintf("type %s has no field or method %s", x.typ, sel)
@@ -498,7 +554,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
} else {
changeCase = string(unicode.ToUpper(r)) + sel[1:]
}
- if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
+ if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
why += ", but does have " + changeCase
}
}
@@ -511,58 +567,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
- // check.dump("### found method %s", m)
check.objDecl(m, nil)
- // If m has a parameterized receiver type, infer the type arguments from
- // the actual receiver provided and then substitute the type parameters in
- // the signature accordingly.
- // TODO(gri) factor this code out
- sig := m.typ.(*Signature)
- if len(sig.rparams) > 0 {
- // For inference to work, we must use the receiver type
- // matching the receiver in the actual method declaration.
- // If the method is embedded, the matching receiver is the
- // embedded struct or interface that declared the method.
- // Traverse the embedding to find that type (issue #44688).
- recv := x.typ
- for i := 0; i < len(index)-1; i++ {
- // The embedded type is either a struct or a pointer to
- // a struct except for the last one (which we don't need).
- recv = asStruct(derefStructPtr(recv)).Field(index[i]).typ
- }
- //check.dump("### recv = %s", recv)
- //check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams)
- // The method may have a pointer receiver, but the actually provided receiver
- // may be a (hopefully addressable) non-pointer value, or vice versa. Here we
- // only care about inferring receiver type parameters; to make the inference
- // work, match up pointer-ness of receiver and argument.
- if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(recv) {
- if ptrRecv {
- recv = NewPointer(recv)
- } else {
- recv = recv.(*Pointer).base
- }
- }
- // Disable reporting of errors during inference below. If we're unable to infer
- // the receiver type arguments here, the receiver must be be otherwise invalid
- // and an error has been reported elsewhere.
- arg := operand{mode: variable, expr: x.expr, typ: recv}
- targs := check.infer(m.pos, sig.rparams, nil, NewTuple(sig.recv), []*operand{&arg}, false /* no error reporting */)
- //check.dump("### inferred targs = %s", targs)
- if targs == nil {
- // We may reach here if there were other errors (see issue #40056).
- goto Error
- }
- // Don't modify m. Instead - for now - make a copy of m and use that instead.
- // (If we modify m, some tests will fail; possibly because the m is in use.)
- // TODO(gri) investigate and provide a correct explanation here
- copy := *m
- copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
- obj = ©
- }
- // TODO(gri) we also need to do substitution for parameterized interface methods
- // (this breaks code in testdata/linalg.go2 at the moment)
- // 12/20/2019: Is this TODO still correct?
}
if x.mode == typexpr {
@@ -576,17 +581,37 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
- // the receiver type becomes the type of the first function
- // argument of the method expression's function type
- var params []*Var
sig := m.typ.(*Signature)
+ if sig.recv == nil {
+ check.error(e, "illegal cycle in method declaration")
+ goto Error
+ }
+
+ // The receiver type becomes the type of the first function
+ // argument of the method expression's function type.
+ var params []*Var
if sig.params != nil {
params = sig.params.vars
}
+ // Be consistent about named/unnamed parameters. This is not needed
+ // for type-checking, but the newly constructed signature may appear
+ // in an error message and then have mixed named/unnamed parameters.
+ // (An alternative would be to not print parameter names in errors,
+ // but it's useful to see them; this is cheap and method expressions
+ // are rare.)
+ name := ""
+ if len(params) > 0 && params[0].name != "" {
+ // name needed
+ name = sig.recv.name
+ if name == "" {
+ name = "_"
+ }
+ }
+ params = append([]*Var{NewVar(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
- params: NewTuple(append([]*Var{NewVar(nopos, check.pkg, "_", x.typ)}, params...)...),
+ params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
@@ -641,56 +666,20 @@ Error:
func (check *Checker) use(arg ...syntax.Expr) {
var x operand
for _, e := range arg {
- // Certain AST fields may legally be nil (e.g., the ast.SliceExpr.High field).
- if e == nil {
- continue
- }
- if l, _ := e.(*syntax.ListExpr); l != nil {
- check.use(l.ElemList...)
+ switch n := e.(type) {
+ case nil:
+ // some AST fields may be nil (e.g., elements of syntax.SliceExpr.Index)
+ // TODO(gri) can those fields really make it here?
continue
- }
- check.rawExpr(&x, e, nil)
- }
-}
-
-// useLHS is like use, but doesn't "use" top-level identifiers.
-// It should be called instead of use if the arguments are
-// expressions on the lhs of an assignment.
-// The arguments must not be nil.
-func (check *Checker) useLHS(arg ...syntax.Expr) {
- var x operand
- for _, e := range arg {
- // If the lhs is an identifier denoting a variable v, this assignment
- // is not a 'use' of v. Remember current value of v.used and restore
- // after evaluating the lhs via check.rawExpr.
- var v *Var
- var v_used bool
- if ident, _ := unparen(e).(*syntax.Name); ident != nil {
- // never type-check the blank name on the lhs
- if ident.Value == "_" {
+ case *syntax.Name:
+ // don't report an error evaluating blank
+ if n.Value == "_" {
continue
}
- if _, obj := check.scope.LookupParent(ident.Value, nopos); obj != nil {
- // It's ok to mark non-local variables, but ignore variables
- // from other packages to avoid potential race conditions with
- // dot-imported variables.
- if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
- v = w
- v_used = v.used
- }
- }
- }
- check.rawExpr(&x, e, nil)
- if v != nil {
- v.used = v_used // restore v.used
+ case *syntax.ListExpr:
+ check.use(n.ElemList...)
+ continue
}
- }
-}
-
-// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid].
-func (check *Checker) instantiatedOperand(x *operand) {
- if x.mode == typexpr && isGeneric(x.typ) {
- check.errorf(x, "cannot use generic type %s without instantiation", x.typ)
- x.typ = Typ[Invalid]
+ check.rawExpr(&x, e, nil, false)
}
}
diff --git a/src/cmd/compile/internal/types2/chan.go b/src/cmd/compile/internal/types2/chan.go
new file mode 100644
index 0000000000000000000000000000000000000000..77650dfb09daad6b56df0138063676b381b84fcc
--- /dev/null
+++ b/src/cmd/compile/internal/types2/chan.go
@@ -0,0 +1,35 @@
+// Copyright 2011 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
+
+// A Chan represents a channel type.
+type Chan struct {
+ dir ChanDir
+ elem Type
+}
+
+// A ChanDir value indicates a channel direction.
+type ChanDir int
+
+// The direction of a channel is indicated by one of these constants.
+const (
+ SendRecv ChanDir = iota
+ SendOnly
+ RecvOnly
+)
+
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ChanDir, elem Type) *Chan {
+ return &Chan{dir: dir, elem: elem}
+}
+
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elem }
+
+func (c *Chan) Underlying() Type { return c }
+func (c *Chan) String() string { return TypeString(c, nil) }
diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go
index 8d6cd1edab9d4c61cd1f1d5d7a99798def7fdcde..aacbb25b3b4f4dc981edf4813acc5168ec1232ca 100644
--- a/src/cmd/compile/internal/types2/check.go
+++ b/src/cmd/compile/internal/types2/check.go
@@ -39,22 +39,24 @@ type exprInfo struct {
val constant.Value // constant value; or nil (if not a constant)
}
-// A context represents the context within which an object is type-checked.
-type context struct {
+// An environment represents the environment within which an object is
+// type-checked.
+type environment struct {
decl *declInfo // package-level declaration whose init expression/function body is checked
scope *Scope // top-most scope for lookups
pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
iota constant.Value // value of iota in a constant declaration; nil otherwise
errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer
+ inTParamList bool // set if inside a type parameter list
sig *Signature // function signature if inside a function; nil otherwise
isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
}
-// lookup looks up name in the current context and returns the matching object, or nil.
-func (ctxt *context) lookup(name string) Object {
- _, obj := ctxt.scope.LookupParent(name, ctxt.pos)
+// lookup looks up name in the current environment and returns the matching object, or nil.
+func (env *environment) lookup(name string) Object {
+ _, obj := env.scope.LookupParent(name, env.pos)
return obj
}
@@ -71,7 +73,29 @@ type importKey struct {
// A dotImportKey describes a dot-imported object in the given scope.
type dotImportKey struct {
scope *Scope
- obj Object
+ 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.
@@ -80,13 +104,13 @@ type Checker struct {
// package information
// (initialized by NewChecker, valid for the life-time of checker)
conf *Config
+ ctxt *Context // context for de-duplicating instances
pkg *Package
*Info
- version version // accepted language version
- objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
- impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
- posMap map[*Interface][]syntax.Pos // maps interface types to lists of embedded interface positions
- typMap map[string]*Named // maps an instantiated named type hash to a *Named type
+ version version // accepted language version
+ nextID uint64 // unique Id for type parameters (first valid Id is 1)
+ objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
+ impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
@@ -101,19 +125,22 @@ type Checker struct {
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
- files []*syntax.File // list of package files
- imports []*PkgName // list of imported packages
- dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
+ files []*syntax.File // list of package files
+ 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)
- context
+ // environment within which the current object is type-checked (valid only
+ // for the duration of type-checking a specific object)
+ environment
// debugging
indent int // indentation for tracing
@@ -144,8 +171,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.
@@ -183,13 +214,12 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
return &Checker{
conf: conf,
+ ctxt: conf.Context,
pkg: pkg,
Info: info,
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
- posMap: make(map[*Interface][]syntax.Pos),
- typMap: make(map[string]*Named),
}
}
@@ -256,6 +286,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print := func(msg string) {
if check.conf.Trace {
+ fmt.Println()
fmt.Println(msg)
}
}
@@ -272,6 +303,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()
@@ -283,9 +317,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
print("== recordUntyped ==")
check.recordUntyped()
- if check.Info != nil {
- print("== sanitizeInfo ==")
- sanitizeInfo(check.Info)
+ if check.firstErr == nil {
+ // TODO(mdempsky): Ensure monomorph is safe when errors exist.
+ check.monomorph()
}
check.pkg.complete = true
@@ -295,6 +329,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
check.dotImportMap = nil
check.pkgPathMap = nil
check.seenPkgMap = nil
+ check.recvTParamMap = nil
+ check.defTypes = nil
+ check.ctxt = nil
// TODO(gri) There's more memory we should release at this point.
@@ -310,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
@@ -365,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}
@@ -418,12 +483,36 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
}
}
-func (check *Checker) recordInferred(call syntax.Expr, targs []Type, sig *Signature) {
- assert(call != nil)
- assert(sig != nil)
- if m := check.Inferred; m != nil {
- m[call] = Inferred{targs, sig}
+// recordInstance records instantiation information into check.Info, if the
+// Instances map is non-nil. The given expr must be an ident, selector, or
+// index (list) expr with ident or selector operand.
+//
+// TODO(rfindley): the expr parameter is fragile. See if we can access the
+// instantiated identifier in some other way.
+func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) {
+ ident := instantiatedIdent(expr)
+ assert(ident != nil)
+ assert(typ != nil)
+ if m := check.Instances; m != nil {
+ m[ident] = Instance{newTypeList(targs), typ}
+ }
+}
+
+func instantiatedIdent(expr syntax.Expr) *syntax.Name {
+ var selOrIdent syntax.Expr
+ switch e := expr.(type) {
+ case *syntax.IndexExpr:
+ selOrIdent = e.X
+ case *syntax.SelectorExpr, *syntax.Name:
+ selOrIdent = e
+ }
+ switch x := selOrIdent.(type) {
+ case *syntax.Name:
+ return x
+ case *syntax.SelectorExpr:
+ return x.Sel
}
+ panic("instantiated ident not found")
}
func (check *Checker) recordDef(id *syntax.Name, obj Object) {
diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go
index 41b0c54702d8d782ad42d4cf0c129f9540919253..f13679d1e3685b8ddb9915df86ae5dfdc0f2796a 100644
--- a/src/cmd/compile/internal/types2/check_test.go
+++ b/src/cmd/compile/internal/types2/check_test.go
@@ -20,14 +20,12 @@
// _ = x /* ERROR "not declared" */ + 1
// }
-// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
-// and test against strict mode.
-
package types2_test
import (
"cmd/compile/internal/syntax"
"flag"
+ "internal/buildcfg"
"internal/testenv"
"os"
"path/filepath"
@@ -96,13 +94,30 @@ func asGoVersion(s string) string {
return ""
}
+// excludedForUnifiedBuild lists files that cannot be tested
+// when using the unified build's export data.
+// TODO(gri) enable as soon as the unified build supports this.
+var excludedForUnifiedBuild = map[string]bool{
+ "issue47818.go2": true,
+ "issue49705.go2": true,
+}
+
func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
if len(filenames) == 0 {
t.Fatal("no source files")
}
+ if buildcfg.Experiment.Unified {
+ for _, f := range filenames {
+ if excludedForUnifiedBuild[filepath.Base(f)] {
+ t.Logf("%s cannot be tested with unified build - skipped", f)
+ return
+ }
+ }
+ }
+
var mode syntax.Mode
- if strings.HasSuffix(filenames[0], ".go2") {
+ if strings.HasSuffix(filenames[0], ".go2") || manual {
mode |= syntax.AllowGenerics
}
// parse files and collect parser errors
@@ -280,7 +295,8 @@ 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 TestCheck(t *testing.T) { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", 55, 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/compilersupport.go b/src/cmd/compile/internal/types2/compilersupport.go
new file mode 100644
index 0000000000000000000000000000000000000000..b35e752b8f7ce51f0e6af8af5b51ede6a81180fe
--- /dev/null
+++ b/src/cmd/compile/internal/types2/compilersupport.go
@@ -0,0 +1,30 @@
+// 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.
+
+// Helper functions exported for the compiler.
+// Do not use internally.
+
+package types2
+
+// If t is a pointer, AsPointer returns that type, otherwise it returns nil.
+func AsPointer(t Type) *Pointer {
+ u, _ := t.Underlying().(*Pointer)
+ return u
+}
+
+// If t is a signature, AsSignature returns that type, otherwise it returns nil.
+func AsSignature(t Type) *Signature {
+ u, _ := t.Underlying().(*Signature)
+ return u
+}
+
+// If typ is a type parameter, structuralType returns the single underlying
+// type of all types in the corresponding type constraint if it exists, or
+// nil otherwise. If the type set contains only unrestricted and restricted
+// channel types (with identical element types), the single underlying type
+// is the restricted channel type if the restrictions are always the same.
+// If typ is not a type parameter, structuralType returns the underlying type.
+func StructuralType(t Type) Type {
+ return structuralType(t)
+}
diff --git a/src/cmd/compile/internal/types2/context.go b/src/cmd/compile/internal/types2/context.go
new file mode 100644
index 0000000000000000000000000000000000000000..7abea6b654b34dd738a707c21c3cd11a3ee48669
--- /dev/null
+++ b/src/cmd/compile/internal/types2/context.go
@@ -0,0 +1,123 @@
+// 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 (
+ "bytes"
+ "fmt"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+// An Context is an opaque type checking context. It may be used to share
+// identical type instances across type-checked packages or calls to
+// Instantiate.
+//
+// It is safe for concurrent use.
+type Context struct {
+ mu sync.Mutex
+ typeMap map[string][]ctxtEntry // type hash -> instances entries
+ nextID int // next unique ID
+ originIDs map[Type]int // origin type -> unique ID
+}
+
+type ctxtEntry struct {
+ orig Type
+ targs []Type
+ instance Type // = orig[targs]
+}
+
+// NewContext creates a new Context.
+func NewContext() *Context {
+ return &Context{
+ typeMap: make(map[string][]ctxtEntry),
+ originIDs: make(map[Type]int),
+ }
+}
+
+// instanceHash returns a string representation of typ instantiated with targs.
+// The hash should be a perfect hash, though out of caution the type checker
+// does not assume this. The result is guaranteed to not contain blanks.
+func (ctxt *Context) instanceHash(orig Type, targs []Type) string {
+ assert(ctxt != nil)
+ assert(orig != nil)
+ var buf bytes.Buffer
+
+ h := newTypeHasher(&buf, ctxt)
+ h.string(strconv.Itoa(ctxt.getID(orig)))
+ // Because we've already written the unique origin ID this call to h.typ is
+ // unnecessary, but we leave it for hash readability. It can be removed later
+ // if performance is an issue.
+ h.typ(orig)
+ if len(targs) > 0 {
+ // TODO(rfindley): consider asserting on isGeneric(typ) here, if and when
+ // isGeneric handles *Signature types.
+ h.typeList(targs)
+ }
+
+ return strings.Replace(buf.String(), " ", "#", -1) // ReplaceAll is not available in Go1.4
+}
+
+// lookup returns an existing instantiation of orig with targs, if it exists.
+// Otherwise, it returns nil.
+func (ctxt *Context) lookup(h string, orig Type, targs []Type) Type {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+
+ for _, e := range ctxt.typeMap[h] {
+ if identicalInstance(orig, targs, e.orig, e.targs) {
+ return e.instance
+ }
+ if debug {
+ // Panic during development to surface any imperfections in our hash.
+ panic(fmt.Sprintf("non-identical instances: (orig: %s, targs: %v) and %s", orig, targs, e.instance))
+ }
+ }
+
+ return nil
+}
+
+// update de-duplicates n against previously seen types with the hash h. If an
+// identical type is found with the type hash h, the previously seen type is
+// returned. Otherwise, n is returned, and recorded in the Context for the hash
+// h.
+func (ctxt *Context) update(h string, orig Type, targs []Type, inst Type) Type {
+ assert(inst != nil)
+
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+
+ for _, e := range ctxt.typeMap[h] {
+ if inst == nil || Identical(inst, e.instance) {
+ return e.instance
+ }
+ if debug {
+ // Panic during development to surface any imperfections in our hash.
+ panic(fmt.Sprintf("%s and %s are not identical", inst, e.instance))
+ }
+ }
+
+ ctxt.typeMap[h] = append(ctxt.typeMap[h], ctxtEntry{
+ orig: orig,
+ targs: targs,
+ instance: inst,
+ })
+
+ return inst
+}
+
+// getID returns a unique ID for the type t.
+func (ctxt *Context) getID(t Type) int {
+ ctxt.mu.Lock()
+ defer ctxt.mu.Unlock()
+ id, ok := ctxt.originIDs[t]
+ if !ok {
+ id = ctxt.nextID
+ ctxt.originIDs[t] = id
+ ctxt.nextID++
+ }
+ return id
+}
diff --git a/src/cmd/compile/internal/types2/context_test.go b/src/cmd/compile/internal/types2/context_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa649b14481f76100988eef817ff1720a9ebd540
--- /dev/null
+++ b/src/cmd/compile/internal/types2/context_test.go
@@ -0,0 +1,69 @@
+// 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 (
+ "testing"
+)
+
+func TestContextHashCollisions(t *testing.T) {
+ if debug {
+ t.Skip("hash collisions are expected, and would fail debug assertions")
+ }
+ // Unit test the de-duplication fall-back logic in Context.
+ //
+ // We can't test this via Instantiate because this is only a fall-back in
+ // case our hash is imperfect.
+ //
+ // These lookups and updates use reasonable looking types in an attempt to
+ // make them robust to internal type assertions, but could equally well use
+ // arbitrary types.
+
+ // Create some distinct origin types. nullaryP and nullaryQ have no
+ // parameters and are identical (but have different type parameter names).
+ // unaryP has a parameter.
+ var nullaryP, nullaryQ, unaryP Type
+ {
+ // type nullaryP = func[P any]()
+ tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface)
+ nullaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false)
+ }
+ {
+ // type nullaryQ = func[Q any]()
+ tparam := NewTypeParam(NewTypeName(nopos, nil, "Q", nil), &emptyInterface)
+ nullaryQ = NewSignatureType(nil, nil, []*TypeParam{tparam}, nil, nil, false)
+ }
+ {
+ // type unaryP = func[P any](_ P)
+ tparam := NewTypeParam(NewTypeName(nopos, nil, "P", nil), &emptyInterface)
+ params := NewTuple(NewVar(nopos, nil, "_", tparam))
+ unaryP = NewSignatureType(nil, nil, []*TypeParam{tparam}, params, nil, false)
+ }
+
+ ctxt := NewContext()
+
+ // Update the context with an instantiation of nullaryP.
+ inst := NewSignatureType(nil, nil, nil, nil, nil, false)
+ if got := ctxt.update("", nullaryP, []Type{Typ[Int]}, inst); got != inst {
+ t.Error("bad")
+ }
+
+ // unaryP is not identical to nullaryP, so we should not get inst when
+ // instantiated with identical type arguments.
+ if got := ctxt.lookup("", unaryP, []Type{Typ[Int]}); got != nil {
+ t.Error("bad")
+ }
+
+ // nullaryQ is identical to nullaryP, so we *should* get inst when
+ // instantiated with identical type arguments.
+ if got := ctxt.lookup("", nullaryQ, []Type{Typ[Int]}); got != inst {
+ t.Error("bad")
+ }
+
+ // ...but verify we don't get inst with different type arguments.
+ if got := ctxt.lookup("", nullaryQ, []Type{Typ[String]}); got != nil {
+ t.Error("bad")
+ }
+}
diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go
index 30201e2b7f4232a147d17a2bdbc8cbb5bc301be3..253868cf931b20adbd7861ad21b094c35a0c245c 100644
--- a/src/cmd/compile/internal/types2/conversions.go
+++ b/src/cmd/compile/internal/types2/conversions.go
@@ -7,6 +7,7 @@
package types2
import (
+ "fmt"
"go/constant"
"unicode"
)
@@ -16,32 +17,74 @@ import (
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant_
- var ok bool
- switch {
- case constArg && isConstType(T):
- // constant conversion
- switch t := asBasic(T); {
- case representableConst(x.val, check, t, &x.val):
- ok = true
+ constConvertibleTo := func(T Type, val *constant.Value) bool {
+ switch t, _ := under(T).(*Basic); {
+ 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 = 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_
+ if check.conf.CompilerErrorMessages {
+ if cause != "" {
+ // Add colon at end of line if we have a following cause.
+ err.errorf(x, "cannot convert %s to type %s:", x, T)
+ err.errorf(nopos, cause)
+ } else {
+ err.errorf(x, "cannot convert %s to type %s", x, T)
+ }
+ } else {
+ err.errorf(x, "cannot convert %s to %s", x, T)
+ if cause != "" {
+ err.errorf(nopos, cause)
+ }
}
+ check.report(&err)
+ x.mode = invalid
return
}
@@ -59,9 +102,9 @@ func (check *Checker) conversion(x *operand, T Type) {
// (See also the TODO below.)
if x.typ == Typ[UntypedNil] {
// ok
- } else if IsInterface(T) || constArg && !isConstType(T) {
+ } else if IsInterface(T) && !isTypeParam(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,108 +123,173 @@ 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
+ // and V and T are not type parameters"
V := x.typ
Vu := under(V)
Tu := under(T)
- if check.identicalIgnoreTags(Vu, Tu) {
+ Vp, _ := V.(*TypeParam)
+ Tp, _ := T.(*TypeParam)
+ if IdenticalIgnoreTags(Vu, Tu) && Vp == nil && Tp == nil {
return true
}
- // "x's type and T are unnamed pointer types and their pointer base types
- // have identical underlying types if tags are ignored"
+ // "V and T are unnamed pointer types and their pointer base types
+ // have identical underlying types if tags are ignored
+ // and their pointer base types are not type parameters"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if check.identicalIgnoreTags(under(V.base), under(T.base)) {
+ if IdenticalIgnoreTags(under(V.base), under(T.base)) && !isTypeParam(V.base) && !isTypeParam(T.base) {
return true
}
}
}
- // "x's type and T are both integer or floating point types"
- if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
+ // "V and T are both integer or floating point types"
+ if isIntegerOrFloat(Vu) && isIntegerOrFloat(Tu) {
return true
}
- // "x's type and T are both complex types"
- if isComplex(V) && isComplex(T) {
+ // "V and T are both complex types"
+ if isComplex(Vu) && isComplex(Tu) {
return true
}
- // "x is an integer or a slice of bytes or runes and T is a string type"
- if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ // "V is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(Vu) || isBytesOrRunes(Vu)) && isString(Tu) {
return true
}
- // "x is a string and T is a slice of bytes or runes"
- if isString(V) && isBytesOrRunes(Tu) {
+ // "V is a string and T is a slice of bytes or runes"
+ if isString(Vu) && isBytesOrRunes(Tu) {
return true
}
// package unsafe:
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
- if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(Tu) {
return true
}
// "and vice versa"
- if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ if isUnsafePointer(Vu) && (isPointer(Tu) || isUintptr(Tu)) {
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 {
- if a := asArray(p.Elem()); a != nil {
- if check.identical(s.Elem(), a.Elem()) {
+ if s, _ := Vu.(*Slice); s != nil {
+ if p, _ := Tu.(*Pointer); p != nil {
+ if a, _ := under(p.Elem()).(*Array); a != nil {
+ if Identical(s.Elem(), a.Elem()) {
if check == nil || check.allowVersion(check.pkg, 1, 17) {
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 {
+ *cause = "conversion of slices to array pointers requires go1.17 or later"
+ if check.conf.CompilerErrorMessages {
+ *cause += fmt.Sprintf(" (-lang was set to %s; check go.mod)", check.conf.GoVersion)
+ }
}
- x.mode = invalid // avoid follow-up error
+ return false
}
}
}
}
+ // optimization: if we don't have type parameters, we're done
+ 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 T == nil {
+ return false // no specific types
+ }
+ 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
}
func isUintptr(typ Type) bool {
- t := asBasic(typ)
+ t, _ := under(typ).(*Basic)
return t != nil && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
- // TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
- // (The former calls under(), while the latter doesn't.)
- // The spec does not say so, but gc claims it is. See also
- // issue 6326.
- t := asBasic(typ)
+ t, _ := under(typ).(*Basic)
return t != nil && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
- return asPointer(typ) != nil
+ _, ok := under(typ).(*Pointer)
+ return ok
}
func isBytesOrRunes(typ Type) bool {
- if s := asSlice(typ); s != nil {
- t := asBasic(s.elem)
+ if s, _ := under(typ).(*Slice); s != nil {
+ t, _ := under(s.elem).(*Basic)
return t != nil && (t.kind == Byte || t.kind == Rune)
}
return false
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
index 1333e4c0eca323c2725b8c24ea733d7919258ff6..a4bc3969c0de1e2ffd44663c8d088c888252ed8f 100644
--- a/src/cmd/compile/internal/types2/decl.go
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -51,7 +51,7 @@ func pathString(path []Object) string {
return s
}
-// objDecl type-checks the declaration of obj in its respective (file) context.
+// objDecl type-checks the declaration of obj in its respective (file) environment.
// For the meaning of def, see Checker.definedType, in typexpr.go.
func (check *Checker) objDecl(obj Object, def *Named) {
if check.conf.Trace && obj.Type() == nil {
@@ -66,6 +66,12 @@ func (check *Checker) objDecl(obj Object, def *Named) {
}()
}
+ // Funcs with m.instRecv set have not yet be completed. Complete them now
+ // so that they have a type when objDecl exits.
+ if m, _ := obj.(*Func); m != nil && m.instRecv != nil {
+ check.completeMethod(nil, m)
+ }
+
// Checking the declaration of obj means inferring its type
// (and possibly its value, for constants).
// An object's type (and thus the object) may be in one of
@@ -118,7 +124,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
fallthrough
case grey:
- // We have a cycle.
+ // We have a (possibly invalid) cycle.
// In the existing code, this is marked by a non-nil type
// for the object except for constants and variables whose
// type may be non-nil (known), or nil if it depends on the
@@ -130,17 +136,17 @@ func (check *Checker) objDecl(obj Object, def *Named) {
// order code.
switch obj := obj.(type) {
case *Const:
- if check.cycle(obj) || obj.typ == nil {
+ if !check.validCycle(obj) || obj.typ == nil {
obj.typ = Typ[Invalid]
}
case *Var:
- if check.cycle(obj) || obj.typ == nil {
+ if !check.validCycle(obj) || obj.typ == nil {
obj.typ = Typ[Invalid]
}
case *TypeName:
- if check.cycle(obj) {
+ if !check.validCycle(obj) {
// break cycle
// (without this, calling underlying()
// below may lead to an endless loop
@@ -150,7 +156,7 @@ func (check *Checker) objDecl(obj Object, def *Named) {
}
case *Func:
- if check.cycle(obj) {
+ if !check.validCycle(obj) {
// Don't set obj.typ to Typ[Invalid] here
// because plenty of code type-asserts that
// functions have a *Signature type. Grey
@@ -172,11 +178,11 @@ func (check *Checker) objDecl(obj Object, def *Named) {
unreachable()
}
- // save/restore current context and setup object context
- defer func(ctxt context) {
- check.context = ctxt
- }(check.context)
- check.context = context{
+ // save/restore current environment and set up object environment
+ defer func(env environment) {
+ check.environment = env
+ }(check.environment)
+ check.environment = environment{
scope: d.file,
}
@@ -204,9 +210,9 @@ func (check *Checker) objDecl(obj Object, def *Named) {
}
}
-// cycle checks if the cycle starting with obj is valid and
+// validCycle reports whether the cycle starting with obj is valid and
// reports an error if it is not.
-func (check *Checker) cycle(obj Object) (isCycle bool) {
+func (check *Checker) validCycle(obj Object) (valid bool) {
// The object map contains the package scope objects and the non-interface methods.
if debug {
info := check.objMap[obj]
@@ -222,13 +228,23 @@ func (check *Checker) cycle(obj Object) (isCycle bool) {
assert(obj.color() >= grey)
start := obj.color() - grey // index of obj in objPath
cycle := check.objPath[start:]
- nval := 0 // number of (constant or variable) values in the cycle
- ndef := 0 // number of type definitions in the cycle
+ tparCycle := false // if set, the cycle is through a type parameter list
+ nval := 0 // number of (constant or variable) values in the cycle; valid if !generic
+ ndef := 0 // number of type definitions in the cycle; valid if !generic
+loop:
for _, obj := range cycle {
switch obj := obj.(type) {
case *Const, *Var:
nval++
case *TypeName:
+ // If we reach a generic type that is part of a cycle
+ // and we are in a type parameter list, we have a cycle
+ // through a type parameter list, which is invalid.
+ if check.inTParamList && isGeneric(obj.typ) {
+ tparCycle = true
+ break loop
+ }
+
// Determine if the type name is an alias or not. For
// package-level objects, use the object map which
// provides syntactic information (which doesn't rely
@@ -256,31 +272,36 @@ func (check *Checker) cycle(obj Object) (isCycle bool) {
if check.conf.Trace {
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
- check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ if tparCycle {
+ check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list")
+ } else {
+ check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ }
defer func() {
- if isCycle {
+ if !valid {
check.trace(obj.Pos(), "=> error: cycle is invalid")
}
}()
}
- // A cycle involving only constants and variables is invalid but we
- // ignore them here because they are reported via the initialization
- // cycle check.
- if nval == len(cycle) {
- return false
- }
+ if !tparCycle {
+ // A cycle involving only constants and variables is invalid but we
+ // ignore them here because they are reported via the initialization
+ // cycle check.
+ if nval == len(cycle) {
+ return true
+ }
- // A cycle involving only types (and possibly functions) must have at least
- // one type definition to be permitted: If there is no type definition, we
- // have a sequence of alias type names which will expand ad infinitum.
- if nval == 0 && ndef > 0 {
- return false // cycle is permitted
+ // A cycle involving only types (and possibly functions) must have at least
+ // one type definition to be permitted: If there is no type definition, we
+ // have a sequence of alias type names which will expand ad infinitum.
+ if nval == 0 && ndef > 0 {
+ return true
+ }
}
check.cycleError(cycle)
-
- return true
+ return false
}
type typeInfo uint
@@ -309,6 +330,13 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
}
+ case *Union:
+ for _, t := range t.terms {
+ if check.validType(t.typ, path) == invalid {
+ return invalid
+ }
+ }
+
case *Interface:
for _, etyp := range t.embeddeds {
if check.validType(etyp, path) == invalid {
@@ -317,6 +345,17 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
case *Named:
+ // 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)
if t.obj.pkg != check.pkg {
@@ -338,20 +377,18 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
// cycle detected
for i, tn := range path {
if t.obj.pkg != check.pkg {
- panic("internal error: type cycle via package-external type")
+ panic("type cycle via package-external type")
}
if tn == t.obj {
check.cycleError(path[i:])
t.info = invalid
- return t.info
+ t.underlying = Typ[Invalid]
+ return invalid
}
}
- panic("internal error: cycle start not found")
+ panic("cycle start not found")
}
return t.info
-
- case *instance:
- return check.validType(t.expand(), path)
}
return valid
@@ -512,84 +549,30 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) {
}
}
- check.initVars(lhs, []syntax.Expr{init}, nopos)
+ check.initVars(lhs, []syntax.Expr{init}, nil)
}
-// under returns the expanded underlying type of n0; possibly by following
-// forward chains of named types. If an underlying type is found, resolve
-// the chain by setting the underlying type for each defined type in the
-// 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.
-func (n0 *Named) under() Type {
- u := n0.underlying
- if u == nil {
- return Typ[Invalid]
- }
-
- // If the underlying type of a defined type is not a defined
- // type, then that is the desired underlying type.
- n := asNamed(u)
- if n == nil {
- return u // common case
- }
-
- // Otherwise, follow the forward chain.
- seen := map[*Named]int{n0: 0}
- path := []Object{n0.obj}
- for {
- u = n.underlying
- if u == nil {
- u = Typ[Invalid]
- break
- }
- n1 := asNamed(u)
- if n1 == nil {
- break // end of chain
- }
-
- seen[n] = len(seen)
- path = append(path, n.obj)
- n = n1
-
- if i, ok := seen[n]; ok {
- // cycle
- // TODO(gri) revert this to a method on Checker. Having a possibly
- // nil Checker on Named and TypeParam is too subtle.
- if n0.check != nil {
- n0.check.cycleError(path[i:])
- }
- u = Typ[Invalid]
- break
- }
- }
-
- for n := range seen {
- // We should never have to update the underlying type of an imported type;
- // those underlying types should have been resolved during the import.
- // Also, doing so would lead to a race condition (was issue #31749).
- // Do this check always, not just in debug more (it's cheap).
- if n0.check != nil && n.obj.pkg != n0.check.pkg {
- panic("internal error: imported type with unresolved underlying type")
- }
- n.underlying = u
- }
-
- return u
-}
-
-func (n *Named) setUnderlying(typ Type) {
- if n != nil {
- n.underlying = typ
+// isImportedConstraint reports whether typ is an imported type constraint.
+func (check *Checker) isImportedConstraint(typ Type) bool {
+ named, _ := typ.(*Named)
+ if named == nil || named.obj.pkg == check.pkg || named.obj.pkg == nil {
+ return false
}
+ u, _ := named.under().(*Interface)
+ return u != nil && !u.IsMethodSet()
}
func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named) {
assert(obj.typ == nil)
+ var rhs Type
check.later(func() {
check.validType(obj.typ, nil)
- })
+ // If typ is local, an error was already reported where typ is specified/defined.
+ if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs)
+ }
+ }).describef(obj, "validType(%s)", obj.Name())
alias := tdecl.Alias
if alias && tdecl.TParamList != nil {
@@ -599,123 +582,136 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
alias = false
}
+ // alias declaration
if alias {
- // type alias declaration
if !check.allowVersion(check.pkg, 1, 9) {
- if check.conf.CompilerErrorMessages {
- check.error(tdecl, "type aliases only supported as of -lang=go1.9")
- } else {
- check.error(tdecl, "type aliases requires go1.9 or later")
- }
+ check.versionErrorf(tdecl, "go1.9", "type aliases")
}
obj.typ = Typ[Invalid]
- obj.typ = check.anyType(tdecl.Type)
-
- } else {
- // defined type declaration
+ rhs = check.varType(tdecl.Type)
+ obj.typ = rhs
+ return
+ }
- named := check.newNamed(obj, nil, nil, nil, nil)
- def.setUnderlying(named)
+ // type definition or generic type declaration
+ named := check.newNamed(obj, nil, nil, nil, nil)
+ def.setUnderlying(named)
- if tdecl.TParamList != nil {
- check.openScope(tdecl, "type parameters")
- defer check.closeScope()
- named.tparams = check.collectTypeParams(tdecl.TParamList)
- }
+ if tdecl.TParamList != nil {
+ check.openScope(tdecl, "type parameters")
+ defer check.closeScope()
+ check.collectTypeParams(&named.tparams, tdecl.TParamList)
+ }
- // determine underlying type of named
- named.fromRHS = check.definedType(tdecl.Type, named)
+ // determine underlying type of 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 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 don't need this restriction anymore if we make the underlying type of a type
+ // parameter its constraint interface: if the RHS is a lone type parameter, we will
+ // use its underlying type (like we do for any RHS in a type declaration), and its
+ // underlying type is an interface and the type declaration is well defined.
+ if isTypeParam(rhs) {
+ check.error(tdecl.Type, "cannot use a type parameter as RHS in type declaration")
+ named.underlying = Typ[Invalid]
+ }
}
-func (check *Checker) collectTypeParams(list []*syntax.Field) (tparams []*TypeName) {
- // Type parameter lists should not be empty. The parser will
- // complain but we still may get an incorrect AST: ignore it.
- if len(list) == 0 {
- return
- }
+func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Field) {
+ tparams := make([]*TypeParam, len(list))
- // Declare type parameters up-front, with empty interface as type bound.
+ // Declare type parameters up-front.
// The scope of type parameters starts at the beginning of the type parameter
- // list (so we can have mutually recursive parameterized interfaces).
- for _, f := range list {
- tparams = check.declareTypeParam(tparams, f.Name)
- }
+ // list (so we can have mutually recursive parameterized type bounds).
+ for i, f := range list {
+ tparams[i] = check.declareTypeParam(f.Name)
+ }
+
+ // Set the type parameters before collecting the type constraints because
+ // the parameterized type may be used by the constraints (issue #47887).
+ // Example: type T[P T[P]] interface{}
+ *dst = bindTParams(tparams)
+
+ // Signal to cycle detection that we are in a type parameter list.
+ // We can only be inside one type parameter list at any given time:
+ // function closures may appear inside a type parameter list but they
+ // cannot be generic, and their bodies are processed in delayed and
+ // sequential fashion. Note that with each new declaration, we save
+ // the existing environment and restore it when done; thus inTParamList
+ // is true exactly only when we are in a specific type parameter list.
+ assert(!check.inTParamList)
+ check.inTParamList = true
+ defer func() {
+ check.inTParamList = false
+ }()
+ // Keep track of bounds for later validation.
var bound Type
- for i, j := 0, 0; i < len(list); i = j {
- f := list[i]
-
- // determine the range of type parameters list[i:j] with identical type bound
- // (declared as in (type a, b, c B))
- j = i + 1
- for j < len(list) && list[j].Type == f.Type {
- j++
- }
-
- // this should never be the case, but be careful
- if f.Type == nil {
- continue
+ var bounds []Type
+ var posers []poser
+ for i, f := range list {
+ // Optimization: Re-use the previous type bound if it hasn't changed.
+ // This also preserves the grouped output of type parameter lists
+ // when printing type strings.
+ if i == 0 || f.Type != list[i-1].Type {
+ bound = check.bound(f.Type)
+ bounds = append(bounds, bound)
+ posers = append(posers, f.Type)
}
+ tparams[i].bound = bound
+ }
- // The predeclared identifier "any" is visible only as a constraint
- // in a type parameter list. Look for it before general constraint
- // resolution.
- if tident, _ := unparen(f.Type).(*syntax.Name); tident != nil && tident.Value == "any" && check.lookup("any") == nil {
- bound = universeAny
- } else {
- bound = check.typ(f.Type)
- }
-
- // type bound must be an interface
- // TODO(gri) We should delay the interface check because
- // we may not have a complete interface yet:
- // type C(type T C) interface {}
- // (issue #39724).
- if _, ok := under(bound).(*Interface); ok {
- // set the type bounds
- for i < j {
- tparams[i].typ.(*TypeParam).bound = bound
- i++
+ check.later(func() {
+ for i, bound := range bounds {
+ if isTypeParam(bound) {
+ // We may be able to allow this since it is now well-defined what
+ // the underlying type and thus type set of a type parameter is.
+ // But we may need some additional form of cycle detection within
+ // type parameter lists.
+ check.error(posers[i], "cannot use a type parameter as constraint")
}
- } else if bound != Typ[Invalid] {
- check.errorf(f.Type, "%s is not an interface", bound)
}
- }
-
- return
+ for _, tpar := range tparams {
+ tpar.iface() // compute type set
+ }
+ })
}
-func (check *Checker) declareTypeParam(tparams []*TypeName, name *syntax.Name) []*TypeName {
- tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
- check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
- check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
- tparams = append(tparams, tpar)
-
- if check.conf.Trace {
- check.trace(name.Pos(), "type param = %v", tparams[len(tparams)-1])
+func (check *Checker) bound(x syntax.Expr) Type {
+ // A type set literal of the form ~T and A|B may only appear as constraint;
+ // embed it in an implicit interface so that only interface type-checking
+ // needs to take care of such type expressions.
+ if op, _ := x.(*syntax.Operation); op != nil && (op.Op == syntax.Tilde || op.Op == syntax.Or) {
+ t := check.typ(&syntax.InterfaceType{MethodList: []*syntax.Field{{Type: x}}})
+ // mark t as implicit interface if all went well
+ if t, _ := t.(*Interface); t != nil {
+ t.implicit = true
+ }
+ return t
}
+ return check.typ(x)
+}
- return tparams
+func (check *Checker) declareTypeParam(name *syntax.Name) *TypeParam {
+ // Use Typ[Invalid] for the type constraint to ensure that a type
+ // is present even if the actual constraint has not been assigned
+ // yet.
+ // TODO(gri) Need to systematically review all uses of type parameter
+ // constraints to make sure we don't rely on them if they
+ // are not properly set yet.
+ tname := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
+ tpar := check.newTypeParam(tname, Typ[Invalid]) // assigns type to tname as a side-effect
+ check.declare(check.scope, name, tname, check.scope.pos) // TODO(gri) check scope position
+ return tpar
}
func (check *Checker) collectMethods(obj *TypeName) {
@@ -735,9 +731,10 @@ func (check *Checker) collectMethods(obj *TypeName) {
// spec: "If the base type is a struct type, the non-blank method
// and field names must be distinct."
- base := asNamed(obj.typ) // shouldn't fail but be conservative
+ base, _ := obj.typ.(*Named) // shouldn't fail but be conservative
if base != nil {
- if t, _ := base.underlying.(*Struct); t != nil {
+ u := base.under()
+ if t, _ := u.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
assert(mset.insert(fld) == nil)
@@ -779,6 +776,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
}
if base != nil {
+ base.resolve(nil) // TODO(mdempsky): Probably unnecessary.
base.methods = append(base.methods, m)
}
}
@@ -805,6 +803,10 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
obj.color_ = saved
+ if len(fdecl.TParamList) > 0 && fdecl.Body == nil {
+ check.softErrorf(fdecl, "parameterized function is missing function body")
+ }
+
// function body must be type-checked after global declarations
// (functions implemented elsewhere have no body)
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
diff --git a/src/cmd/compile/internal/types2/errorcalls_test.go b/src/cmd/compile/internal/types2/errorcalls_test.go
index 28bb33aaffd00443da31ca7086caa1e8cfba37f2..80b05f9f0f06883022a9333d84d93c233f4c7294 100644
--- a/src/cmd/compile/internal/types2/errorcalls_test.go
+++ b/src/cmd/compile/internal/types2/errorcalls_test.go
@@ -18,7 +18,7 @@ func TestErrorCalls(t *testing.T) {
}
for _, file := range files {
- syntax.Walk(file, func(n syntax.Node) bool {
+ syntax.Crawl(file, func(n syntax.Node) bool {
call, _ := n.(*syntax.CallExpr)
if call == nil {
return false
diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go
index af4ecb2300a58cbfe5e8eeae32220fe72129ba2d..c39652fe5e4378bc254151060afedee4feec8cd9 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,13 +85,13 @@ 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:
arg = ""
case operand:
- panic("internal error: should always pass *operand")
+ panic("got operand instead of *operand")
case *operand:
arg = operandString(a, qf)
case syntax.Pos:
@@ -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
}
@@ -111,7 +114,7 @@ func (check *Checker) qualifier(pkg *Package) string {
if check.pkgPathMap == nil {
check.pkgPathMap = make(map[string]map[string]bool)
check.seenPkgMap = make(map[*Package]bool)
- check.markImports(pkg)
+ check.markImports(check.pkg)
}
// If the same package name was used by multiple packages, display the full path.
if len(check.pkgPathMap[pkg.name]) > 1 {
@@ -143,12 +146,12 @@ 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_) {
if err.empty() {
- panic("internal error: reporting no error")
+ panic("no error to report")
}
check.err(err.pos(), err.msg(check.qualifier), err.soft)
}
@@ -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) {
@@ -227,6 +230,16 @@ func (check *Checker) softErrorf(at poser, format string, args ...interface{}) {
check.err(at, check.sprintf(format, args...), true)
}
+func (check *Checker) versionErrorf(at poser, goVersion string, format string, args ...interface{}) {
+ msg := check.sprintf(format, args...)
+ if check.conf.CompilerErrorMessages {
+ msg = fmt.Sprintf("%s requires %s or later (-lang was set to %s; check go.mod)", msg, goVersion, check.conf.GoVersion)
+ } else {
+ msg = fmt.Sprintf("%s requires %s or later", msg, goVersion)
+ }
+ check.err(at, msg, true)
+}
+
// posFor reports the left (= start) position of at.
func posFor(at poser) syntax.Pos {
switch x := at.(type) {
@@ -246,7 +259,7 @@ func stripAnnotations(s string) string {
var b bytes.Buffer
for _, r := range s {
// strip #'s and subscript digits
- if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
+ if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
b.WriteRune(r)
}
}
diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go
index e1f0e83fc97d16f43be86cf4eb169d78e1bba284..ac73ca4650dc4a54852ad20092a7b6df99a4e4ce 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)
@@ -35,7 +35,6 @@ func TestStripAnnotations(t *testing.T) {
{"foo", "foo"},
{"foo₀", "foo"},
{"foo(T₀)", "foo(T)"},
- {"#foo(T₀)", "foo(T)"},
} {
got := stripAnnotations(test.in)
if got != test.want {
diff --git a/src/cmd/compile/internal/types2/example_test.go b/src/cmd/compile/internal/types2/example_test.go
index 714bf77821399683925a986183c984869edc93b2..4edaad580e5fce5869181b35495307a508cc3f8e 100644
--- a/src/cmd/compile/internal/types2/example_test.go
+++ b/src/cmd/compile/internal/types2/example_test.go
@@ -216,36 +216,36 @@ func fib(x int) int {
// var x int:
// defined at fib.go:8:10
// used at 10:10, 12:13, 12:24, 9:5
-
- // TODO(gri) Enable once positions are updated/verified
- // Types and Values of each expression:
- // 4: 8 | string | type : string
- // 6:15 | len | builtin : func(string) int
- // 6:15 | len(b) | value : int
- // 6:19 | b | var : fib.S
- // 6:23 | S | type : fib.S
- // 6:23 | S(c) | value : fib.S
- // 6:25 | c | var : string
- // 6:29 | "hello" | value : string = "hello"
- // 8:12 | int | type : int
- // 8:17 | int | type : int
- // 9: 5 | x | var : int
- // 9: 5 | x < 2 | value : untyped bool
- // 9: 9 | 2 | value : int = 2
- // 10:10 | x | var : int
- // 12: 9 | fib | value : func(x int) int
- // 12: 9 | fib(x - 1) | value : int
- // 12: 9 | fib(x - 1) - fib(x - 2) | value : int
- // 12:13 | x | var : int
- // 12:13 | x - 1 | value : int
- // 12:15 | 1 | value : int = 1
- // 12:20 | fib | value : func(x int) int
- // 12:20 | fib(x - 2) | value : int
- // 12:24 | x | var : int
- // 12:24 | x - 2 | value : int
- // 12:26 | 2 | value : int = 2
}
+// TODO(gri) Enable once positions are updated/verified
+// Types and Values of each expression:
+// 4: 8 | string | type : string
+// 6:15 | len | builtin : func(string) int
+// 6:15 | len(b) | value : int
+// 6:19 | b | var : fib.S
+// 6:23 | S | type : fib.S
+// 6:23 | S(c) | value : fib.S
+// 6:25 | c | var : string
+// 6:29 | "hello" | value : string = "hello"
+// 8:12 | int | type : int
+// 8:17 | int | type : int
+// 9: 5 | x | var : int
+// 9: 5 | x < 2 | value : untyped bool
+// 9: 9 | 2 | value : int = 2
+// 10:10 | x | var : int
+// 12: 9 | fib | value : func(x int) int
+// 12: 9 | fib(x - 1) | value : int
+// 12: 9 | fib(x - 1) - fib(x - 2) | value : int
+// 12:13 | x | var : int
+// 12:13 | x - 1 | value : int
+// 12:15 | 1 | value : int = 1
+// 12:20 | fib | value : func(x int) int
+// 12:20 | fib(x - 2) | value : int
+// 12:24 | x | var : int
+// 12:24 | x - 2 | value : int
+// 12:26 | 2 | value : int = 2
+
func mode(tv types2.TypeAndValue) string {
switch {
case tv.IsVoid():
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 23b79656bb5fb0dabb7c769a27bcb86e10e68b7a..5961f32f37f58314b225d1809fdc2106c20806a9 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,8 +113,10 @@ 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))
+ check.representable(x, under(x.typ).(*Basic))
return
}
@@ -127,9 +129,7 @@ func (check *Checker) overflow(x *operand) {
}
// opName returns the name of an operation, or the empty string.
-// For now, only operations that might overflow are handled.
-// TODO(gri) Expand this to a general mechanism giving names to
-// nodes?
+// Only operations that might overflow are handled.
func opName(e *syntax.Operation) string {
op := int(e.Op)
if e.Y == nil {
@@ -157,6 +157,15 @@ 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 {
+ if tpar, _ := typ.(*TypeParam); tpar != nil {
+ return tpar.underIs(f)
+ }
+ return f(under(typ))
+}
+
func (check *Checker) unary(x *operand, e *syntax.Operation) {
check.expr(x, e.X)
if x.mode == invalid {
@@ -177,19 +186,25 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
return
case syntax.Recv:
- typ := asChan(x.typ)
- if typ == nil {
+ u := structuralType(x.typ)
+ if u == nil {
+ check.errorf(x, invalidOp+"cannot receive from %s: no structural type", x)
+ x.mode = invalid
+ return
+ }
+ ch, _ := u.(*Chan)
+ if ch == nil {
check.errorf(x, invalidOp+"cannot receive from non-channel %s", x)
x.mode = invalid
return
}
- if typ.dir == SendOnly {
+ if ch.dir == SendOnly {
check.errorf(x, invalidOp+"cannot receive from send-only channel %s", x)
x.mode = invalid
return
}
x.mode = commaok
- x.typ = typ.elem
+ x.typ = ch.elem
check.hasCallOrRecv = true
return
}
@@ -597,7 +612,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
- old.typ = asBasic(typ)
+ old.typ = under(typ).(*Basic)
check.untyped[x] = old
return
}
@@ -610,7 +625,7 @@ func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
// If x is the lhs of a shift, its final type must be integer.
// We already know from the shift check that it is representable
// as an integer if it is a constant.
- if !isInteger(typ) {
+ if !allInteger(typ) {
check.errorf(x, invalidOp+"shifted operand %s (type %s) must be integer", x, typ)
return
}
@@ -643,7 +658,11 @@ func (check *Checker) updateExprVal(x syntax.Expr, val constant.Value) {
func (check *Checker) convertUntyped(x *operand, target Type) {
newType, val, code := check.implicitTypeAndValue(x, target)
if code != 0 {
- check.invalidConversion(code, x, target.Underlying())
+ t := target
+ if !isTypeParam(target) {
+ t = safeUnderlying(target)
+ }
+ check.invalidConversion(code, x, t)
x.mode = invalid
return
}
@@ -664,7 +683,6 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
// If x is a constant operand, the returned constant.Value will be the
// representation of x in this context.
func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) {
- target = expand(target)
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
return x.typ, nil, 0
}
@@ -691,10 +709,10 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
return nil, nil, _InvalidUntypedConversion
}
- switch t := optype(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
}
@@ -723,20 +741,23 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
default:
return nil, nil, _InvalidUntypedConversion
}
- case *Sum:
- ok := t.is(func(t Type) bool {
- target, _, _ := check.implicitTypeAndValue(x, t)
- return target != nil
- })
- if !ok {
- return nil, nil, _InvalidUntypedConversion
- }
case *Interface:
+ if isTypeParam(target) {
+ if !u.typeSet().underIs(func(u Type) bool {
+ if u == nil {
+ return false
+ }
+ t, _, _ := check.implicitTypeAndValue(x, u)
+ return t != nil
+ }) {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ break
+ }
// Update operand types to the default type rather than the target
// (interface) type: values must have concrete dynamic types.
// Untyped nil was handled upfront.
- check.completeInterface(nopos, t)
- 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
@@ -760,7 +781,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()
}
@@ -814,7 +835,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 {
@@ -845,12 +866,12 @@ 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) {
- check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y)
+ } else if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ check.versionErrorf(y, "go1.13", invalidOp+"signed shift count %s", y)
x.mode = invalid
return
}
@@ -920,7 +941,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
@@ -934,19 +955,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.And: isInteger,
- syntax.Or: isInteger,
- syntax.Xor: isInteger,
- syntax.AndNot: isInteger,
-
- syntax.AndAnd: isBoolean,
- syntax.OrOr: isBoolean,
+ syntax.Add: allNumericOrString,
+ syntax.Sub: allNumeric,
+ syntax.Mul: allNumeric,
+ syntax.Div: allNumeric,
+ syntax.Rem: allInteger,
+
+ syntax.And: allInteger,
+ syntax.Or: allInteger,
+ syntax.Xor: allInteger,
+ syntax.AndNot: allInteger,
+
+ syntax.AndAnd: allBoolean,
+ syntax.OrOr: allBoolean,
}
}
@@ -972,14 +993,35 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
return
}
- check.convertUntyped(x, y.typ)
- if x.mode == invalid {
- return
+ // TODO(gri) make canMix more efficient - called for each binary operation
+ canMix := func(x, y *operand) bool {
+ if IsInterface(x.typ) && !isTypeParam(x.typ) || IsInterface(y.typ) && !isTypeParam(y.typ) {
+ return true
+ }
+ if allBoolean(x.typ) != allBoolean(y.typ) {
+ return false
+ }
+ if allString(x.typ) != allString(y.typ) {
+ return false
+ }
+ if x.isNil() && !hasNil(y.typ) {
+ return false
+ }
+ if y.isNil() && !hasNil(x.typ) {
+ return false
+ }
+ return true
}
- check.convertUntyped(&y, x.typ)
- if y.mode == invalid {
- x.mode = invalid
- return
+ if canMix(x, &y) {
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
}
if isComparison(op) {
@@ -987,11 +1029,15 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
return
}
- if !check.identical(x.typ, y.typ) {
+ if !Identical(x.typ, y.typ) {
// 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+"mismatched types %s and %s", 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
@@ -1004,7 +1050,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
@@ -1057,8 +1103,10 @@ const (
// rawExpr typechecks expression e and initializes x with the expression
// value or type. If an error occurred, x.mode is set to invalid.
// If hint != nil, it is the type of a composite literal element.
+// If allowGeneric is set, the operand type may be an uninstantiated
+// parameterized type or function value.
//
-func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type) exprKind {
+func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type, allowGeneric bool) exprKind {
if check.conf.Trace {
check.trace(e.Pos(), "expr %s", e)
check.indent++
@@ -1069,11 +1117,40 @@ func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type) exprKind {
}
kind := check.exprInternal(x, e, hint)
+
+ if !allowGeneric {
+ check.nonGeneric(x)
+ }
+
check.record(x)
return kind
}
+// If x is a generic function or type, nonGeneric reports an error and invalidates x.mode and x.typ.
+// Otherwise it leaves x alone.
+func (check *Checker) nonGeneric(x *operand) {
+ if x.mode == invalid || x.mode == novalue {
+ return
+ }
+ var what string
+ switch t := x.typ.(type) {
+ case *Named:
+ if isGeneric(t) {
+ what = "type"
+ }
+ case *Signature:
+ if t.tparams != nil {
+ what = "function"
+ }
+ }
+ if what != "" {
+ check.errorf(x.expr, "cannot use generic %s %s without instantiation", what, x.expr)
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+ }
+}
+
// exprInternal contains the core of type checking of expressions.
// Must only be called by rawExpr.
//
@@ -1176,7 +1253,11 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
case hint != nil:
// no composite literal type present - use hint (element type of enclosing type)
typ = hint
- base, _ = deref(under(typ)) // *T implies &T{}
+ base = typ
+ if !isTypeParam(typ) {
+ base = under(typ)
+ }
+ base, _ = deref(base) // *T implies &T{}
default:
// TODO(gri) provide better error messages depending on context
@@ -1184,8 +1265,14 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
goto Error
}
- switch utyp := optype(base).(type) {
+ switch utyp := structuralType(base).(type) {
case *Struct:
+ // Prevent crash if the struct referred to is not yet set up.
+ // See analogous comment for *Array.
+ if utyp.fields == nil {
+ check.error(e, "illegal cycle in type declaration")
+ goto Error
+ }
if len(e.ElemList) == 0 {
break
}
@@ -1314,9 +1401,9 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
- if asInterface(utyp.key) != nil {
+ if IsInterface(utyp.key) {
for _, vtyp := range visited[xkey] {
- if check.identical(vtyp, x.typ) {
+ if Identical(vtyp, x.typ) {
duplicate = true
break
}
@@ -1358,7 +1445,7 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
x.typ = typ
case *syntax.ParenExpr:
- kind := check.rawExpr(x, e.X, nil)
+ kind := check.rawExpr(x, e.X, nil, false)
x.expr = e
return kind
@@ -1384,12 +1471,16 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
if x.mode == invalid {
goto Error
}
+ // TODO(gri) we may want to permit type assertions on type parameter values at some point
+ if isTypeParam(x.typ) {
+ check.errorf(x, invalidOp+"cannot use type assertion on type parameter value %s", x)
+ goto Error
+ }
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
- check.errorf(x, "%s is not an interface type", x)
+ check.errorf(x, invalidOp+"%s is not an interface", x)
goto Error
}
- check.ordinaryType(x.Pos(), xtyp)
// x.(type) expressions are encoded via TypeSwitchGuards
if e.Type == nil {
check.error(e, invalidAST+"invalid use of AssertExpr")
@@ -1399,7 +1490,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
@@ -1441,20 +1532,31 @@ func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKin
// unary expression
if e.Op == syntax.Mul {
// pointer indirection
- check.exprOrType(x, e.X)
+ check.exprOrType(x, e.X, false)
switch x.mode {
case invalid:
goto Error
case typexpr:
x.typ = &Pointer{base: x.typ}
default:
- if typ := asPointer(x.typ); typ != nil {
- x.mode = variable
- x.typ = typ.base
- } else {
- check.errorf(x, invalidOp+"cannot indirect %s", x)
+ var base Type
+ if !underIs(x.typ, func(u Type) bool {
+ p, _ := u.(*Pointer)
+ if p == nil {
+ check.errorf(x, invalidOp+"cannot indirect %s", x)
+ return false
+ }
+ if base != nil && !Identical(p.base, base) {
+ check.errorf(x, invalidOp+"pointers of %s must have identical base types", x)
+ return false
+ }
+ base = p.base
+ return true
+ }) {
goto Error
}
+ x.mode = variable
+ x.typ = base
}
break
}
@@ -1530,26 +1632,27 @@ 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 err error_
var msg string
- if wrongType != nil {
- if check.identical(method.typ, wrongType.typ) {
- msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
- } else {
- msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
- }
- } else {
- msg = "missing method " + method.name
- }
- if check.conf.CompilerErrorMessages {
- check.errorf(pos, "impossible type assertion: %s (%s)", x, msg)
+ if typeSwitch {
+ err.errorf(e.Pos(), "impossible type switch case: %s", e)
+ msg = check.sprintf("%s cannot have dynamic type %s %s", x, T,
+ check.missingMethodReason(T, x.typ, method, wrongType))
+
} else {
- check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
+ err.errorf(e.Pos(), "impossible type assertion: %s", e)
+ msg = check.sprintf("%s does not implement %s %s", T, x.typ,
+ check.missingMethodReason(T, x.typ, method, wrongType))
+
}
+ err.errorf(nopos, msg)
+ check.report(&err)
}
// expr typechecks expression e and initializes x with the expression value.
@@ -1557,14 +1660,14 @@ func (check *Checker) typeAssertion(pos syntax.Pos, x *operand, xtyp *Interface,
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) expr(x *operand, e syntax.Expr) {
- check.rawExpr(x, e, nil)
+ check.rawExpr(x, e, nil, false)
check.exclude(x, 1< 0 {
+ if sig, _ := under(x.typ).(*Signature); sig != nil && sig.TypeParams().Len() > 0 {
// function instantiation
return true
}
}
+ // x should not be generic at this point, but be safe and check
+ check.nonGeneric(x)
+ if x.mode == invalid {
+ return false
+ }
+
// ordinary index expression
valid := false
length := int64(-1) // valid if >= 0
- switch typ := optype(x.typ).(type) {
+ switch typ := under(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
@@ -64,7 +72,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
x.typ = typ.elem
case *Pointer:
- if typ := asArray(typ.base); typ != nil {
+ if typ, _ := under(typ.base).(*Array); typ != nil {
valid = true
length = typ.len
x.mode = variable
@@ -80,7 +88,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
var key operand
check.expr(&key, index)
@@ -89,101 +97,100 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
x.mode = mapindex
x.typ = typ.elem
x.expr = e
- return
+ return false
- case *Sum:
- // A sum type can be indexed if all of the sum's types
- // support indexing and have the same index and element
- // type. Special rules apply for maps in the sum type.
- var tkey, telem Type // key is for map types only
- nmaps := 0 // number of map types in sum type
- if typ.is(func(t Type) bool {
- var e Type
- switch t := under(t).(type) {
+ case *Interface:
+ if !isTypeParam(x.typ) {
+ break
+ }
+ // TODO(gri) report detailed failure cause for better error messages
+ 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.typeSet().underIs(func(u Type) bool {
+ 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) {
e = universeByte
+ mode = value
}
case *Array:
+ l = t.len
e = t.elem
+ if x.mode != variable {
+ mode = value
+ }
case *Pointer:
- if t := asArray(t.base); t != nil {
+ if t, _ := under(t.base).(*Array); t != nil {
+ l = t.len
e = t.elem
}
case *Slice:
e = t.elem
case *Map:
- // If there are multiple maps in the sum type,
- // they must have identical key types.
- // TODO(gri) We may be able to relax this rule
- // but it becomes complicated very quickly.
- if tkey != nil && !Identical(t.key, tkey) {
- return false
- }
- tkey = t.key
+ k = t.key
e = t.elem
- nmaps++
- case *TypeParam:
- check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
- case *instance:
- panic("unimplemented")
}
- if e == nil || telem != nil && !Identical(e, telem) {
+ if e == nil {
+ return false
+ }
+ if elem == nil {
+ // first type
+ 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
}
- telem = e
+ // track the minimal length for arrays, if any
+ if l >= 0 && l < length {
+ length = l
+ }
return true
}) {
- // If there are maps, the index expression must be assignable
- // to the map key type (as for simple map index expressions).
- if nmaps > 0 {
+ // For maps, the index expression must be assignable to the map key type.
+ if key != nil {
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ 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
-
- // If there are only maps, we are done.
- if nmaps == len(typ.types) {
- x.mode = mapindex
- x.typ = telem
- x.expr = e
- return
- }
-
- // Otherwise we have mix of maps and other types. For
- // now we require that the map key be an integer type.
- // TODO(gri) This is probably not good enough.
- valid = isInteger(tkey)
- // avoid 2nd indexing error if indexing failed above
- if !valid && key.mode == invalid {
- x.mode = invalid
- return
- }
- x.mode = value // map index expressions are not addressable
- } else {
- // no maps
- valid = true
- x.mode = variable
+ x.mode = mapindex
+ x.typ = elem
+ x.expr = e
+ return false
}
- x.typ = telem
+
+ // no maps
+ valid = true
+ x.mode = mode
+ x.typ = elem
}
}
if !valid {
check.errorf(x, invalidOp+"cannot index %s", x)
x.mode = invalid
- return
+ return false
}
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
- return
+ return false
}
// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
@@ -206,11 +213,20 @@ 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 := structuralString(x.typ).(type) {
+ case nil:
+ check.errorf(x, invalidOp+"cannot slice %s: %s has no structural type", x, x.typ)
+ x.mode = invalid
+ return
+
case *Basic:
- if isString(typ) {
+ if isString(u) {
if e.Full {
- check.error(x, invalidOp+"3-index slice of string")
+ at := e.Index[2]
+ if at == nil {
+ at = e // e.Index[2] should be present but be careful
+ }
+ check.error(at, invalidOp+"3-index slice of string")
x.mode = invalid
return
}
@@ -220,36 +236,31 @@ 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 isUntyped(x.typ) {
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, _ := under(u.base).(*Array); u != nil {
valid = true
- length = typ.len
- x.typ = &Slice{elem: typ.elem}
+ length = u.len
+ x.typ = &Slice{elem: u.elem}
}
case *Slice:
valid = true
// x.typ doesn't change
-
- case *Sum, *TypeParam:
- check.error(x, "generic slice expressions not yet implemented")
- x.mode = invalid
- return
}
if !valid {
@@ -298,9 +309,12 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
L:
for i, x := range ind[:len(ind)-1] {
if x > 0 {
- for _, y := range ind[i+1:] {
- if y >= 0 && x > y {
- check.errorf(e, "invalid slice indices: %d > %d", x, y)
+ for j, y := range ind[i+1:] {
+ if y >= 0 && y < x {
+ // The value y corresponds to the expression e.Index[i+1+j].
+ // Because y >= 0, it must have been set from the expression
+ // when checking indices and thus e.Index[i+1+j] is not nil.
+ check.errorf(e.Index[i+1+j], "invalid slice indices: %d < %d", y, x)
break L // only report one error, ok to continue
}
}
@@ -382,7 +396,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 f37d7f6477e96ab29bb8d0e6705cb65b0dd8cb37..b203985b8d129027d75a8b16c10c54e7bc0307b5 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -9,6 +9,7 @@ package types2
import (
"bytes"
"cmd/compile/internal/syntax"
+ "fmt"
)
const useConstraintTypeInference = true
@@ -27,8 +28,7 @@ const useConstraintTypeInference = true
// 3) Infer type arguments from untyped function arguments.
//
// Constraint type inference is used after each step to expand the set of type arguments.
-//
-func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, params *Tuple, args []*operand, report bool) (result []Type) {
+func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
if debug {
defer func() {
assert(result == nil || len(result) == len(tparams))
@@ -60,7 +60,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
// If we have type arguments, see how far we get with constraint type inference.
if len(targs) > 0 && useConstraintTypeInference {
var index int
- targs, index = check.inferB(tparams, targs, report)
+ targs, index = check.inferB(pos, tparams, targs)
if targs == nil || index < 0 {
return targs
}
@@ -83,18 +83,18 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
// Substitute type arguments for their respective type parameters in params,
// if any. Note that nil targs entries are ignored by check.subst.
- // TODO(gri) Can we avoid this (we're setting known type argumemts below,
+ // TODO(gri) Can we avoid this (we're setting known type arguments below,
// but that doesn't impact the isParameterized check for now).
if params.Len() > 0 {
smap := makeSubstMap(tparams, targs)
- params = check.subst(nopos, params, smap).(*Tuple)
+ params = check.subst(nopos, params, smap, nil).(*Tuple)
}
// --- 2 ---
// Unify parameter and argument types for generic parameters with typed arguments
// and collect the indices of generic parameters with untyped arguments.
// Terminology: generic parameter = function parameter with a type-parameterized type
- u := newUnifier(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
// Set the type arguments which we know already.
@@ -105,9 +105,6 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
}
errorf := func(kind string, tpar, targ Type, arg *operand) {
- if !report {
- return
- }
// provide a better error message if we can
targs, index := u.x.types()
if index == 0 {
@@ -122,12 +119,12 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
}
}
if allFailed {
- check.errorf(arg, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams))
+ check.errorf(arg, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeParamsString(tparams))
return
}
}
smap := makeSubstMap(tparams, targs)
- inferred := check.subst(arg.Pos(), tpar, smap)
+ inferred := check.subst(arg.Pos(), tpar, smap, nil)
if inferred != tpar {
check.errorf(arg, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
} else {
@@ -174,7 +171,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
// Note that even if we don't have any type arguments, constraint type inference
// may produce results for constraints that explicitly specify a type.
if useConstraintTypeInference {
- targs, index = check.inferB(tparams, targs, report)
+ targs, index = check.inferB(pos, tparams, targs)
if targs == nil || index < 0 {
return targs
}
@@ -212,7 +209,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
// Again, follow up with constraint type inference.
if useConstraintTypeInference {
- targs, index = check.inferB(tparams, targs, report)
+ targs, index = check.inferB(pos, tparams, targs)
if targs == nil || index < 0 {
return targs
}
@@ -221,24 +218,22 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeName, targs []Type, p
// At least one type argument couldn't be inferred.
assert(targs != nil && index >= 0 && targs[index] == nil)
tpar := tparams[index]
- if report {
- check.errorf(pos, "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs)
- }
+ check.errorf(pos, "cannot infer %s (%s)", tpar.obj.name, tpar.obj.pos)
return nil
}
-// typeNamesString produces a string containing all the
-// type names in list suitable for human consumption.
-func typeNamesString(list []*TypeName) string {
+// typeParamsString produces a string of the type parameter names
+// in list suitable for human consumption.
+func typeParamsString(list []*TypeParam) string {
// common cases
n := len(list)
switch n {
case 0:
return ""
case 1:
- return list[0].name
+ return list[0].obj.name
case 2:
- return list[0].name + " and " + list[1].name
+ return list[0].obj.name + " and " + list[1].obj.name
}
// general case (n > 2)
@@ -248,15 +243,15 @@ func typeNamesString(list []*TypeName) string {
if i > 0 {
b.WriteString(", ")
}
- b.WriteString(tname.name)
+ b.WriteString(tname.obj.name)
}
b.WriteString(", and ")
- b.WriteString(list[n-1].name)
+ b.WriteString(list[n-1].obj.name)
return b.String()
}
// IsParameterized reports whether typ contains any of the type parameters of tparams.
-func isParameterized(tparams []*TypeName, typ Type) bool {
+func isParameterized(tparams []*TypeParam, typ Type) bool {
w := tpWalker{
seen: make(map[Type]bool),
tparams: tparams,
@@ -266,7 +261,7 @@ func isParameterized(tparams []*TypeName, typ Type) bool {
type tpWalker struct {
seen map[Type]bool
- tparams []*TypeName
+ tparams []*TypeParam
}
func (w *tpWalker) isParameterized(typ Type) (res bool) {
@@ -307,9 +302,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}
}
- case *Sum:
- return w.isParameterizedList(t.types)
-
case *Signature:
// t.tparams may not be nil if we are looking at a signature
// of a generic function type (or an interface method) that is
@@ -321,24 +313,15 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return w.isParameterized(t.params) || w.isParameterized(t.results)
case *Interface:
- if t.allMethods != nil {
- // interface is complete - quick test
- for _, m := range t.allMethods {
- if w.isParameterized(m.typ) {
- return true
- }
+ tset := t.typeSet()
+ for _, m := range tset.methods {
+ if w.isParameterized(m.typ) {
+ return true
}
- return w.isParameterizedList(unpack(t.allTypes))
}
-
- return t.iterate(func(t *Interface) bool {
- for _, m := range t.methods {
- if w.isParameterized(m.typ) {
- return true
- }
- }
- return w.isParameterizedList(unpack(t.types))
- }, nil)
+ return tset.is(func(t *term) bool {
+ return t != nil && w.isParameterized(t.typ)
+ })
case *Map:
return w.isParameterized(t.key) || w.isParameterized(t.elem)
@@ -347,14 +330,11 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return w.isParameterized(t.elem)
case *Named:
- return w.isParameterizedList(t.targs)
+ return w.isParameterizedTypeList(t.targs.list())
case *TypeParam:
// t must be one of w.tparams
- return t.index < len(w.tparams) && w.tparams[t.index].typ == t
-
- case *instance:
- return w.isParameterizedList(t.targs)
+ return tparamIndex(w.tparams, t) >= 0
default:
unreachable()
@@ -363,7 +343,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return false
}
-func (w *tpWalker) isParameterizedList(list []Type) bool {
+func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
for _, t := range list {
if w.isParameterized(t) {
return true
@@ -380,12 +360,12 @@ func (w *tpWalker) isParameterizedList(list []Type) bool {
// first type argument in that list that couldn't be inferred (and thus is nil). If all
// type arguments were inferred successfully, index is < 0. The number of type arguments
// provided may be less than the number of type parameters, but there must be at least one.
-func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (types []Type, index int) {
+func (check *Checker) inferB(pos syntax.Pos, 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(check, false)
+ u := newUnifier(false)
u.x.init(tparams)
u.y = u.x // type parameters between LHS and RHS of unification are identical
@@ -396,24 +376,28 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
}
}
- // 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.typ.(*TypeParam)
- sbound := check.structuralType(typ.bound)
+ sbound := structuralType(tpar)
if sbound != nil {
- if !u.unify(typ, sbound) {
- if report {
- check.errorf(tpar, "%s does not match %s", tpar, 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(pos, "%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-
- // nil entries may still contain references to other type parameters. For instance,
- // for [A any, B interface{type []C}, C interface{type *A}], if A == int
+ // 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
// remaining type parameters by substituting the type parameters in this type list
// until nothing changes anymore.
@@ -424,6 +408,34 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
}
}
+ // The data structure of each (provided or inferred) type represents a graph, where
+ // each node corresponds to a type and each (directed) vertice points to a component
+ // type. The substitution process described above repeatedly replaces type parameter
+ // nodes in these graphs with the graphs of the types the type parameters stand for,
+ // which creates a new (possibly bigger) graph for each type.
+ // The substitution process will not stop if the replacement graph for a type parameter
+ // also contains that type parameter.
+ // For instance, for [A interface{ *A }], without any type argument provided for A,
+ // unification produces the type list [*A]. Substituting A in *A with the value for
+ // A will lead to infinite expansion by producing [**A], [****A], [********A], etc.,
+ // because the graph A -> *A has a cycle through A.
+ // Generally, cycles may occur across multiple type parameters and inferred types
+ // (for instance, consider [P interface{ *Q }, Q interface{ func(P) }]).
+ // We eliminate cycles by walking the graphs for all type parameters. If a cycle
+ // through a type parameter is detected, cycleFinder nils out the respectice type
+ // which kills the cycle; this also means that the respective type could not be
+ // inferred.
+ //
+ // TODO(gri) If useful, we could report the respective cycle as an error. We don't
+ // do this now because type inference will fail anyway, and furthermore,
+ // constraints with cycles of this kind cannot currently be satisfied by
+ // any user-suplied type. But should that change, reporting an error
+ // would be wrong.
+ w := cycleFinder{tparams, types, make(map[Type]bool)}
+ for _, t := range tparams {
+ w.typ(t) // t != nil
+ }
+
// dirty tracks the indices of all types that may still contain type parameters.
// We know that nil type entries and entries corresponding to provided (non-nil)
// type arguments are clean, so exclude them from the start.
@@ -442,7 +454,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
n := 0
for _, index := range dirty {
t0 := types[index]
- if t1 := check.subst(nopos, t0, smap); t1 != t0 {
+ if t1 := check.subst(nopos, t0, smap, nil); t1 != t0 {
types[index] = t1
dirty[n] = index
n++
@@ -473,15 +485,97 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
return
}
-// structuralType returns the structural type of a constraint, if any.
-func (check *Checker) structuralType(constraint Type) Type {
- if iface, _ := under(constraint).(*Interface); iface != nil {
- check.completeInterface(nopos, iface)
- types := unpack(iface.allTypes)
- if len(types) == 1 {
- return types[0]
+type cycleFinder struct {
+ tparams []*TypeParam
+ types []Type
+ seen map[Type]bool
+}
+
+func (w *cycleFinder) typ(typ Type) {
+ if w.seen[typ] {
+ // We have seen typ before. If it is one of the type parameters
+ // in tparams, iterative substitution will lead to infinite expansion.
+ // Nil out the corresponding type which effectively kills the cycle.
+ if tpar, _ := typ.(*TypeParam); tpar != nil {
+ if i := tparamIndex(w.tparams, tpar); i >= 0 {
+ // cycle through tpar
+ w.types[i] = nil
+ }
}
- return nil
+ // If we don't have one of our type parameters, the cycle is due
+ // to an ordinary recursive type and we can just stop walking it.
+ return
+ }
+ w.seen[typ] = true
+ defer delete(w.seen, typ)
+
+ switch t := typ.(type) {
+ case *Basic:
+ // nothing to do
+
+ case *Array:
+ w.typ(t.elem)
+
+ case *Slice:
+ w.typ(t.elem)
+
+ case *Struct:
+ w.varList(t.fields)
+
+ case *Pointer:
+ w.typ(t.base)
+
+ // case *Tuple:
+ // This case should not occur because tuples only appear
+ // in signatures where they are handled explicitly.
+
+ case *Signature:
+ // There are no "method types" so we should never see a recv.
+ assert(t.recv == nil)
+ if t.params != nil {
+ w.varList(t.params.vars)
+ }
+ if t.results != nil {
+ w.varList(t.results.vars)
+ }
+
+ case *Union:
+ for _, t := range t.terms {
+ w.typ(t.typ)
+ }
+
+ case *Interface:
+ for _, m := range t.methods {
+ w.typ(m.typ)
+ }
+ for _, t := range t.embeddeds {
+ w.typ(t)
+ }
+
+ case *Map:
+ w.typ(t.key)
+ w.typ(t.elem)
+
+ case *Chan:
+ w.typ(t.elem)
+
+ case *Named:
+ for _, tpar := range t.TypeArgs().list() {
+ w.typ(tpar)
+ }
+
+ case *TypeParam:
+ if i := tparamIndex(w.tparams, t); i >= 0 && w.types[i] != nil {
+ w.typ(w.types[i])
+ }
+
+ default:
+ panic(fmt.Sprintf("unexpected %T", typ))
+ }
+}
+
+func (w *cycleFinder) varList(list []*Var) {
+ for _, v := range list {
+ w.typ(v.typ)
}
- return constraint
}
diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go
index 40816276665117ba4bae14c040d515544df9654b..cf6110baa92c0f918b201af3bfe05f9e7f4d2586 100644
--- a/src/cmd/compile/internal/types2/initorder.go
+++ b/src/cmd/compile/internal/types2/initorder.go
@@ -7,6 +7,7 @@ package types2
import (
"container/heap"
"fmt"
+ "sort"
)
// initOrder computes the Info.InitOrder for package variables.
@@ -190,6 +191,12 @@ type graphNode struct {
ndeps int // number of outstanding dependencies before this object can be initialized
}
+// cost returns the cost of removing this node, which involves copying each
+// predecessor to each successor (and vice-versa).
+func (n *graphNode) cost() int {
+ return len(n.pred) * len(n.succ)
+}
+
type nodeSet map[*graphNode]bool
func (s *nodeSet) add(p *graphNode) {
@@ -227,35 +234,48 @@ func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
}
}
+ var G, funcG []*graphNode // separate non-functions and functions
+ for _, n := range M {
+ if _, ok := n.obj.(*Func); ok {
+ funcG = append(funcG, n)
+ } else {
+ G = append(G, n)
+ }
+ }
+
// remove function nodes and collect remaining graph nodes in G
// (Mutually recursive functions may introduce cycles among themselves
// which are permitted. Yet such cycles may incorrectly inflate the dependency
// count for variables which in turn may not get scheduled for initialization
// in correct order.)
- var G []*graphNode
- for obj, n := range M {
- if _, ok := obj.(*Func); ok {
- // connect each predecessor p of n with each successor s
- // and drop the function node (don't collect it in G)
- for p := range n.pred {
- // ignore self-cycles
- if p != n {
- // Each successor s of n becomes a successor of p, and
- // each predecessor p of n becomes a predecessor of s.
- for s := range n.succ {
- // ignore self-cycles
- if s != n {
- p.succ.add(s)
- s.pred.add(p)
- delete(s.pred, n) // remove edge to n
- }
+ //
+ // Note that because we recursively copy predecessors and successors
+ // throughout the function graph, the cost of removing a function at
+ // position X is proportional to cost * (len(funcG)-X). Therefore, we should
+ // remove high-cost functions last.
+ sort.Slice(funcG, func(i, j int) bool {
+ return funcG[i].cost() < funcG[j].cost()
+ })
+ for _, n := range funcG {
+ // connect each predecessor p of n with each successor s
+ // and drop the function node (don't collect it in G)
+ for p := range n.pred {
+ // ignore self-cycles
+ if p != n {
+ // Each successor s of n becomes a successor of p, and
+ // each predecessor p of n becomes a predecessor of s.
+ for s := range n.succ {
+ // ignore self-cycles
+ if s != n {
+ p.succ.add(s)
+ s.pred.add(p)
}
- delete(p.succ, n) // remove edge to n
}
+ delete(p.succ, n) // remove edge to n
}
- } else {
- // collect non-function nodes
- G = append(G, n)
+ }
+ for s := range n.succ {
+ delete(s.pred, n) // remove edge to n
}
}
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 0df52e851c9de4b6f02726325ad1698fb73c060e..cda6c7baf4e5c543f674903a05f01ca8a24d9a81 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -2,62 +2,265 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// This file implements instantiation of generic types
+// through substitution of type parameters by type arguments.
+
package types2
import (
"cmd/compile/internal/syntax"
+ "errors"
"fmt"
)
-// Instantiate instantiates the type typ with the given type arguments.
-// typ must be a *Named or a *Signature type, it must be generic, and
-// its number of type parameters must match the number of provided type
-// arguments. The result is a new, instantiated (not generic) type of
-// the same kind (either a *Named or a *Signature). The type arguments
-// are not checked against the constraints of the type parameters.
-// Any methods attached to a *Named are simply copied; they are not
-// instantiated.
-func Instantiate(pos syntax.Pos, typ Type, targs []Type) (res Type) {
- // TODO(gri) This code is basically identical to the prolog
- // in Checker.instantiate. Factor.
- var tparams []*TypeName
- switch t := typ.(type) {
+// Instantiate instantiates the type orig with the given type arguments targs.
+// orig must be a *Named or a *Signature type. If there is no error, the
+// resulting Type is a new, instantiated (not parameterized) type of the same
+// kind (either a *Named or a *Signature). Methods attached to a *Named type
+// are also instantiated, and associated with a new *Func that has the same
+// position as the original method, but nil function scope.
+//
+// If ctxt is non-nil, it may be used to de-duplicate the instance against
+// previous instances with the same identity. As a special case, generic
+// *Signature origin types are only considered identical if they are pointer
+// equivalent, so that instantiating distinct (but possibly identical)
+// signatures will yield different instances.
+//
+// If validate is set, Instantiate verifies that the number of type arguments
+// and parameters match, and that the type arguments satisfy their
+// corresponding type constraints. If verification fails, the resulting error
+// may wrap an *ArgumentError indicating which type argument did not satisfy
+// its corresponding type parameter constraint, and why.
+//
+// If validate is not set, Instantiate does not verify the type argument count
+// or whether the type arguments satisfy their constraints. Instantiate is
+// guaranteed to not return an error, but may panic. Specifically, for
+// *Signature types, Instantiate will panic immediately if the type argument
+// count is incorrect; for *Named types, a panic may occur later inside the
+// *Named API.
+func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, error) {
+ if validate {
+ var tparams []*TypeParam
+ switch t := orig.(type) {
+ case *Named:
+ tparams = t.TypeParams().list()
+ case *Signature:
+ tparams = t.TypeParams().list()
+ }
+ if len(targs) != len(tparams) {
+ return nil, fmt.Errorf("got %d type arguments but %s has %d type parameters", len(targs), orig, len(tparams))
+ }
+ if i, err := (*Checker)(nil).verify(nopos, tparams, targs); err != nil {
+ return nil, &ArgumentError{i, err}
+ }
+ }
+
+ inst := (*Checker)(nil).instance(nopos, orig, targs, ctxt)
+ return inst, nil
+}
+
+// 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.
+func (check *Checker) instance(pos syntax.Pos, orig Type, targs []Type, ctxt *Context) (res Type) {
+ var h string
+ if ctxt != nil {
+ h = ctxt.instanceHash(orig, targs)
+ // typ may already have been instantiated with identical type arguments. In
+ // that case, re-use the existing instance.
+ if inst := ctxt.lookup(h, orig, targs); inst != nil {
+ return inst
+ }
+ }
+
+ switch orig := orig.(type) {
case *Named:
- tparams = t.tparams
- case *Signature:
- tparams = t.tparams
- defer func() {
- // If we had an unexpected failure somewhere don't panic below when
- // asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
- // is returned.
- if _, ok := res.(*Signature); !ok {
- return
- }
- // If the signature doesn't use its type parameters, subst
- // will not make a copy. In that case, make a copy now (so
- // we can set tparams to nil w/o causing side-effects).
- if t == res {
- copy := *t
- res = ©
- }
- // After instantiating a generic signature, it is not generic
- // anymore; we need to set tparams to nil.
- res.(*Signature).tparams = nil
- }()
+ tname := NewTypeName(pos, orig.obj.pkg, orig.obj.name, nil)
+ named := check.newNamed(tname, orig, 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)
+ }
+ res = named
+ case *Signature:
+ tparams := orig.TypeParams()
+ if !check.validateTArgLen(pos, tparams.Len(), len(targs)) {
+ return Typ[Invalid]
+ }
+ if tparams.Len() == 0 {
+ return orig // nothing to do (minor optimization)
+ }
+ sig := check.subst(pos, orig, makeSubstMap(tparams.list(), targs), ctxt).(*Signature)
+ // If the signature doesn't use its type parameters, subst
+ // will not make a copy. In that case, make a copy now (so
+ // we can set tparams to nil w/o causing side-effects).
+ if sig == orig {
+ copy := *sig
+ sig = ©
+ }
+ // After instantiating a generic signature, it is not generic
+ // anymore; we need to set tparams to nil.
+ sig.tparams = nil
+ res = sig
default:
- panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
+ // only types and functions can be generic
+ panic(fmt.Sprintf("%v: cannot instantiate %v", pos, orig))
}
- // the number of supplied types must match the number of type parameters
- if len(targs) != len(tparams) {
- panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams)))
+ if ctxt != nil {
+ // It's possible that we've lost a race to add named to the context.
+ // In this case, use whichever instance is recorded in the context.
+ res = ctxt.update(h, orig, targs, res)
}
- if len(tparams) == 0 {
- return typ // nothing to do (minor optimization)
+ return res
+}
+
+// validateTArgLen verifies that the length of targs and tparams matches,
+// reporting an error if not. If validation fails and check is nil,
+// validateTArgLen panics.
+func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool {
+ if ntargs != ntparams {
+ // TODO(gri) provide better error message
+ if check != nil {
+ check.errorf(pos, "got %d arguments but %d type parameters", ntargs, ntparams)
+ return false
+ }
+ panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, ntargs, ntparams))
+ }
+ return true
+}
+
+func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type) (int, error) {
+ // TODO(rfindley): it would be great if users could pass in a qualifier here,
+ // rather than falling back to verbose qualification. Maybe this can be part
+ // of the shared context.
+ var qf Qualifier
+ if check != nil {
+ qf = check.qualifier
}
smap := makeSubstMap(tparams, targs)
- return (*Checker)(nil).subst(pos, typ, smap)
+ for i, tpar := range tparams {
+ // The type parameter bound is parameterized with the same type parameters
+ // as the instantiated type; before we can use it for bounds checking we
+ // need to instantiate it with the type arguments with which we instantiated
+ // the parameterized type.
+ bound := check.subst(pos, tpar.bound, smap, nil)
+ if err := check.implements(targs[i], bound, qf); err != nil {
+ return i, err
+ }
+ }
+ return -1, nil
+}
+
+// implements checks if V implements T and reports an error if it doesn't.
+// If a qualifier is provided, it is used in error formatting.
+func (check *Checker) implements(V, T Type, qf Qualifier) error {
+ Vu := under(V)
+ Tu := under(T)
+ if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
+ return nil
+ }
+
+ errorf := func(format string, args ...interface{}) error {
+ return errors.New(sprintf(qf, false, format, args...))
+ }
+
+ Ti, _ := Tu.(*Interface)
+ if Ti == nil {
+ return errorf("%s is not an interface", T)
+ }
+
+ // Every type satisfies the empty interface.
+ if Ti.Empty() {
+ return nil
+ }
+ // T is not the empty interface (i.e., the type set of T is restricted)
+
+ // An interface V with an empty type set satisfies any interface.
+ // (The empty set is a subset of any set.)
+ Vi, _ := Vu.(*Interface)
+ if Vi != nil && Vi.typeSet().IsEmpty() {
+ return nil
+ }
+ // type set of V is not empty
+
+ // No type with non-empty type set satisfies the empty type set.
+ if Ti.typeSet().IsEmpty() {
+ return errorf("cannot implement %s (empty type set)", T)
+ }
+
+ // If T is comparable, V must be comparable.
+ // TODO(gri) the error messages could be better, here
+ if Ti.IsComparable() && !Comparable(V) {
+ if Vi != nil && Vi.Empty() {
+ return errorf("empty interface %s does not implement %s", V, T)
+ }
+ return errorf("%s does not implement comparable", V)
+ }
+
+ // V must implement T (methods)
+ // - check only if we have methods
+ if Ti.NumMethods() > 0 {
+ if m, wrong := check.missingMethod(V, Ti, true); m != nil {
+ // TODO(gri) needs to print updated name to avoid major confusion in error message!
+ // (print warning for now)
+ // Old warning:
+ // check.softErrorf(pos, "%s does not implement %s (warning: name not updated) = %s (missing method %s)", V, T, Ti, m)
+ if wrong != nil {
+ // TODO(gri) This can still report uninstantiated types which makes the error message
+ // more difficult to read then necessary.
+ return errorf("%s does not implement %s: wrong method signature\n\tgot %s\n\twant %s",
+ V, T, wrong, m,
+ )
+ }
+ return errorf("%s does not implement %s (missing method %s)", V, T, m.name)
+ }
+ }
+
+ // V must also be in the set of types of T, if any.
+ // Constraints with empty type sets were already excluded above.
+ if !Ti.typeSet().hasTerms() {
+ return nil // nothing to do
+ }
+
+ // If V is itself an interface, each of its possible types must be in the set
+ // of T types (i.e., the V type set must be a subset of the T type set).
+ // Interfaces V with empty type sets were already excluded above.
+ if Vi != nil {
+ if !Vi.typeSet().subsetOf(Ti.typeSet()) {
+ // TODO(gri) report which type is missing
+ return errorf("%s does not implement %s", V, T)
+ }
+ return nil
+ }
+
+ // Otherwise, V's type must be included in the iface type set.
+ var alt Type
+ if Ti.typeSet().is(func(t *term) bool {
+ if !t.includes(V) {
+ // If V ∉ t.typ but V ∈ ~t.typ then remember this type
+ // so we can suggest it as an alternative in the error
+ // message.
+ if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) {
+ tt := *t
+ tt.tilde = true
+ if tt.includes(V) {
+ alt = t.typ
+ }
+ }
+ return true
+ }
+ return false
+ }) {
+ if alt != nil {
+ return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T)
+ } else {
+ return errorf("%s does not implement %s", V, T)
+ }
+ }
+
+ return nil
}
diff --git a/src/cmd/compile/internal/types2/instantiate_test.go b/src/cmd/compile/internal/types2/instantiate_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..591b467a2e382a8323f32367e915c73153d56ed4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/instantiate_test.go
@@ -0,0 +1,247 @@
+// 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 (
+ . "cmd/compile/internal/types2"
+ "strings"
+ "testing"
+)
+
+func TestInstantiateEquality(t *testing.T) {
+ emptySignature := NewSignatureType(nil, nil, nil, nil, nil, false)
+ tests := []struct {
+ src string
+ name1 string
+ targs1 []Type
+ name2 string
+ targs2 []Type
+ wantEqual bool
+ }{
+ {
+ "package basictype; type T[P any] int",
+ "T", []Type{Typ[Int]},
+ "T", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package differenttypeargs; type T[P any] int",
+ "T", []Type{Typ[Int]},
+ "T", []Type{Typ[String]},
+ false,
+ },
+ {
+ "package typeslice; type T[P any] int",
+ "T", []Type{NewSlice(Typ[Int])},
+ "T", []Type{NewSlice(Typ[Int])},
+ true,
+ },
+ {
+ // interface{interface{...}} is equivalent to interface{...}
+ "package equivalentinterfaces; type T[P any] int",
+ "T", []Type{
+ NewInterfaceType([]*Func{NewFunc(nopos, nil, "M", emptySignature)}, nil),
+ },
+ "T", []Type{
+ NewInterfaceType(
+ nil,
+ []Type{
+ NewInterfaceType([]*Func{NewFunc(nopos, nil, "M", emptySignature)}, nil),
+ },
+ ),
+ },
+ true,
+ },
+ {
+ // int|string is equivalent to string|int
+ "package equivalenttypesets; type T[P any] int",
+ "T", []Type{
+ NewInterfaceType(nil, []Type{
+ NewUnion([]*Term{NewTerm(false, Typ[Int]), NewTerm(false, Typ[String])}),
+ }),
+ },
+ "T", []Type{
+ NewInterfaceType(nil, []Type{
+ NewUnion([]*Term{NewTerm(false, Typ[String]), NewTerm(false, Typ[Int])}),
+ }),
+ },
+ true,
+ },
+ {
+ "package basicfunc; func F[P any]() {}",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package funcslice; func F[P any]() {}",
+ "F", []Type{NewSlice(Typ[Int])},
+ "F", []Type{NewSlice(Typ[Int])},
+ true,
+ },
+ {
+ "package funcwithparams; func F[P any](x string) float64 { return 0 }",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[Int]},
+ true,
+ },
+ {
+ "package differentfuncargs; func F[P any](x string) float64 { return 0 }",
+ "F", []Type{Typ[Int]},
+ "F", []Type{Typ[String]},
+ false,
+ },
+ {
+ "package funcequality; func F1[P any](x int) {}; func F2[Q any](x int) {}",
+ "F1", []Type{Typ[Int]},
+ "F2", []Type{Typ[Int]},
+ false,
+ },
+ {
+ "package funcsymmetry; func F1[P any](x P) {}; func F2[Q any](x Q) {}",
+ "F1", []Type{Typ[Int]},
+ "F2", []Type{Typ[Int]},
+ false,
+ },
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgFor(".", test.src, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Run(pkg.Name(), func(t *testing.T) {
+ ctxt := NewContext()
+
+ T1 := pkg.Scope().Lookup(test.name1).Type()
+ res1, err := Instantiate(ctxt, T1, test.targs1, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ T2 := pkg.Scope().Lookup(test.name2).Type()
+ res2, err := Instantiate(ctxt, T2, test.targs2, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if gotEqual := res1 == res2; gotEqual != test.wantEqual {
+ t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual)
+ }
+ })
+ }
+}
+
+func TestInstantiateNonEquality(t *testing.T) {
+ const src = "package p; type T[P any] int"
+ pkg1, err := pkgFor(".", src, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg2, err := pkgFor(".", src, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // We consider T1 and T2 to be distinct types, so their instances should not
+ // be deduplicated by the context.
+ T1 := pkg1.Scope().Lookup("T").Type().(*Named)
+ T2 := pkg2.Scope().Lookup("T").Type().(*Named)
+ ctxt := NewContext()
+ res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res1 == res2 {
+ t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2)
+ }
+ if Identical(res1, res2) {
+ t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
+ }
+}
+
+func TestMethodInstantiation(t *testing.T) {
+ const prefix = `package p
+
+type T[P any] struct{}
+
+var X T[int]
+
+`
+ tests := []struct {
+ decl string
+ want string
+ }{
+ {"func (r T[P]) m() P", "func (T[int]).m() int"},
+ {"func (r T[P]) m(P)", "func (T[int]).m(int)"},
+ {"func (r *T[P]) m(P)", "func (*T[int]).m(int)"},
+ {"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"},
+ {"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"},
+ {"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"},
+ {"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"},
+ }
+
+ for _, test := range tests {
+ src := prefix + test.decl
+ pkg, err := pkgFor(".", src, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ typ := NewPointer(pkg.Scope().Lookup("X").Type())
+ obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+ m, _ := obj.(*Func)
+ if m == nil {
+ t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ }
+ if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
+ t.Errorf("instantiated %q, want %q", got, test.want)
+ }
+ }
+}
+
+func TestImmutableSignatures(t *testing.T) {
+ const src = `package p
+
+type T[P any] struct{}
+
+func (T[P]) m() {}
+
+var _ T[int]
+`
+ pkg, err := pkgFor(".", src, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ typ := pkg.Scope().Lookup("T").Type().(*Named)
+ obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
+ if obj == nil {
+ t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ }
+
+ // Verify that the original method is not mutated by instantiating T (this
+ // bug manifested when subst did not return a new signature).
+ want := "func (T[P]).m()"
+ if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
+ t.Errorf("instantiated %q, want %q", got, want)
+ }
+}
+
+// Copied from errors.go.
+func stripAnnotations(s string) string {
+ var b strings.Builder
+ for _, r := range s {
+ // strip #'s and subscript digits
+ if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
+ b.WriteRune(r)
+ }
+ }
+ if b.Len() < len(s) {
+ return b.String()
+ }
+ return s
+}
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..b048fdd9e25ba70c6687f695f15767b7e25b17fe
--- /dev/null
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -0,0 +1,184 @@
+// 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"
+
+// ----------------------------------------------------------------------------
+// API
+
+// An Interface represents an interface type.
+type Interface struct {
+ check *Checker // for error reporting; nil once type set is computed
+ obj *TypeName // corresponding declared object; or nil (for better error messages)
+ methods []*Func // ordered list of explicitly declared methods
+ embeddeds []Type // ordered list of explicitly embedded elements
+ embedPos *[]syntax.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space
+ implicit bool // interface is wrapper for type set literal (non-interface T, ~T, or A|B)
+ complete bool // indicates that all fields (except for tset) are set up
+
+ tset *_TypeSet // type set described by this interface, computed lazily
+}
+
+// typeSet returns the type set for interface t.
+func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) }
+
+// emptyInterface represents the empty interface
+var emptyInterface = Interface{complete: true, tset: &topTypeSet}
+
+// NewInterfaceType returns a new interface for the given methods and embedded types.
+// NewInterfaceType takes ownership of the provided methods and may modify their types
+// by setting missing receivers.
+func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
+ if len(methods) == 0 && len(embeddeds) == 0 {
+ return &emptyInterface
+ }
+
+ // set method receivers if necessary
+ typ := new(Interface)
+ for _, m := range methods {
+ if sig := m.typ.(*Signature); sig.recv == nil {
+ sig.recv = NewVar(m.pos, m.pkg, "", typ)
+ }
+ }
+
+ // sort for API stability
+ sortMethods(methods)
+
+ typ.methods = methods
+ typ.embeddeds = embeddeds
+ typ.complete = true
+
+ 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) }
+
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
+
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
+
+// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
+
+// NumMethods returns the total number of methods of interface t.
+func (t *Interface) NumMethods() int { return t.typeSet().NumMethods() }
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
+
+// Empty reports whether t is the empty interface.
+func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
+
+// IsComparable reports whether each type in interface t's type set is comparable.
+func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
+
+// IsMethodSet reports whether the interface t is fully described by its method set.
+func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }
+
+// IsImplicit reports whether the interface t is a wrapper for a type set literal.
+func (t *Interface) IsImplicit() bool { return t.implicit }
+
+func (t *Interface) Underlying() Type { return t }
+func (t *Interface) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) {
+ addEmbedded := func(pos syntax.Pos, typ Type) {
+ ityp.embeddeds = append(ityp.embeddeds, typ)
+ if ityp.embedPos == nil {
+ ityp.embedPos = new([]syntax.Pos)
+ }
+ *ityp.embedPos = append(*ityp.embedPos, pos)
+ }
+
+ for _, f := range iface.MethodList {
+ if f.Name == nil {
+ addEmbedded(posFor(f.Type), parseUnion(check, f.Type))
+ continue
+ }
+ // f.Name != nil
+
+ // We have a method with name f.Name.
+ name := f.Name.Value
+ if name == "_" {
+ if check.conf.CompilerErrorMessages {
+ check.error(f.Name, "methods must have a unique non-blank name")
+ } else {
+ check.error(f.Name, "invalid method name _")
+ }
+ continue // ignore
+ }
+
+ typ := check.typ(f.Type)
+ sig, _ := typ.(*Signature)
+ if sig == nil {
+ if typ != Typ[Invalid] {
+ check.errorf(f.Type, invalidAST+"%s is not a method signature", typ)
+ }
+ continue // ignore
+ }
+
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (This extra check is needed here because interface method signatures don't have
+ // a receiver specification.)
+ if sig.tparams != nil && !acceptMethodTypeParams {
+ check.error(f.Type, "methods cannot have type parameters")
+ }
+
+ // use named receiver type if available (for better error messages)
+ var recvTyp Type = ityp
+ if def != nil {
+ recvTyp = def
+ }
+ sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
+
+ m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
+ check.recordDef(f.Name, m)
+ ityp.methods = append(ityp.methods, m)
+ }
+
+ // All methods and embedded elements for this interface are collected;
+ // i.e., this interface may be used in a type set computation.
+ ityp.complete = true
+
+ if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
+ // empty interface
+ ityp.tset = &topTypeSet
+ return
+ }
+
+ // sort for API stability
+ // (don't sort embeddeds: they must correspond to *embedPos entries)
+ sortMethods(ityp.methods)
+
+ // Compute type set with a non-nil *Checker as soon as possible
+ // to report any errors. Subsequent uses of type sets will use
+ // this computed type set and won't need to pass in a *Checker.
+ //
+ // Pin the checker to the interface type in the interim, in case the type set
+ // must be used before delayed funcs are processed (see issue #48234).
+ // TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet
+ ityp.check = check
+ check.later(func() {
+ computeInterfaceTypeSet(check, iface.Pos(), ityp)
+ ityp.check = nil
+ }).describef(iface, "compute type set for %s", ityp)
+}
diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go
index e716a48038510a2320c02524c4375d0bdaa9ef6f..9890b79323ac81dfe0347532b831b1cf2f635513 100644
--- a/src/cmd/compile/internal/types2/issues_test.go
+++ b/src/cmd/compile/internal/types2/issues_test.go
@@ -321,7 +321,7 @@ func TestIssue25627(t *testing.T) {
}
}
- syntax.Walk(f, func(n syntax.Node) bool {
+ syntax.Crawl(f, func(n syntax.Node) bool {
if decl, _ := n.(*syntax.TypeDecl); decl != nil {
if tv, ok := info.Types[decl.Type]; ok && decl.Name.Value == "T" {
want := strings.Count(src, ";") + 1
@@ -402,8 +402,9 @@ func TestIssue28282(t *testing.T) {
// create type interface { error }
et := Universe.Lookup("error").Type()
it := NewInterfaceType(nil, []Type{et})
- it.Complete()
// verify that after completing the interface, the embedded method remains unchanged
+ // (interfaces are "completed" lazily now, so the completion happens implicitly when
+ // accessing Method(0))
want := et.Underlying().(*Interface).Method(0)
got := it.Method(0)
if got != want {
diff --git a/src/cmd/compile/internal/types2/labels.go b/src/cmd/compile/internal/types2/labels.go
index d3206988b54b69d41fd525e6ba9beec487b3e8ba..6f02e2fc969b084f4c86496ff8e9cc0999be2f6c 100644
--- a/src/cmd/compile/internal/types2/labels.go
+++ b/src/cmd/compile/internal/types2/labels.go
@@ -32,7 +32,8 @@ func (check *Checker) labels(body *syntax.BlockStmt) {
}
// spec: "It is illegal to define a label that is never used."
- for _, obj := range all.elems {
+ for name, obj := range all.elems {
+ obj = resolve(name, obj)
if lbl := obj.(*Label); !lbl.used {
check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
}
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go
index 78299502e9c00d9ab176733912a31c60d2602a50..ee764c7d1407ef2fdc1a9c2da41fb00ef461e82e 100644
--- a/src/cmd/compile/internal/types2/lookup.go
+++ b/src/cmd/compile/internal/types2/lookup.go
@@ -6,6 +6,16 @@
package types2
+import (
+ "fmt"
+ "strings"
+)
+
+// Internal use of LookupFieldOrMethod: If the obj result is a method
+// associated with a concrete (non-interface) type, the method's signature
+// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
+// the method's type.
+
// LookupFieldOrMethod looks up a field or method with given package and name
// in T and returns the corresponding *Var or *Func, an index sequence, and a
// bool indicating if there were any pointer indirections on the path to the
@@ -33,19 +43,6 @@ package types2
// the method's formal receiver base type, nor was the receiver addressable.
//
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
- return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name)
-}
-
-// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method
-// associated with a concrete (non-interface) type, the method's signature
-// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
-// the method's type.
-// TODO(gri) Now that we provide the *Checker, we can probably remove this
-// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate.
-
-// lookupFieldOrMethod is like the external version but completes interfaces
-// as necessary.
-func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// Methods cannot be associated to a named pointer type
// (spec: "The type denoted by T is called the receiver base type;
// it must not be a pointer or interface type and it must be declared
@@ -53,9 +50,9 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
// Thus, if we have a named pointer type, proceed with the underlying
// pointer type but discard the result if it is a method since we would
// not have found it for T (see also issue 8590).
- if t := asNamed(T); t != nil {
- if p, _ := t.underlying.(*Pointer); p != nil {
- obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
+ if t, _ := T.(*Named); t != nil {
+ if p, _ := t.Underlying().(*Pointer); p != nil {
+ obj, index, indirect = lookupFieldOrMethod(p, false, false, pkg, name)
if _, ok := obj.(*Func); ok {
return nil, nil, false
}
@@ -63,7 +60,7 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
}
}
- return check.rawLookupFieldOrMethod(T, addressable, pkg, name)
+ return lookupFieldOrMethod(T, addressable, false, pkg, name)
}
// TODO(gri) The named type consolidation and seen maps below must be
@@ -71,10 +68,13 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
// types always have only one representation (even when imported
// indirectly via different packages.)
-// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod.
-func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+// lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
+// If checkFold is true, the lookup for methods will include looking for any method
+// which case-folds to the same as 'name' (used for giving helpful error messages).
+//
+// The resulting object may not be fully type-checked.
+func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
// WARNING: The code in this function is extremely subtle - do not modify casually!
- // This function and NewMethodSet should be kept in sync.
if name == "_" {
return // blank fields/methods are never found
@@ -83,9 +83,10 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
typ, isPtr := deref(T)
// *typ where typ is an interface has no methods.
- // Be cautious: typ may be nil (issue 39634, crash #3).
- if typ == nil || isPtr && IsInterface(typ) {
- return
+ if isPtr {
+ if _, ok := under(typ).(*Interface); ok {
+ return
+ }
}
// Start with typ as single entry at shallowest depth.
@@ -111,7 +112,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
// If we have a named type, we may have associated methods.
// Look for those first.
- if named := asNamed(typ); named != nil {
+ if named, _ := typ.(*Named); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
@@ -126,7 +127,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
seen[named] = true
// look for a matching attached method
- if i, m := lookupMethod(named.methods, pkg, name); m != nil {
+ named.resolve(nil)
+ if i, m := lookupMethodFold(named.methods, pkg, name, checkFold); m != nil {
// potential match
// caution: method may not have a proper signature yet
index = concat(e.index, i)
@@ -138,12 +140,8 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
continue // we can't have a matching field or interface method
}
- // continue with underlying type, but only if it's not a type parameter
- // TODO(gri) is this what we want to do for type parameters? (spec question)
+ // continue with underlying type
typ = named.under()
- if asTypeParam(typ) != nil {
- continue
- }
}
tpar = nil
@@ -181,9 +179,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
case *Interface:
// look for a matching method
- // TODO(gri) t.allMethods is sorted - use binary search
- check.completeInterface(nopos, t)
- if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
+ if i, m := lookupMethodFold(t.typeSet().methods, pkg, name, checkFold); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
@@ -194,7 +190,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
}
case *TypeParam:
- if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
+ if i, m := lookupMethodFold(t.iface().typeSet().methods, pkg, name, checkFold); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
if obj != nil || e.multiples {
@@ -221,7 +217,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
// is shorthand for (&x).m()".
if f, _ := obj.(*Func); f != nil {
// determine if method has a pointer receiver
- hasPtrRecv := tpar == nil && ptrRecv(f)
+ hasPtrRecv := tpar == nil && f.hasPtrRecv()
if hasPtrRecv && !indirect && !addressable {
return nil, nil, true // pointer/addressable receiver required
}
@@ -229,7 +225,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
return
}
- current = check.consolidateMultiples(next)
+ current = consolidateMultiples(next)
}
return nil, nil, false // not found
@@ -246,7 +242,7 @@ type embeddedType struct {
// consolidateMultiples collects multiple list entries with the same type
// into a single entry marked as containing multiples. The result is the
// consolidated list.
-func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
+func consolidateMultiples(list []embeddedType) []embeddedType {
if len(list) <= 1 {
return list // at most one entry - nothing to do
}
@@ -254,7 +250,7 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
n := 0 // number of entries w/ unique type
prev := make(map[Type]int) // index at which type was previously seen
for _, e := range list {
- if i, found := check.lookupType(prev, e.typ); found {
+ if i, found := lookupType(prev, e.typ); found {
list[i].multiples = true
// ignore this entry
} else {
@@ -266,14 +262,14 @@ func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
return list[:n]
}
-func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
+func lookupType(m map[Type]int, typ Type) (int, bool) {
// fast path: maybe the types are equal
if i, found := m[typ]; found {
return i, true
}
for t, i := range m {
- if check.identical(t, typ) {
+ if Identical(t, typ) {
return i, true
}
}
@@ -306,41 +302,41 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
// To improve error messages, also report the wrong signature
// when the method exists on *V instead of V.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
- check.completeInterface(nopos, T)
-
// fast path for common case
if T.Empty() {
return
}
- if ityp := asInterface(V); ityp != nil {
- check.completeInterface(nopos, ityp)
- // TODO(gri) allMethods is sorted - can do this more efficiently
- for _, m := range T.allMethods {
- _, f := lookupMethod(ityp.allMethods, m.pkg, m.name)
+ if ityp, _ := under(V).(*Interface); ityp != nil {
+ // TODO(gri) the methods are sorted - could do this more efficiently
+ for _, m := range T.typeSet().methods {
+ _, f := ityp.typeSet().LookupMethod(m.pkg, m.name)
if f == nil {
- // if m is the magic method == we're ok (interfaces are comparable)
- if m.name == "==" || !static {
+ if !static {
continue
}
+ // We don't do any case-fold check if V is an interface.
return m, f
}
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
- if len(ftyp.tparams) != len(mtyp.tparams) {
+ if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
+ if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
+ panic("method with type parameters")
+ }
// If the methods have type parameters we don't care whether they
// are the same or not, as long as they match up. Use unification
// to see if they can be made to match.
// TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
- u.x.init(ftyp.tparams)
+ u := newUnifier(true)
+ u.x.init(ftyp.TypeParams().list())
if !u.unify(ftyp, mtyp) {
return m, f
}
@@ -350,17 +346,25 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
}
// A concrete type implements T if it implements all methods of T.
- Vd, _ := deref(V)
- Vn := asNamed(Vd)
- for _, m := range T.allMethods {
- // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
- obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
+ for _, m := range T.typeSet().methods {
+ // TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)?
+ obj, _, _ := lookupFieldOrMethod(V, false, false, m.pkg, m.name)
// Check if *V implements this method of T.
if obj == nil {
ptr := NewPointer(V)
- obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name)
+ obj, _, _ = lookupFieldOrMethod(ptr, false, false, m.pkg, m.name)
+ if obj == nil {
+ // If we didn't find the exact method (even with pointer
+ // receiver), look to see if there is a method that
+ // matches m.name with case-folding.
+ obj, _, _ = lookupFieldOrMethod(V, false, true, m.pkg, m.name)
+ }
if obj != nil {
+ // methods may not have a fully set up signature yet
+ if check != nil {
+ check.objDecl(obj, nil)
+ }
return m, obj.(*Func)
}
}
@@ -368,10 +372,6 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
if f == nil {
- // if m is the magic method == and V is comparable, we're ok
- if m.name == "==" && Comparable(V) {
- continue
- }
return m, nil
}
@@ -383,28 +383,11 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// both methods must have the same number of type parameters
ftyp := f.typ.(*Signature)
mtyp := m.typ.(*Signature)
- if len(ftyp.tparams) != len(mtyp.tparams) {
+ if ftyp.TypeParams().Len() != mtyp.TypeParams().Len() {
return m, f
}
-
- // If V is a (instantiated) generic type, its methods are still
- // parameterized using the original (declaration) receiver type
- // parameters (subst simply copies the existing method list, it
- // does not instantiate the methods).
- // In order to compare the signatures, substitute the receiver
- // type parameters of ftyp with V's instantiation type arguments.
- // This lazily instantiates the signature of method f.
- if Vn != nil && len(Vn.tparams) > 0 {
- // Be careful: The number of type arguments may not match
- // the number of receiver parameters. If so, an error was
- // reported earlier but the length discrepancy is still
- // here. Exit early in this case to prevent an assertion
- // failure in makeSubstMap.
- // TODO(gri) Can we avoid this check by fixing the lengths?
- if len(ftyp.rparams) != len(Vn.targs) {
- return
- }
- ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature)
+ if !acceptMethodTypeParams && ftyp.TypeParams().Len() > 0 {
+ panic("method with type parameters")
}
// If the methods have type parameters we don't care whether they
@@ -412,8 +395,21 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// to see if they can be made to match.
// TODO(gri) is this always correct? what about type bounds?
// (Alternative is to rename/subst type parameters and compare.)
- u := newUnifier(check, true)
- u.x.init(ftyp.tparams)
+ u := newUnifier(true)
+ if ftyp.TypeParams().Len() > 0 {
+ // We reach here only if we accept method type parameters.
+ // In this case, unification must consider any receiver
+ // and method type parameters as "free" type parameters.
+ assert(acceptMethodTypeParams)
+ // We don't have a test case for this at the moment since
+ // we can't parse method type parameters. Keeping the
+ // unimplemented call so that we test this code if we
+ // enable method type parameters.
+ unimplemented()
+ u.x.init(append(ftyp.RecvTypeParams().list(), ftyp.TypeParams().list()...))
+ } else {
+ u.x.init(ftyp.RecvTypeParams().list())
+ }
if !u.unify(ftyp, mtyp) {
return m, f
}
@@ -422,6 +418,59 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
return
}
+// missingMethodReason returns a string giving the detailed reason for a missing method m,
+// where m is missing from V, but required by T. It puts the reason in parentheses,
+// and may include more have/want info after that. If non-nil, wrongType is a relevant
+// method that matches in some way. It may have the correct name, but wrong type, or
+// it may have a pointer receiver, or it may have the correct name except wrong case.
+func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string {
+ var r string
+ var mname string
+ if check.conf.CompilerErrorMessages {
+ mname = m.Name() + " method"
+ } else {
+ mname = "method " + m.Name()
+ }
+ if wrongType != nil {
+ if Identical(m.typ, wrongType.typ) {
+ if m.Name() == wrongType.Name() {
+ r = fmt.Sprintf("(%s has pointer receiver)", mname)
+ } else {
+ r = fmt.Sprintf("(missing %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
+ mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
+ }
+ } else {
+ if check.conf.CompilerErrorMessages {
+ r = fmt.Sprintf("(wrong type for %s)\n\t\thave %s^^%s\n\t\twant %s^^%s",
+ mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ)
+ } else {
+ r = fmt.Sprintf("(wrong type for %s: have %s, want %s)",
+ mname, wrongType.typ, m.typ)
+ }
+ }
+ // This is a hack to print the function type without the leading
+ // 'func' keyword in the have/want printouts. We could change to have
+ // an extra formatting option for types2.Type that doesn't print out
+ // 'func'.
+ r = strings.Replace(r, "^^func", "", -1)
+ } else if IsInterface(T) && !isTypeParam(T) {
+ if isInterfacePtr(V) {
+ r = fmt.Sprintf("(%s is pointer to interface, not interface)", V)
+ }
+ } else if isInterfacePtr(T) && !isTypeParam(T) {
+ r = fmt.Sprintf("(%s is pointer to interface, not interface)", T)
+ }
+ if r == "" {
+ r = fmt.Sprintf("(missing %s)", mname)
+ }
+ return r
+}
+
+func isInterfacePtr(T Type) bool {
+ p, _ := under(T).(*Pointer)
+ return p != nil && IsInterface(p.base) && !isTypeParam(p.base)
+}
+
// assertableTo reports whether a value of type V can be asserted to have type T.
// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
// method required by V and whether it is missing or just has the wrong type.
@@ -433,7 +482,7 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."
- if asInterface(T) != nil && !forceStrict {
+ if IsInterface(T) && !forceStrict {
return
}
return check.missingMethod(T, V, false)
@@ -443,6 +492,13 @@ func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Fun
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {
if p, _ := typ.(*Pointer); p != nil {
+ // p.base should never be nil, but be conservative
+ if p.base == nil {
+ if debug {
+ panic("pointer with nil base type (possibly due to an invalid cyclic declaration)")
+ }
+ return Typ[Invalid], true
+ }
return p.base, true
}
return typ, false
@@ -451,8 +507,8 @@ func deref(typ Type) (Type, bool) {
// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
// (named or unnamed) struct and returns its base. Otherwise it returns typ.
func derefStructPtr(typ Type) Type {
- if p := asPointer(typ); p != nil {
- if asStruct(p.base) != nil {
+ if p, _ := under(typ).(*Pointer); p != nil {
+ if _, ok := under(p.base).(*Struct); ok {
return p.base
}
}
@@ -491,21 +547,20 @@ func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
return -1, nil
}
-// ptrRecv reports whether the receiver is of the form *T.
-func ptrRecv(f *Func) bool {
- // If a method's receiver type is set, use that as the source of truth for the receiver.
- // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty
- // signature. We may reach here before the signature is fully set up: we must explicitly
- // check if the receiver is set (we cannot just look for non-nil f.typ).
- if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil {
- _, isPtr := deref(sig.recv.typ)
- return isPtr
+// lookupMethodFold is like lookupMethod, but if checkFold is true, it matches a method
+// name if the names are equal with case folding.
+func lookupMethodFold(methods []*Func, pkg *Package, name string, checkFold bool) (int, *Func) {
+ if name != "_" {
+ for i, m := range methods {
+ if m.name != name && !(checkFold && strings.EqualFold(m.name, name)) {
+ continue
+ }
+ // Use m.name, since we've already checked that m.name and
+ // name are equal with folding.
+ if m.sameId(pkg, m.name) {
+ return i, m
+ }
+ }
}
-
- // If a method's type is not set it may be a method/function that is:
- // 1) client-supplied (via NewFunc with no signature), or
- // 2) internally created but not yet type-checked.
- // For case 1) we can't do anything; the client must know what they are doing.
- // For case 2) we can use the information gathered by the resolver.
- return f.hasPtrRecv
+ return -1, nil
}
diff --git a/src/cmd/compile/internal/types2/map.go b/src/cmd/compile/internal/types2/map.go
new file mode 100644
index 0000000000000000000000000000000000000000..0d3464caae728b7b0207365264170337666953ad
--- /dev/null
+++ b/src/cmd/compile/internal/types2/map.go
@@ -0,0 +1,24 @@
+// Copyright 2011 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
+
+// A Map represents a map type.
+type Map struct {
+ key, elem Type
+}
+
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key: key, elem: elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elem }
+
+func (t *Map) Underlying() Type { return t }
+func (t *Map) String() string { return TypeString(t, nil) }
diff --git a/src/cmd/compile/internal/types2/mono.go b/src/cmd/compile/internal/types2/mono.go
new file mode 100644
index 0000000000000000000000000000000000000000..7bd79f4282efdcc1d196c4149428c07c3c459169
--- /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, xlist []syntax.Expr) {
+ for i, tpar := range tparams {
+ pos := pos
+ if i < len(xlist) {
+ pos = syntax.StartPos(xlist[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 0000000000000000000000000000000000000000..19d0e95637947191d268a776be9f7c4f54380c00
--- /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
new file mode 100644
index 0000000000000000000000000000000000000000..51ea27a6dbd19dab05d799c8315bbb28d33e0102
--- /dev/null
+++ b/src/cmd/compile/internal/types2/named.go
@@ -0,0 +1,347 @@
+// Copyright 2011 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"
+ "sync"
+)
+
+// A Named represents a named (defined) type.
+type Named struct {
+ check *Checker
+ info typeInfo // for cycle detection
+ obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types
+ orig *Named // original, uninstantiated type
+ fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ tparams *TypeParamList // type parameters, or nil
+ targs *TypeList // type arguments (after instantiation), or nil
+ methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
+
+ // resolver may be provided to lazily resolve type parameters, underlying, and methods.
+ resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods []*Func)
+ once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
+// The underlying type must not be a *Named.
+func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ if _, ok := underlying.(*Named); ok {
+ panic("underlying type must not be *Named")
+ }
+ return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
+}
+
+func (t *Named) resolve(ctxt *Context) *Named {
+ if t.resolver == nil {
+ return t
+ }
+
+ t.once.Do(func() {
+ // TODO(mdempsky): Since we're passing t to the resolver anyway
+ // (necessary because types2 expects the receiver type for methods
+ // on defined interface types to be the Named rather than the
+ // underlying Interface), maybe it should just handle calling
+ // SetTypeParams, SetUnderlying, and AddMethod instead? Those
+ // methods would need to support reentrant calls though. It would
+ // also make the API more future-proof towards further extensions
+ // (like SetTypeParams).
+ t.tparams, t.underlying, t.methods = t.resolver(ctxt, t)
+ t.fromRHS = t.underlying // for cycle detection
+ })
+ return t
+}
+
+// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
+func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods []*Func) *Named {
+ typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
+ if typ.orig == nil {
+ typ.orig = typ
+ }
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ // Ensure that typ is always expanded and sanity-checked.
+ if check != nil {
+ check.defTypes = append(check.defTypes, typ)
+ }
+ return typ
+}
+
+// Obj returns the type name for the declaration defining the named type t. For
+// instantiated types, this is the type name of the base type.
+func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this is the same as t.obj
+
+// Origin returns the parameterized type from which the named type t is
+// instantiated. If t is not an instantiated type, the result is t.
+func (t *Named) Origin() *Named { return t.orig }
+
+// TODO(gri) Come up with a better representation and API to distinguish
+// between parameterized instantiated and non-instantiated types.
+
+// TypeParams returns the type parameters of the named type t, or nil.
+// The result is non-nil for an (originally) parameterized type even if it is instantiated.
+func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
+
+// SetTypeParams sets the type parameters of the named type t.
+func (t *Named) SetTypeParams(tparams []*TypeParam) { t.resolve(nil).tparams = bindTParams(tparams) }
+
+// TypeArgs returns the type arguments used to instantiate the named type t.
+func (t *Named) TypeArgs() *TypeList { return t.targs }
+
+// NumMethods returns the number of explicit methods whose receiver is named type t.
+func (t *Named) NumMethods() int { return len(t.resolve(nil).methods) }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func { return t.resolve(nil).methods[i] }
+
+// SetUnderlying sets the underlying type and marks t as complete.
+func (t *Named) SetUnderlying(underlying Type) {
+ if underlying == nil {
+ panic("underlying type must not be nil")
+ }
+ if _, ok := underlying.(*Named); ok {
+ panic("underlying type must not be *Named")
+ }
+ t.resolve(nil).underlying = underlying
+}
+
+// AddMethod adds method m unless it is already in the method list.
+func (t *Named) AddMethod(m *Func) {
+ t.resolve(nil)
+ if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
+ t.methods = append(t.methods, m)
+ }
+}
+
+func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
+func (t *Named) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// under returns the expanded underlying type of n0; possibly by following
+// forward chains of named types. If an underlying type is found, resolve
+// the chain by setting the underlying type for each defined type in the
+// 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()
+
+ // If the underlying type of a defined type is not a defined
+ // (incl. instance) type, then that is the desired underlying
+ // type.
+ var n1 *Named
+ switch u1 := u.(type) {
+ case nil:
+ // After expansion via Underlying(), we should never encounter a nil
+ // underlying.
+ panic("nil underlying")
+ default:
+ // common case
+ return u
+ case *Named:
+ // handled below
+ n1 = u1
+ }
+
+ if n0.check == nil {
+ panic("Named.check == nil but type is incomplete")
+ }
+
+ // Invariant: after this point n0 as well as any named types in its
+ // underlying chain should be set up when this function exits.
+ check := n0.check
+ n := n0
+
+ seen := make(map[*Named]int) // types that need their underlying resolved
+ var path []Object // objects encountered, for cycle reporting
+
+loop:
+ for {
+ seen[n] = len(seen)
+ path = append(path, n.obj)
+ n = n1
+ if i, ok := seen[n]; ok {
+ // cycle
+ check.cycleError(path[i:])
+ u = Typ[Invalid]
+ break
+ }
+ u = n.Underlying()
+ switch u1 := u.(type) {
+ case nil:
+ u = Typ[Invalid]
+ break loop
+ default:
+ break loop
+ case *Named:
+ // Continue collecting *Named types in the chain.
+ n1 = u1
+ }
+ }
+
+ for n := range seen {
+ // We should never have to update the underlying type of an imported type;
+ // those underlying types should have been resolved during the import.
+ // Also, doing so would lead to a race condition (was issue #31749).
+ // Do this check always, not just in debug mode (it's cheap).
+ if n.obj.pkg != check.pkg {
+ panic("imported type with unresolved underlying type")
+ }
+ n.underlying = u
+ }
+
+ return u
+}
+
+func (n *Named) setUnderlying(typ Type) {
+ if n != nil {
+ n.underlying = typ
+ }
+}
+
+// bestContext returns the best available context. In order of preference:
+// - the given ctxt, if non-nil
+// - check.ctxt, if check is non-nil
+// - a new Context
+func (check *Checker) bestContext(ctxt *Context) *Context {
+ if ctxt != nil {
+ return ctxt
+ }
+ if check != nil {
+ if check.ctxt == nil {
+ check.ctxt = NewContext()
+ }
+ return check.ctxt
+ }
+ return NewContext()
+}
+
+// expandNamed ensures that the underlying type of n is instantiated.
+// 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 _, 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.instanceHash(n.orig, n.targs.list())
+ // ensure that an instance is recorded for h to avoid infinite recursion.
+ ctxt.update(h, n.orig, n.TypeArgs().list(), n)
+
+ smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
+ underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
+
+ for i := 0; i < n.orig.NumMethods(); i++ {
+ origm := n.orig.Method(i)
+
+ // During type checking origm may not have a fully set up type, so defer
+ // instantiation of its signature until later.
+ m := NewFunc(origm.pos, origm.pkg, origm.name, nil)
+ m.hasPtrRecv_ = origm.hasPtrRecv()
+ // Setting instRecv here allows us to complete later (we need the
+ // instRecv to get targs and the original method).
+ m.instRecv = n
+
+ methods = append(methods, m)
+ }
+ } else {
+ underlying = Typ[Invalid]
+ }
+
+ // Methods should not escape the type checker API without being completed. If
+ // we're in the context of a type checking pass, we need to defer this until
+ // later (not all methods may have types).
+ completeMethods := func() {
+ for _, m := range methods {
+ if m.instRecv != nil {
+ check.completeMethod(ctxt, m)
+ }
+ }
+ }
+ if check != nil {
+ check.later(completeMethods)
+ } else {
+ completeMethods()
+ }
+
+ return n.orig.tparams, underlying, methods
+}
+
+func (check *Checker) completeMethod(ctxt *Context, m *Func) {
+ assert(m.instRecv != nil)
+ rbase := m.instRecv
+ m.instRecv = nil
+ m.setColor(black)
+
+ assert(rbase.TypeArgs().Len() > 0)
+
+ // Look up the original method.
+ _, orig := lookupMethod(rbase.orig.methods, rbase.obj.pkg, m.name)
+ assert(orig != nil)
+ if check != nil {
+ check.objDecl(orig, nil)
+ }
+ origSig := orig.typ.(*Signature)
+ if origSig.RecvTypeParams().Len() != rbase.targs.Len() {
+ m.typ = origSig // or new(Signature), but we can't use Typ[Invalid]: Funcs must have Signature type
+ return // error reported elsewhere
+ }
+
+ smap := makeSubstMap(origSig.RecvTypeParams().list(), rbase.targs.list())
+ sig := check.subst(orig.pos, origSig, smap, ctxt).(*Signature)
+ if sig == origSig {
+ // No substitution occurred, but we still need to create a new signature to
+ // hold the instantiated receiver.
+ copy := *origSig
+ sig = ©
+ }
+ var rtyp Type
+ if m.hasPtrRecv() {
+ rtyp = NewPointer(rbase)
+ } else {
+ rtyp = rbase
+ }
+ sig.recv = NewParam(origSig.recv.pos, origSig.recv.pkg, origSig.recv.name, rtyp)
+
+ m.typ = sig
+}
+
+// safeUnderlying returns the underlying of typ without expanding instances, to
+// avoid infinite recursion.
+//
+// TODO(rfindley): eliminate this function or give it a better name.
+func safeUnderlying(typ Type) Type {
+ if t, _ := typ.(*Named); t != nil {
+ return t.underlying
+ }
+ return typ.Underlying()
+}
diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go
index 844bc34b6a3d1e7034880d667b48b1bedcef87f2..c7c64ca9d5dd209224e30d49ae80c1d077bfa09a 100644
--- a/src/cmd/compile/internal/types2/object.go
+++ b/src/cmd/compile/internal/types2/object.go
@@ -186,6 +186,45 @@ func (obj *object) sameId(pkg *Package, name string) bool {
return pkg.path == obj.pkg.path
}
+// less reports whether object a is ordered before object b.
+//
+// Objects are ordered nil before non-nil, exported before
+// non-exported, then by name, and finally (for non-exported
+// functions) by package height and path.
+func (a *object) less(b *object) bool {
+ if a == b {
+ return false
+ }
+
+ // Nil before non-nil.
+ if a == nil {
+ return true
+ }
+ if b == nil {
+ return false
+ }
+
+ // Exported functions before non-exported.
+ ea := isExported(a.name)
+ eb := isExported(b.name)
+ if ea != eb {
+ return ea
+ }
+
+ // Order by name and then (for non-exported names) by package.
+ if a.name != b.name {
+ return a.name < b.name
+ }
+ if !ea {
+ if a.pkg.height != b.pkg.height {
+ return a.pkg.height < b.pkg.height
+ }
+ return a.pkg.path < b.pkg.path
+ }
+
+ return false
+}
+
// A PkgName represents an imported Go package.
// PkgNames don't have a type.
type PkgName struct {
@@ -237,6 +276,26 @@ func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
}
+// NewTypeNameLazy returns a new defined type like NewTypeName, but it
+// lazily calls resolve to finish constructing the Named object.
+func NewTypeNameLazy(pos syntax.Pos, pkg *Package, name string, load func(named *Named) (tparams []*TypeParam, underlying Type, methods []*Func)) *TypeName {
+ obj := NewTypeName(pos, pkg, name, nil)
+
+ resolve := func(_ *Context, t *Named) (*TypeParamList, Type, []*Func) {
+ tparams, underlying, methods := load(t)
+
+ switch underlying.(type) {
+ case nil, *Named:
+ panic(fmt.Sprintf("invalid underlying type %T", t.underlying))
+ }
+
+ return bindTParams(tparams), underlying, methods
+ }
+
+ NewNamed(obj, nil, nil).resolver = resolve
+ return obj
+}
+
// IsAlias reports whether obj is an alias name for a type.
func (obj *TypeName) IsAlias() bool {
switch t := obj.typ.(type) {
@@ -256,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
}
@@ -304,7 +365,8 @@ func (*Var) isDependency() {} // a variable may be a dependency of an initializa
// An abstract method may belong to many interfaces due to embedding.
type Func struct {
object
- hasPtrRecv bool // only valid for methods that don't have a type yet
+ instRecv *Named // if non-nil, the receiver type for an incomplete instance method
+ hasPtrRecv_ bool // only valid for methods that don't have a type yet; use hasPtrRecv() to read
}
// NewFunc returns a new function with the given signature, representing
@@ -315,7 +377,7 @@ func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func {
if sig != nil {
typ = sig
}
- return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false}
+ return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, nil, false}
}
// FullName returns the package- or receiver-type-qualified name of
@@ -327,36 +389,27 @@ func (obj *Func) FullName() string {
}
// Scope returns the scope of the function's body block.
+// The result is nil for imported or instantiated functions and methods
+// (but there is also no mechanism to get to an instantiated function).
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
-// Less reports whether function a is ordered before function b.
-//
-// Functions are ordered exported before non-exported, then by name,
-// and finally (for non-exported functions) by package path.
-//
-// TODO(gri) The compiler also sorts by package height before package
-// path for non-exported names.
-func (a *Func) less(b *Func) bool {
- if a == b {
- return false
+// hasPtrRecv reports whether the receiver is of the form *T for the given method obj.
+func (obj *Func) hasPtrRecv() bool {
+ // If a method's receiver type is set, use that as the source of truth for the receiver.
+ // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty
+ // signature. We may reach here before the signature is fully set up: we must explicitly
+ // check if the receiver is set (we cannot just look for non-nil obj.typ).
+ if sig, _ := obj.typ.(*Signature); sig != nil && sig.recv != nil {
+ _, isPtr := deref(sig.recv.typ)
+ return isPtr
}
- // Exported functions before non-exported.
- ea := isExported(a.name)
- eb := isExported(b.name)
- if ea != eb {
- return ea
- }
-
- // Order by name and then (for non-exported names) by package.
- if a.name != b.name {
- return a.name < b.name
- }
- if !ea {
- return a.pkg.path < b.pkg.path
- }
-
- return false
+ // If a method's type is not set it may be a method/function that is:
+ // 1) client-supplied (via NewFunc with no signature), or
+ // 2) internally created but not yet type-checked.
+ // For case 1) we can't do anything; the client must know what they are doing.
+ // For case 2) we can use the information gathered by the resolver.
+ return obj.hasPtrRecv_
}
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
@@ -407,6 +460,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
case *TypeName:
tname = obj
buf.WriteString("type")
+ if isTypeParam(typ) {
+ buf.WriteString(" parameter")
+ }
case *Var:
if obj.isField {
@@ -452,19 +508,34 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
}
if tname != nil {
- // We have a type object: Don't print anything more for
- // basic types since there's no more information (names
- // are the same; see also comment in TypeName.IsAlias).
- if _, ok := typ.(*Basic); ok {
+ switch t := typ.(type) {
+ case *Basic:
+ // Don't print anything more for basic types since there's
+ // no more information.
return
+ case *Named:
+ if t.TypeParams().Len() > 0 {
+ newTypeWriter(buf, qf).tParamList(t.TypeParams().list())
+ }
}
if tname.IsAlias() {
buf.WriteString(" =")
+ } else if t, _ := typ.(*TypeParam); t != nil {
+ typ = t.bound
} else {
+ // TODO(gri) should this be fromRHS for *Named?
typ = under(typ)
}
}
+ // Special handling for any: because WriteType will format 'any' as 'any',
+ // resulting in the object string `type any = any` rather than `type any =
+ // interface{}`. To avoid this, swap in a different empty interface.
+ if obj == universeAny {
+ assert(Identical(typ, &emptyInterface))
+ typ = &emptyInterface
+ }
+
buf.WriteByte(' ')
WriteType(buf, typ, qf)
}
diff --git a/src/cmd/compile/internal/types2/object_test.go b/src/cmd/compile/internal/types2/object_test.go
index 7f63c793325721dca1512a4628d970e31f523892..8f0303d4b2457851ec9f1392cca40ee335219ca2 100644
--- a/src/cmd/compile/internal/types2/object_test.go
+++ b/src/cmd/compile/internal/types2/object_test.go
@@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package types2
+package types2_test
import (
"cmd/compile/internal/syntax"
+ "internal/testenv"
"strings"
"testing"
-)
-func parseSrc(path, src string) (*syntax.File, error) {
- return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), nil, nil, 0)
-}
+ . "cmd/compile/internal/types2"
+)
func TestIsAlias(t *testing.T) {
check := func(obj *TypeName, want bool) {
@@ -25,7 +24,7 @@ func TestIsAlias(t *testing.T) {
check(Unsafe.Scope().Lookup("Pointer").(*TypeName), false)
for _, name := range Universe.Names() {
if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
- check(obj, name == "byte" || name == "rune")
+ check(obj, name == "any" || name == "byte" || name == "rune")
}
}
@@ -33,6 +32,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
@@ -40,12 +41,13 @@ func TestIsAlias(t *testing.T) {
{NewTypeName(nopos, nil, "t0", nil), false}, // no type yet
{NewTypeName(nopos, pkg, "t0", nil), false}, // no type yet
{t1, false}, // type name refers to named type and vice versa
- {NewTypeName(nopos, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type
- {NewTypeName(nopos, pkg, "t3", n1), true}, // type name refers to named type with different type name
- {NewTypeName(nopos, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
- {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
+ {NewTypeName(nopos, nil, "t2", NewInterfaceType(nil, nil)), true}, // type name refers to unnamed type
+ {NewTypeName(nopos, pkg, "t3", n1), true}, // type name refers to named type with different type name
+ {NewTypeName(nopos, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
+ {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)
}
@@ -86,3 +88,80 @@ func TestEmbeddedMethod(t *testing.T) {
t.Fatalf("%s (%p) != %s (%p)", orig, orig, embed, embed)
}
}
+
+var testObjects = []struct {
+ src string
+ obj string
+ want string
+}{
+ {"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
+
+ {"const c = 1.2", "c", "const p.c untyped float"},
+ {"const c float64 = 3.14", "c", "const p.c float64"},
+
+ {"type t struct{f int}", "t", "type p.t struct{f int}"},
+ {"type t func(int)", "t", "type p.t func(int)"},
+ {"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
+ {"type t[P any] struct{f P}", "t.P", "type parameter P any"},
+ {"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
+
+ {"type t = struct{f int}", "t", "type p.t = struct{f int}"},
+ {"type t = func(int)", "t", "type p.t = func(int)"},
+
+ {"var v int", "v", "var p.v int"},
+
+ {"func f(int) string", "f", "func p.f(int) string"},
+ {"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
+ {"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
+ {"", "any", "type any = interface{}"},
+}
+
+func TestObjectString(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ for _, test := range testObjects {
+ src := "package p; " + test.src
+ pkg, err := makePkg(src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+
+ names := strings.Split(test.obj, ".")
+ if len(names) != 1 && len(names) != 2 {
+ t.Errorf("%s: invalid object path %s", test.src, test.obj)
+ continue
+ }
+ _, obj := pkg.Scope().LookupParent(names[0], nopos)
+ if obj == nil {
+ t.Errorf("%s: %s not found", test.src, names[0])
+ continue
+ }
+ if len(names) == 2 {
+ if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
+ obj = lookupTypeParamObj(typ.TypeParams(), names[1])
+ if obj == nil {
+ t.Errorf("%s: %s not found", test.src, test.obj)
+ continue
+ }
+ } else {
+ t.Errorf("%s: %s has no type parameters", test.src, names[0])
+ continue
+ }
+ }
+
+ if got := obj.String(); got != test.want {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.want)
+ }
+ }
+}
+
+func lookupTypeParamObj(list *TypeParamList, name string) Object {
+ for i := 0; i < list.Len(); i++ {
+ tpar := list.At(i)
+ if tpar.Obj().Name() == name {
+ return tpar.Obj()
+ }
+ }
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
index 455d8b5dd1df22c40fb3b0bf12d8f8b914ed1ea9..f6bd0291ece212a7f6b261883a7bd61a6d1004e7 100644
--- a/src/cmd/compile/internal/types2/operand.go
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -116,7 +116,7 @@ func operandString(x *operand, qf Qualifier) string {
case nil, Typ[Invalid]:
return "nil (with invalid type)"
case Typ[UntypedNil]:
- return "untyped nil"
+ return "nil"
default:
return fmt.Sprintf("nil (of type %s)", TypeString(x.typ, qf))
}
@@ -176,16 +176,17 @@ func operandString(x *operand, qf Qualifier) string {
if hasType {
if x.typ != Typ[Invalid] {
var intro string
- switch {
- case isGeneric(x.typ):
- intro = " of generic type "
- case asTypeParam(x.typ) != nil:
- intro = " of type parameter type "
- default:
+ if isGeneric(x.typ) {
+ intro = " of parameterized type "
+ } else {
intro = " of type "
}
buf.WriteString(intro)
WriteType(&buf, x.typ, qf)
+ if tpar, _ := x.typ.(*TypeParam); tpar != nil {
+ buf.WriteString(" constrained by ")
+ WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here
+ }
} else {
buf.WriteString(" with invalid type")
}
@@ -249,46 +250,61 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
V := x.typ
// x's type is identical to T
- if check.identical(V, T) {
+ if Identical(V, T) {
return true, 0
}
- Vu := optype(V)
- Tu := optype(T)
+ Vu := under(V)
+ Tu := under(T)
+ Vp, _ := V.(*TypeParam)
+ Tp, _ := T.(*TypeParam)
// x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- if t, ok := Tu.(*Sum); ok {
- return t.is(func(t Type) bool {
- // TODO(gri) this could probably be more efficient
- ok, _ := x.assignableTo(check, t, reason)
- return ok
+ 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
+ }
+ // 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 check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ // and neither V nor T is a type parameter.
+ if Identical(Vu, Tu) && (!hasName(V) || !hasName(T)) && Vp == nil && Tp == nil {
return true, 0
}
- // T is an interface type and x implements T
- if Ti, ok := Tu.(*Interface); ok {
- if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
+ // T is an interface type and x implements T and T is not a type parameter
+ if Ti, ok := Tu.(*Interface); ok && Tp == nil {
+ if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
if reason != nil {
- if wrongType != nil {
- if check.identical(m.typ, wrongType.typ) {
- *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
+ if check.conf.CompilerErrorMessages {
+ *reason = check.sprintf("%s does not implement %s %s", x.typ, T,
+ check.missingMethodReason(x.typ, T, m, wrongType))
+ } else {
+ if wrongType != nil {
+ if Identical(m.typ, wrongType.typ) {
+ *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
+ } else {
+ *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
+ }
} else {
- *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
+ *reason = "missing method " + m.Name()
}
-
- } else {
- *reason = "missing method " + m.Name()
}
}
return false, _InvalidIfaceAssign
@@ -296,15 +312,91 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
return true, 0
}
+ // Provide extra detail in compiler error messages in some cases when T is
+ // not an interface.
+ if check != nil && check.conf.CompilerErrorMessages {
+ if isInterfacePtr(Tu) {
+ if reason != nil {
+ *reason = check.sprintf("%s does not implement %s (%s is pointer to interface, not interface)", x.typ, T, T)
+ }
+ return false, _InvalidIfaceAssign
+ }
+ if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
+ if m, _ := check.missingMethod(T, Vi, true); m == nil {
+ // T implements Vi, so give hint about type assertion.
+ if reason != nil {
+ *reason = check.sprintf("need type assertion")
+ }
+ return false, _IncompatibleAssign
+ }
+ }
+ }
+
// 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 && check.identical(Vc.elem, Tc.elem) {
- return !isNamed(V) || !isNamed(T), _InvalidChanAssign
+ if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
+ 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/package.go b/src/cmd/compile/internal/types2/package.go
index 31b1e7178771f5db37264b4886b8b3604e03b4b3..8044e7e6a76ce25254bcacab3599d547698e64a6 100644
--- a/src/cmd/compile/internal/types2/package.go
+++ b/src/cmd/compile/internal/types2/package.go
@@ -13,8 +13,9 @@ type Package struct {
path string
name string
scope *Scope
- complete bool
imports []*Package
+ height int
+ complete bool
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
}
@@ -22,8 +23,14 @@ type Package struct {
// NewPackage returns a new Package for the given package path and name.
// The package is not complete and contains no explicit imports.
func NewPackage(path, name string) *Package {
+ return NewPackageHeight(path, name, 0)
+}
+
+// NewPackageHeight is like NewPackage, but allows specifying the
+// package's height.
+func NewPackageHeight(path, name string, height int) *Package {
scope := NewScope(Universe, nopos, nopos, fmt.Sprintf("package %q", path))
- return &Package{path: path, name: name, scope: scope}
+ return &Package{path: path, name: name, scope: scope, height: height}
}
// Path returns the package path.
@@ -32,13 +39,22 @@ func (pkg *Package) Path() string { return pkg.path }
// Name returns the package name.
func (pkg *Package) Name() string { return pkg.name }
+// Height returns the package height.
+func (pkg *Package) Height() int { return pkg.height }
+
// SetName sets the package name.
func (pkg *Package) SetName(name string) { pkg.name = name }
// Scope returns the (complete or incomplete) package scope
// holding the objects declared at package level (TypeNames,
// Consts, Vars, and Funcs).
-func (pkg *Package) Scope() *Scope { return pkg.scope }
+// For a nil pkg receiver, Scope returns the Universe scope.
+func (pkg *Package) Scope() *Scope {
+ if pkg != nil {
+ return pkg.scope
+ }
+ return Universe
+}
// A package is complete if its scope contains (at least) all
// exported objects; otherwise it is incomplete.
diff --git a/src/cmd/compile/internal/types2/pointer.go b/src/cmd/compile/internal/types2/pointer.go
new file mode 100644
index 0000000000000000000000000000000000000000..63055fc6b056a76822d261a2913b502f9fab2476
--- /dev/null
+++ b/src/cmd/compile/internal/types2/pointer.go
@@ -0,0 +1,19 @@
+// Copyright 2011 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
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+ base Type // element type
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+func (p *Pointer) Underlying() Type { return p }
+func (p *Pointer) String() string { return TypeString(p, nil) }
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index ae186a0b5d1efd402335101d6caa125ff277c3a2..cf2993f68b08b15e0258a8ff367a85a312b85a51 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -6,80 +6,98 @@
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) {
- case *Basic, *Named, *TypeParam, *instance:
- return true
- }
- return false
+// 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
}
-// 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.tparams != nil && named.targs == nil
+// 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(structuralType(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(structuralType(t), info).
+func allBasic(t Type, info BasicInfo) bool {
+ if tpar, _ := t.(*TypeParam); tpar != nil {
+ return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
+ }
+ return isBasic(t, info)
}
-func is(typ Type, what BasicInfo) bool {
- switch t := optype(typ).(type) {
- case *Basic:
- return t.info&what != 0
- case *Sum:
- return t.is(func(typ Type) bool { return is(typ, what) })
+// 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
}
-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 Basic()!
- // A *Named or *instance type is always typed, so
- // we only need to check if we have a true *Basic
- // type.
- t, _ := typ.(*Basic)
- return t == nil || t.info&IsUntyped == 0
+ // set up. Must not call under()!
+ 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) }
+// IsInterface reports whether t is an interface type.
+func IsInterface(t Type) bool {
+ _, ok := under(t).(*Interface)
+ return ok
+}
-func isConstType(typ Type) bool {
- // Type parameters are never const types.
- t, _ := under(typ).(*Basic)
- return t != nil && t.info&IsConstType != 0
+// isTypeParam reports whether t is a type parameter.
+func isTypeParam(t Type) bool {
+ _, ok := t.(*TypeParam)
+ return ok
}
-// IsInterface reports whether typ is an interface type.
-func IsInterface(typ Type) bool {
- return asInterface(typ) != nil
+// 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.
@@ -96,24 +114,12 @@ func comparable(T Type, seen map[Type]bool) bool {
}
seen[T] = true
- // If T is a type parameter not constrained by any type
- // list (i.e., it's underlying type is the top type),
- // T is comparable if it has the == method. Otherwise,
- // the underlying type "wins". For instance
- //
- // interface{ comparable; type []byte }
- //
- // is not comparable because []byte is not comparable.
- if t := asTypeParam(T); t != nil && optype(t) == theTop {
- return t.Bound().IsComparable()
- }
-
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Basic:
// assume invalid types to be comparable
// to avoid follow-up errors
return t.kind != UntypedNil
- case *Pointer, *Interface, *Chan:
+ case *Pointer, *Chan:
return true
case *Struct:
for _, f := range t.fields {
@@ -124,42 +130,27 @@ func comparable(T Type, seen map[Type]bool) bool {
return true
case *Array:
return comparable(t.elem, seen)
- case *Sum:
- pred := func(t Type) bool {
- return comparable(t, seen)
- }
- return t.is(pred)
- case *TypeParam:
- return t.Bound().IsComparable()
+ case *Interface:
+ return !isTypeParam(T) || t.IsComparable()
}
return false
}
-// hasNil reports whether a type includes the nil value.
-func hasNil(typ Type) bool {
- switch t := optype(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
- case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return u.kind == UnsafePointer
+ case *Slice, *Pointer, *Signature, *Map, *Chan:
return true
- case *Sum:
- return t.is(hasNil)
+ case *Interface:
+ return !isTypeParam(t) || u.typeSet().underIs(func(u Type) bool {
+ return u != nil && hasNil(u)
+ })
}
return false
}
-// identical reports whether x and y are identical types.
-// Receivers of Signature types are ignored.
-func (check *Checker) identical(x, y Type) bool {
- return check.identical0(x, y, true, nil)
-}
-
-// identicalIgnoreTags reports whether x and y are identical types if tags are ignored.
-// Receivers of Signature types are ignored.
-func (check *Checker) identicalIgnoreTags(x, y Type) bool {
- return check.identical0(x, y, false, nil)
-}
-
// An ifacePair is a node in a stack of interface type pairs compared for identity.
type ifacePair struct {
x, y *Interface
@@ -171,11 +162,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
}
// For changes to this code the corresponding changes should be made to unifier.nify.
-func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
- // types must be expanded for comparison
- x = expandf(x)
- y = expandf(y)
-
+func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if x == y {
return true
}
@@ -195,13 +182,13 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
- return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
- return check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.elem, y.elem, cmpTags, p)
}
case *Struct:
@@ -216,7 +203,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if f.embedded != g.embedded ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
- !check.identical0(f.typ, g.typ, cmpTags, p) {
+ !identical(f.typ, g.typ, cmpTags, p) {
return false
}
}
@@ -227,7 +214,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
- return check.identical0(x.base, y.base, cmpTags, p)
+ return identical(x.base, y.base, cmpTags, p)
}
case *Tuple:
@@ -238,7 +225,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
- if !check.identical0(v.typ, w.typ, cmpTags, p) {
+ if !identical(v.typ, w.typ, cmpTags, p) {
return false
}
}
@@ -248,57 +235,79 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
}
case *Signature:
- // Two function types are identical if they have the same number of parameters
- // and result values, corresponding parameter and result types are identical,
- // and either both functions are variadic or neither is. Parameter and result
- // names are not required to match.
- // Generic functions must also have matching type parameter lists, but for the
- // parameter names.
- if y, ok := y.(*Signature); ok {
- return x.variadic == y.variadic &&
- check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
- check.identical0(x.params, y.params, cmpTags, p) &&
- check.identical0(x.results, y.results, cmpTags, p)
+ y, _ := y.(*Signature)
+ if y == nil {
+ return false
}
- case *Sum:
- // Two sum types are identical if they contain the same types.
- // (Sum types always consist of at least two types. Also, the
- // the set (list) of types in a sum type consists of unique
- // types - each type appears exactly once. Thus, two sum types
- // must contain the same number of types to have chance of
- // being equal.
- if y, ok := y.(*Sum); ok && len(x.types) == len(y.types) {
- // Every type in x.types must be in y.types.
- // Quadratic algorithm, but probably good enough for now.
- // TODO(gri) we need a fast quick type ID/hash for all types.
- L:
- for _, x := range x.types {
- for _, y := range y.types {
- if Identical(x, y) {
- continue L // x is in y.types
- }
+ // Two function types are identical if they have the same number of
+ // parameters and result values, corresponding parameter and result types
+ // are identical, and either both functions are variadic or neither is.
+ // Parameter and result names are not required to match, and type
+ // parameters are considered identical modulo renaming.
+
+ if x.TypeParams().Len() != y.TypeParams().Len() {
+ return false
+ }
+
+ // In the case of generic signatures, we will substitute in yparams and
+ // yresults.
+ yparams := y.params
+ yresults := y.results
+
+ if x.TypeParams().Len() > 0 {
+ // We must ignore type parameter names when comparing x and y. The
+ // easiest way to do this is to substitute x's type parameters for y's.
+ xtparams := x.TypeParams().list()
+ ytparams := y.TypeParams().list()
+
+ var targs []Type
+ for i := range xtparams {
+ targs = append(targs, x.TypeParams().At(i))
+ }
+ smap := makeSubstMap(ytparams, targs)
+
+ var check *Checker // ok to call subst on a nil *Checker
+
+ // Constraints must be pair-wise identical, after substitution.
+ for i, xtparam := range xtparams {
+ ybound := check.subst(nopos, ytparams[i].bound, smap, nil)
+ if !identical(xtparam.bound, ybound, cmpTags, p) {
+ return false
}
- return false // x is not in y.types
}
- return true
+
+ yparams = check.subst(nopos, y.params, smap, nil).(*Tuple)
+ yresults = check.subst(nopos, y.results, smap, nil).(*Tuple)
+ }
+
+ return x.variadic == y.variadic &&
+ identical(x.params, yparams, cmpTags, p) &&
+ identical(x.results, yresults, cmpTags, p)
+
+ case *Union:
+ if y, _ := y.(*Union); y != nil {
+ xset := computeUnionTypeSet(nil, nopos, x)
+ yset := computeUnionTypeSet(nil, nopos, y)
+ return xset.terms.equal(yset.terms)
}
case *Interface:
+ // Two interface types are identical if they describe the same type sets.
+ // With the existing implementation restriction, this simplifies to:
+ //
// Two interface types are identical if they have the same set of methods with
- // the same names and identical function types. Lower-case method names from
- // different packages are always different. The order of the methods is irrelevant.
+ // the same names and identical function types, and if any type restrictions
+ // are the same. Lower-case method names from different packages are always
+ // different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
- // If identical0 is called (indirectly) via an external API entry point
- // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
- // that case, interfaces are expected to be complete and lazy completion
- // here is not needed.
- if check != nil {
- check.completeInterface(nopos, x)
- check.completeInterface(nopos, y)
+ xset := x.typeSet()
+ yset := y.typeSet()
+ if !xset.terms.equal(yset.terms) {
+ return false
}
- a := x.allMethods
- b := y.allMethods
+ a := xset.methods
+ b := yset.methods
if len(a) == len(b) {
// Interface types are the only types where cycles can occur
// that are not "terminated" via named types; and such cycles
@@ -335,7 +344,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
}
for i, f := range a {
g := b[i]
- if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
+ if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
return false
}
}
@@ -346,20 +355,41 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
- return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
+ return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
- return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
+ return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
}
case *Named:
// Two named types are identical if their type names originate
// in the same type declaration.
if y, ok := y.(*Named); ok {
+ xargs := x.TypeArgs().list()
+ yargs := y.TypeArgs().list()
+
+ if len(xargs) != len(yargs) {
+ return false
+ }
+
+ if len(xargs) > 0 {
+ // Instances are identical if their original type and type arguments
+ // are identical.
+ if !Identical(x.orig, y.orig) {
+ return false
+ }
+ for i, xa := range xargs {
+ if !Identical(xa, yargs[i]) {
+ return false
+ }
+ }
+ return true
+ }
+
// TODO(gri) Why is x == y not sufficient? And if it is,
// we can just return false here because x == y
// is caught in the very beginning of this function.
@@ -369,14 +399,6 @@ func (check *Checker) identical0(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 *instance:
- // unreachable since types are expanded
-
- case *bottom, *top:
- // Either both types are theBottom, or both 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
@@ -387,25 +409,28 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
return false
}
-func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
- if len(x) != len(y) {
+// identicalInstance reports if two type instantiations are identical.
+// Instantiations are identical if their origin and type arguments are
+// identical.
+func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool {
+ if len(xargs) != len(yargs) {
return false
}
- for i, x := range x {
- y := y[i]
- if !check.identical0(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) {
+
+ for i, xa := range xargs {
+ if !Identical(xa, yargs[i]) {
return false
}
}
- return true
+
+ return Identical(xorig, yorig)
}
// 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]
@@ -421,5 +446,5 @@ func Default(typ Type) Type {
return Typ[String]
}
}
- return typ
+ return t
}
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
index fa30650bd444f3d995b1939e897783e6ef50b768..a8cb244c55e6095c31bbb68d5dee0d427e35a789 100644
--- a/src/cmd/compile/internal/types2/resolver.go
+++ b/src/cmd/compile/internal/types2/resolver.go
@@ -196,6 +196,7 @@ func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package {
// methods with receiver base type names.
func (check *Checker) collectObjects() {
pkg := check.pkg
+ pkg.height = 0
// pkgImports is the set of packages already imported by any package file seen
// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
@@ -253,6 +254,15 @@ func (check *Checker) collectObjects() {
continue
}
+ if imp == Unsafe {
+ // typecheck ignores imports of package unsafe for
+ // calculating height.
+ // TODO(mdempsky): Revisit this. This seems fine, but I
+ // don't remember explicitly considering this case.
+ } else if h := imp.height + 1; h > pkg.height {
+ pkg.height = h
+ }
+
// local name overrides imported package name
name := imp.name
if s.LocalPkgName != nil {
@@ -265,7 +275,7 @@ func (check *Checker) collectObjects() {
}
if name == "init" {
- check.error(s.LocalPkgName, "cannot import package as init - init must be a func")
+ check.error(s, "cannot import package as init - init must be a func")
continue
}
@@ -298,22 +308,26 @@ func (check *Checker) collectObjects() {
check.dotImportMap = make(map[dotImportKey]*PkgName)
}
// merge imported scope with file scope
- for _, obj := range imp.scope.elems {
+ for name, obj := range imp.scope.elems {
+ // Note: Avoid eager resolve(name, obj) here, so we only
+ // resolve dot-imported objects as needed.
+
// A package scope may contain non-exported objects,
// do not import them!
- if obj.Exported() {
+ if isExported(name) {
// declare dot-imported object
// (Do not use check.declare because it modifies the object
// via Object.setScopePos, which leads to a race condition;
// the object may be imported into more than one file scope
// concurrently. See issue #32154.)
- if alt := fileScope.Insert(obj); alt != nil {
+ if alt := fileScope.Lookup(name); alt != nil {
var err error_
- err.errorf(s.LocalPkgName, "%s redeclared in this block", obj.Name())
+ err.errorf(s.LocalPkgName, "%s redeclared in this block", alt.Name())
err.recordAltDecl(alt)
check.report(&err)
} else {
- check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
+ fileScope.insert(name, obj)
+ check.dotImportMap[dotImportKey{fileScope, name}] = pkgName
}
}
}
@@ -398,52 +412,60 @@ func (check *Checker) collectObjects() {
}
case *syntax.TypeDecl:
+ if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
+ check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
+ }
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
case *syntax.FuncDecl:
- d := s // TODO(gri) get rid of this
- name := d.Name.Value
- obj := NewFunc(d.Name.Pos(), pkg, name, nil)
- if d.Recv == nil {
+ name := s.Name.Value
+ obj := NewFunc(s.Name.Pos(), pkg, name, nil)
+ hasTParamError := false // avoid duplicate type parameter errors
+ if s.Recv == nil {
// regular function
if name == "init" || name == "main" && pkg.name == "main" {
- if d.TParamList != nil {
- check.softErrorf(d, "func %s must have no type parameters", name)
+ if len(s.TParamList) != 0 {
+ check.softErrorf(s.TParamList[0], "func %s must have no type parameters", name)
+ hasTParamError = true
}
- if t := d.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
- check.softErrorf(d, "func %s must have no arguments and no return values", name)
+ if t := s.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
+ check.softErrorf(s, "func %s must have no arguments and no return values", name)
}
}
// don't declare init functions in the package scope - they are invisible
if name == "init" {
obj.parent = pkg.scope
- check.recordDef(d.Name, obj)
+ check.recordDef(s.Name, obj)
// init functions must have a body
- if d.Body == nil {
+ if s.Body == nil {
// TODO(gri) make this error message consistent with the others above
check.softErrorf(obj.pos, "missing function body")
}
} else {
- check.declare(pkg.scope, d.Name, obj, nopos)
+ check.declare(pkg.scope, s.Name, obj, nopos)
}
} else {
// method
// d.Recv != nil
- if !acceptMethodTypeParams && len(d.TParamList) != 0 {
+ if !acceptMethodTypeParams && len(s.TParamList) != 0 {
//check.error(d.TParamList.Pos(), invalidAST + "method must have no type parameters")
- check.error(d, invalidAST+"method must have no type parameters")
+ check.error(s.TParamList[0], invalidAST+"method must have no type parameters")
+ hasTParamError = true
}
- ptr, recv, _ := check.unpackRecv(d.Recv.Type, false)
+ ptr, recv, _ := check.unpackRecv(s.Recv.Type, false)
// (Methods with invalid receiver cannot be associated to a type, and
// methods with blank _ names are never found; no need to collect any
// of them. They will still be type-checked with all the other functions.)
if recv != nil && name != "_" {
methods = append(methods, methodInfo{obj, ptr, recv})
}
- check.recordDef(d.Name, obj)
+ check.recordDef(s.Name, obj)
+ }
+ if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
+ check.softErrorf(s.TParamList[0], "type parameters require go1.18 or later")
}
- info := &declInfo{file: fileScope, fdecl: d}
+ info := &declInfo{file: fileScope, fdecl: s}
// Methods are not package-level objects but we still track them in the
// object map so that we can handle them like regular functions (if the
// receiver is invalid); also we need their fdecl info when associating
@@ -459,8 +481,9 @@ func (check *Checker) collectObjects() {
// verify that objects in package and file scopes have different names
for _, scope := range fileScopes {
- for _, obj := range scope.elems {
- if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ for name, obj := range scope.elems {
+ if alt := pkg.scope.Lookup(name); alt != nil {
+ obj = resolve(name, obj)
var err error_
if pkg, ok := obj.(*PkgName); ok {
err.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())
@@ -486,7 +509,7 @@ func (check *Checker) collectObjects() {
// Determine the receiver base type and associate m with it.
ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
if base != nil {
- m.obj.hasPtrRecv = ptr
+ m.obj.hasPtrRecv_ = ptr
check.methods[base] = append(check.methods[base], m.obj)
}
}
diff --git a/src/cmd/compile/internal/types2/resolver_test.go b/src/cmd/compile/internal/types2/resolver_test.go
index aee435ff5fb6cd7e0aa6d0d34028290197e71968..a02abce081b8334e3bb57bb0444ff6bced209e79 100644
--- a/src/cmd/compile/internal/types2/resolver_test.go
+++ b/src/cmd/compile/internal/types2/resolver_test.go
@@ -143,7 +143,7 @@ func TestResolveIdents(t *testing.T) {
// check that qualified identifiers are resolved
for _, f := range files {
- syntax.Walk(f, func(n syntax.Node) bool {
+ syntax.Crawl(f, func(n syntax.Node) bool {
if s, ok := n.(*syntax.SelectorExpr); ok {
if x, ok := s.X.(*syntax.Name); ok {
obj := uses[x]
@@ -177,7 +177,7 @@ func TestResolveIdents(t *testing.T) {
foundDefs := make(map[*syntax.Name]bool)
var both []string
for _, f := range files {
- syntax.Walk(f, func(n syntax.Node) bool {
+ syntax.Crawl(f, func(n syntax.Node) bool {
if x, ok := n.(*syntax.Name); ok {
var objects int
if _, found := uses[x]; found {
diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go
index 204e456a916fb127e02b3fa9aee0f6c41cd5971b..6c3e1842ce7d715b93d350ff58cc1e3738e4cf0d 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/sanitize.go b/src/cmd/compile/internal/types2/sanitize.go
deleted file mode 100644
index 64a2dedc7d83d9972af155599757891b41e8a549..0000000000000000000000000000000000000000
--- a/src/cmd/compile/internal/types2/sanitize.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// 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 types2
-
-// sanitizeInfo walks the types contained in info to ensure that all instances
-// are expanded.
-//
-// This includes some objects that may be shared across concurrent
-// type-checking passes (such as those in the universe scope), so we are
-// careful here not to write types that are already sanitized. This avoids a
-// data race as any shared types should already be sanitized.
-func sanitizeInfo(info *Info) {
- var s sanitizer = make(map[Type]Type)
-
- // Note: Some map entries are not references.
- // If modified, they must be assigned back.
-
- for e, tv := range info.Types {
- if typ := s.typ(tv.Type); typ != tv.Type {
- tv.Type = typ
- info.Types[e] = tv
- }
- }
-
- for e, inf := range info.Inferred {
- changed := false
- for i, targ := range inf.Targs {
- if typ := s.typ(targ); typ != targ {
- inf.Targs[i] = typ
- changed = true
- }
- }
- if typ := s.typ(inf.Sig); typ != inf.Sig {
- inf.Sig = typ.(*Signature)
- changed = true
- }
- if changed {
- info.Inferred[e] = inf
- }
- }
-
- for _, obj := range info.Defs {
- if obj != nil {
- if typ := s.typ(obj.Type()); typ != obj.Type() {
- obj.setType(typ)
- }
- }
- }
-
- for _, obj := range info.Uses {
- if obj != nil {
- if typ := s.typ(obj.Type()); typ != obj.Type() {
- obj.setType(typ)
- }
- }
- }
-
- // TODO(gri) sanitize as needed
- // - info.Implicits
- // - info.Selections
- // - info.Scopes
- // - info.InitOrder
-}
-
-type sanitizer map[Type]Type
-
-func (s sanitizer) typ(typ Type) Type {
- if typ == nil {
- return nil
- }
-
- if t, found := s[typ]; found {
- return t
- }
- s[typ] = typ
-
- switch t := typ.(type) {
- case *Basic, *bottom, *top:
- // nothing to do
-
- case *Array:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Slice:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Struct:
- s.varList(t.fields)
-
- case *Pointer:
- if base := s.typ(t.base); base != t.base {
- t.base = base
- }
-
- case *Tuple:
- s.tuple(t)
-
- case *Signature:
- s.var_(t.recv)
- s.tuple(t.params)
- s.tuple(t.results)
-
- case *Sum:
- s.typeList(t.types)
-
- case *Interface:
- s.funcList(t.methods)
- if types := s.typ(t.types); types != t.types {
- t.types = types
- }
- s.typeList(t.embeddeds)
- s.funcList(t.allMethods)
- if allTypes := s.typ(t.allTypes); allTypes != t.allTypes {
- t.allTypes = allTypes
- }
-
- case *Map:
- if key := s.typ(t.key); key != t.key {
- t.key = key
- }
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Chan:
- if elem := s.typ(t.elem); elem != t.elem {
- t.elem = elem
- }
-
- case *Named:
- if orig := s.typ(t.fromRHS); orig != t.fromRHS {
- t.fromRHS = orig
- }
- if under := s.typ(t.underlying); under != t.underlying {
- t.underlying = under
- }
- s.typeList(t.targs)
- s.funcList(t.methods)
-
- case *TypeParam:
- if bound := s.typ(t.bound); bound != t.bound {
- t.bound = bound
- }
-
- case *instance:
- typ = t.expand()
- s[t] = typ
-
- default:
- panic("unimplemented")
- }
-
- return typ
-}
-
-func (s sanitizer) var_(v *Var) {
- if v != nil {
- if typ := s.typ(v.typ); typ != v.typ {
- v.typ = typ
- }
- }
-}
-
-func (s sanitizer) varList(list []*Var) {
- for _, v := range list {
- s.var_(v)
- }
-}
-
-func (s sanitizer) tuple(t *Tuple) {
- if t != nil {
- s.varList(t.vars)
- }
-}
-
-func (s sanitizer) func_(f *Func) {
- if f != nil {
- if typ := s.typ(f.typ); typ != f.typ {
- f.typ = typ
- }
- }
-}
-
-func (s sanitizer) funcList(list []*Func) {
- for _, f := range list {
- s.func_(f)
- }
-}
-
-func (s sanitizer) typeList(list []Type) {
- for i, t := range list {
- if typ := s.typ(t); typ != t {
- list[i] = typ
- }
- }
-}
diff --git a/src/cmd/compile/internal/types2/scope.go b/src/cmd/compile/internal/types2/scope.go
index ade0a79b31d11daf4fab28c7094d15b8175f22b7..095875d94b0d9ecec9d318d6456a0420abbd7442 100644
--- a/src/cmd/compile/internal/types2/scope.go
+++ b/src/cmd/compile/internal/types2/scope.go
@@ -13,6 +13,7 @@ import (
"io"
"sort"
"strings"
+ "sync"
)
// A Scope maintains a set of objects and links to its containing
@@ -22,6 +23,7 @@ import (
type Scope struct {
parent *Scope
children []*Scope
+ number int // parent.children[number-1] is this scope; 0 if there is no parent
elems map[string]Object // lazily allocated
pos, end syntax.Pos // scope extent; may be invalid
comment string // for debugging only
@@ -31,10 +33,11 @@ type Scope struct {
// NewScope returns a new, empty scope contained in the given parent
// scope, if any. The comment is for debugging only.
func NewScope(parent *Scope, pos, end syntax.Pos, comment string) *Scope {
- s := &Scope{parent, nil, nil, pos, end, comment, false}
+ s := &Scope{parent, nil, 0, nil, pos, end, comment, false}
// don't add children to Universe scope!
if parent != nil && parent != Universe {
parent.children = append(parent.children, s)
+ s.number = len(parent.children)
}
return s
}
@@ -66,7 +69,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
// Lookup returns the object in scope s with the given name if such an
// object exists; otherwise the result is nil.
func (s *Scope) Lookup(name string) Object {
- return s.elems[name]
+ return resolve(name, s.elems[name])
}
// LookupParent follows the parent chain of scopes starting with s until
@@ -81,7 +84,7 @@ func (s *Scope) Lookup(name string) Object {
// whose scope is the scope of the package that exported them.
func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) {
for ; s != nil; s = s.parent {
- if obj := s.elems[name]; obj != nil && (!pos.IsKnown() || obj.scopePos().Cmp(pos) <= 0) {
+ if obj := s.Lookup(name); obj != nil && (!pos.IsKnown() || obj.scopePos().Cmp(pos) <= 0) {
return s, obj
}
}
@@ -95,19 +98,38 @@ func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) {
// if not already set, and returns nil.
func (s *Scope) Insert(obj Object) Object {
name := obj.Name()
- if alt := s.elems[name]; alt != nil {
+ if alt := s.Lookup(name); alt != nil {
return alt
}
- if s.elems == nil {
- s.elems = make(map[string]Object)
- }
- s.elems[name] = obj
+ s.insert(name, obj)
if obj.Parent() == nil {
obj.setParent(s)
}
return nil
}
+// InsertLazy is like Insert, but allows deferring construction of the
+// inserted object until it's accessed with Lookup. The Object
+// returned by resolve must have the same name as given to InsertLazy.
+// If s already contains an alternative object with the same name,
+// InsertLazy leaves s unchanged and returns false. Otherwise it
+// records the binding and returns true. The object's parent scope
+// will be set to s after resolve is called.
+func (s *Scope) InsertLazy(name string, resolve func() Object) bool {
+ if s.elems[name] != nil {
+ return false
+ }
+ s.insert(name, &lazyObject{parent: s, resolve: resolve})
+ return true
+}
+
+func (s *Scope) insert(name string, obj Object) {
+ if s.elems == nil {
+ s.elems = make(map[string]Object)
+ }
+ s.elems[name] = obj
+}
+
// Squash merges s with its parent scope p by adding all
// objects of s to p, adding all children of s to the
// children of p, and removing s from p's children.
@@ -117,7 +139,8 @@ func (s *Scope) Insert(obj Object) Object {
func (s *Scope) Squash(err func(obj, alt Object)) {
p := s.parent
assert(p != nil)
- for _, obj := range s.elems {
+ for name, obj := range s.elems {
+ obj = resolve(name, obj)
obj.setParent(nil)
if alt := p.Insert(obj); alt != nil {
err(obj, alt)
@@ -196,7 +219,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
indn1 := indn + ind
for _, name := range s.Names() {
- fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
+ fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name))
}
if recurse {
@@ -214,3 +237,57 @@ func (s *Scope) String() string {
s.WriteTo(&buf, 0, false)
return buf.String()
}
+
+// A lazyObject represents an imported Object that has not been fully
+// resolved yet by its importer.
+type lazyObject struct {
+ parent *Scope
+ resolve func() Object
+ obj Object
+ once sync.Once
+}
+
+// resolve returns the Object represented by obj, resolving lazy
+// objects as appropriate.
+func resolve(name string, obj Object) Object {
+ if lazy, ok := obj.(*lazyObject); ok {
+ lazy.once.Do(func() {
+ obj := lazy.resolve()
+
+ if _, ok := obj.(*lazyObject); ok {
+ panic("recursive lazy object")
+ }
+ if obj.Name() != name {
+ panic("lazy object has unexpected name")
+ }
+
+ if obj.Parent() == nil {
+ obj.setParent(lazy.parent)
+ }
+ lazy.obj = obj
+ })
+
+ obj = lazy.obj
+ }
+ return obj
+}
+
+// stub implementations so *lazyObject implements Object and we can
+// store them directly into Scope.elems.
+func (*lazyObject) Parent() *Scope { panic("unreachable") }
+func (*lazyObject) Pos() syntax.Pos { panic("unreachable") }
+func (*lazyObject) Pkg() *Package { panic("unreachable") }
+func (*lazyObject) Name() string { panic("unreachable") }
+func (*lazyObject) Type() Type { panic("unreachable") }
+func (*lazyObject) Exported() bool { panic("unreachable") }
+func (*lazyObject) Id() string { panic("unreachable") }
+func (*lazyObject) String() string { panic("unreachable") }
+func (*lazyObject) order() uint32 { panic("unreachable") }
+func (*lazyObject) color() color { panic("unreachable") }
+func (*lazyObject) setType(Type) { panic("unreachable") }
+func (*lazyObject) setOrder(uint32) { panic("unreachable") }
+func (*lazyObject) setColor(color color) { panic("unreachable") }
+func (*lazyObject) setParent(*Scope) { panic("unreachable") }
+func (*lazyObject) sameId(pkg *Package, name string) bool { panic("unreachable") }
+func (*lazyObject) scopePos() syntax.Pos { panic("unreachable") }
+func (*lazyObject) setScopePos(pos syntax.Pos) { panic("unreachable") }
diff --git a/src/cmd/compile/internal/types2/self_test.go b/src/cmd/compile/internal/types2/self_test.go
index 4722fec9889f0f53670daef90d75f537962f378e..9a01ccdf7a28bbce7903885bf51e36feaa4ab374 100644
--- a/src/cmd/compile/internal/types2/self_test.go
+++ b/src/cmd/compile/internal/types2/self_test.go
@@ -24,12 +24,7 @@ func TestSelf(t *testing.T) {
conf := Config{Importer: defaultImporter()}
_, err = conf.Check("cmd/compile/internal/types2", files, nil)
if err != nil {
- // Importing go/constant doesn't work in the
- // build dashboard environment. Don't report an error
- // for now so that the build remains green.
- // TODO(gri) fix this
- t.Log(err) // replace w/ t.Fatal eventually
- return
+ t.Fatal(err)
}
}
@@ -38,6 +33,7 @@ func BenchmarkCheck(b *testing.B) {
filepath.Join("src", "net", "http"),
filepath.Join("src", "go", "parser"),
filepath.Join("src", "go", "constant"),
+ filepath.Join("src", "runtime"),
filepath.Join("src", "go", "internal", "gcimporter"),
} {
b.Run(path.Base(p), func(b *testing.B) {
diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go
new file mode 100644
index 0000000000000000000000000000000000000000..06dcd9131a42566763e9784190802b37913150ed
--- /dev/null
+++ b/src/cmd/compile/internal/types2/signature.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 types2
+
+import "cmd/compile/internal/syntax"
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Signature represents a (non-builtin) function or method type.
+// The receiver is ignored when comparing signatures for identity.
+type Signature struct {
+ // We need to keep the scope in Signature (rather than passing it around
+ // and store it in the Func Object) because when type-checking a function
+ // literal we call the general type checker which returns a general Type.
+ // We then unpack the *Signature and use the scope for the literal body.
+ rparams *TypeParamList // receiver type parameters from left to right, or nil
+ tparams *TypeParamList // type parameters from left to right, or nil
+ scope *Scope // function scope for package-local and non-instantiated signatures; nil otherwise
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+}
+
+// NewSignatureType creates a new function type for the given receiver,
+// receiver type parameters, type parameters, parameters, and results. If
+// variadic is set, params must hold at least one parameter and the last
+// parameter must be of unnamed slice type. If recv is non-nil, typeParams must
+// be empty. If recvTypeParams is non-empty, recv must be non-nil.
+func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
+ if variadic {
+ n := params.Len()
+ if n == 0 {
+ panic("variadic function must have at least one parameter")
+ }
+ if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+ panic("variadic parameter must be of unnamed slice type")
+ }
+ }
+ sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
+ if len(recvTypeParams) != 0 {
+ if recv == nil {
+ panic("function with receiver type parameters must have a receiver")
+ }
+ sig.rparams = bindTParams(recvTypeParams)
+ }
+ if len(typeParams) != 0 {
+ if recv != nil {
+ panic("function with type parameters cannot have a receiver")
+ }
+ sig.tparams = bindTParams(typeParams)
+ }
+ return sig
+}
+
+// Recv returns the receiver of signature s (if a method), or nil if a
+// function. It is ignored when comparing signatures for identity.
+//
+// For an abstract method, Recv returns the enclosing interface either
+// as a *Named or an *Interface. Due to embedding, an interface may
+// contain methods whose receiver type is a different interface.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// TypeParams returns the type parameters of signature s, or nil.
+func (s *Signature) TypeParams() *TypeParamList { return s.tparams }
+
+// SetTypeParams sets the type parameters of signature s.
+func (s *Signature) SetTypeParams(tparams []*TypeParam) { s.tparams = bindTParams(tparams) }
+
+// RecvTypeParams returns the receiver type parameters of signature s, or nil.
+func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
+
+// SetRecvTypeParams sets the receiver type params of signature s.
+func (s *Signature) SetRecvTypeParams(rparams []*TypeParam) { s.rparams = bindTParams(rparams) }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Variadic reports whether the signature s is variadic.
+func (s *Signature) Variadic() bool { return s.variadic }
+
+func (s *Signature) Underlying() Type { return s }
+func (s *Signature) String() string { return TypeString(s, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// Disabled by default, but enabled when running tests (via types_test.go).
+var acceptMethodTypeParams bool
+
+// funcType type-checks a function or method type.
+func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []*syntax.Field, ftyp *syntax.FuncType) {
+ check.openScope(ftyp, "function")
+ check.scope.isFunc = true
+ check.recordScope(ftyp, check.scope)
+ sig.scope = check.scope
+ defer check.closeScope()
+
+ if recvPar != nil {
+ // collect generic receiver type parameters, if any
+ // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
+ // - the receiver specification acts as local declaration for its type parameters, which may be blank
+ _, rname, rparams := check.unpackRecv(recvPar.Type, true)
+ if len(rparams) > 0 {
+ tparams := make([]*TypeParam, len(rparams))
+ for i, rparam := range rparams {
+ tparams[i] = check.declareTypeParam(rparam)
+ }
+ sig.rparams = bindTParams(tparams)
+ // Blank identifiers don't get declared, so naive type-checking of the
+ // receiver type expression would fail in Checker.collectParams below,
+ // when Checker.ident cannot resolve the _ to a type.
+ //
+ // Checker.recvTParamMap maps these blank identifiers to their type parameter
+ // types, so that they may be resolved in Checker.ident when they fail
+ // lookup in the scope.
+ for i, p := range rparams {
+ if p.Value == "_" {
+ tpar := sig.rparams.At(i)
+ if check.recvTParamMap == nil {
+ check.recvTParamMap = make(map[*syntax.Name]*TypeParam)
+ }
+ check.recvTParamMap[p] = tpar
+ }
+ }
+ // determine receiver type to get its type parameters
+ // and the respective type parameter bounds
+ var recvTParams []*TypeParam
+ if rname != nil {
+ // recv should be a Named type (otherwise an error is reported elsewhere)
+ // Also: Don't report an error via genericType since it will be reported
+ // again when we type-check the signature.
+ // TODO(gri) maybe the receiver should be marked as invalid instead?
+ if recv, _ := check.genericType(rname, false).(*Named); recv != nil {
+ recvTParams = recv.TypeParams().list()
+ }
+ }
+ // provide type parameter bounds
+ // - only do this if we have the right number (otherwise an error is reported elsewhere)
+ if sig.RecvTypeParams().Len() == len(recvTParams) {
+ // We have a list of *TypeNames but we need a list of Types.
+ 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() {
+ bound := recvTParams[i].bound
+ // bound is (possibly) parameterized in the context of the
+ // receiver type declaration. Substitute parameters for the
+ // current context.
+ tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
+ }
+ }
+ }
+ }
+
+ if tparams != nil {
+ check.collectTypeParams(&sig.tparams, tparams)
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (A separate check is needed when type-checking interface method signatures because
+ // they don't have a receiver specification.)
+ if recvPar != nil && !acceptMethodTypeParams {
+ check.error(ftyp, "methods cannot have type parameters")
+ }
+ }
+
+ // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
+ // declarations and then squash that scope into the parent scope (and report any redeclarations at
+ // that time).
+ scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
+ var recvList []*Var // TODO(gri) remove the need for making a list here
+ if recvPar != nil {
+ recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, false) // use rewritten receiver type, if any
+ }
+ params, variadic := check.collectParams(scope, ftyp.ParamList, true)
+ results, _ := check.collectParams(scope, ftyp.ResultList, false)
+ scope.Squash(func(obj, alt Object) {
+ var err error_
+ err.errorf(obj, "%s redeclared in this block", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
+ })
+
+ if recvPar != nil {
+ // recv parameter list present (may be empty)
+ // spec: "The receiver is specified via an extra parameter section preceding the
+ // method name. That parameter section must declare a single parameter, the receiver."
+ var recv *Var
+ switch len(recvList) {
+ case 0:
+ // error reported by resolver
+ recv = NewParam(nopos, nil, "", Typ[Invalid]) // ignore recv below
+ default:
+ // more than one receiver
+ check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
+ fallthrough // continue with first receiver
+ case 1:
+ recv = recvList[0]
+ }
+
+ // TODO(gri) We should delay rtyp expansion to when we actually need the
+ // receiver; thus all checks here should be delayed to later.
+ rtyp, _ := deref(recv.typ)
+
+ // spec: "The receiver type must be of the form T or *T where T is a type name."
+ // (ignore invalid types - error was reported before)
+ if rtyp != Typ[Invalid] {
+ var err string
+ switch T := rtyp.(type) {
+ case *Named:
+ T.resolve(check.bestContext(nil))
+ // The receiver type may be an instantiated type referred to
+ // by an alias (which cannot have receiver parameters for now).
+ if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
+ check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ)
+ break
+ }
+ // spec: "The type denoted by T is called the receiver base type; it must not
+ // be a pointer or interface type and it must be declared in the same package
+ // as the method."
+ if T.obj.pkg != check.pkg {
+ err = "type not defined in this package"
+ if check.conf.CompilerErrorMessages {
+ check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
+ err = ""
+ }
+ } else {
+ // The underlying type of a receiver base type can be a type parameter;
+ // e.g. for methods with a generic receiver T[P] with type T[P any] P.
+ underIs(T, func(u Type) bool {
+ switch u := u.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ err = "unsafe.Pointer"
+ return false
+ }
+ case *Pointer, *Interface:
+ err = "pointer or interface type"
+ return false
+ }
+ return true
+ })
+ }
+ case *Basic:
+ err = "basic or unnamed type"
+ if check.conf.CompilerErrorMessages {
+ check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
+ err = ""
+ }
+ default:
+ check.errorf(recv.pos, "invalid receiver type %s", recv.typ)
+ }
+ if err != "" {
+ check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err)
+ // ok to continue
+ }
+ }
+ sig.recv = recv
+ }
+
+ sig.params = NewTuple(params...)
+ sig.results = NewTuple(results...)
+ sig.variadic = variadic
+}
+
+// collectParams declares the parameters of list in scope and returns the corresponding
+// variable list.
+func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, variadicOk bool) (params []*Var, variadic bool) {
+ if list == nil {
+ return
+ }
+
+ var named, anonymous bool
+
+ var typ Type
+ var prev syntax.Expr
+ for i, field := range list {
+ ftype := field.Type
+ // type-check type of grouped fields only once
+ if ftype != prev {
+ prev = ftype
+ if t, _ := ftype.(*syntax.DotsType); t != nil {
+ ftype = t.Elem
+ if variadicOk && i == len(list)-1 {
+ variadic = true
+ } else {
+ check.softErrorf(t, "can only use ... with final parameter in list")
+ // ignore ... and continue
+ }
+ }
+ typ = check.varType(ftype)
+ }
+ // The parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag.
+ if field.Name != nil {
+ // named parameter
+ name := field.Name.Value
+ if name == "" {
+ check.error(field.Name, invalidAST+"anonymous parameter")
+ // ok to continue
+ }
+ par := NewParam(field.Name.Pos(), check.pkg, name, typ)
+ check.declare(scope, field.Name, par, scope.pos)
+ params = append(params, par)
+ named = true
+ } else {
+ // anonymous parameter
+ par := NewParam(field.Pos(), check.pkg, "", typ)
+ check.recordImplicit(field, par)
+ params = append(params, par)
+ anonymous = true
+ }
+ }
+
+ if named && anonymous {
+ check.error(list[0], invalidAST+"list contains both named and anonymous parameters")
+ // ok to continue
+ }
+
+ // For a variadic function, change the last parameter's type from T to []T.
+ // Since we type-checked T rather than ...T, we also need to retro-actively
+ // record the type for ...T.
+ if variadic {
+ last := params[len(params)-1]
+ last.typ = &Slice{elem: last.typ}
+ check.recordTypeAndValue(list[len(list)-1].Type, typexpr, last.typ, nil)
+ }
+
+ return
+}
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index 236feb0404ebbd84d894690d7227a352a29ba896..99b846b80b91c1f355777331ca012ce54ee7f405 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -26,30 +26,29 @@ func TestSizeof(t *testing.T) {
{Struct{}, 24, 48},
{Pointer{}, 8, 16},
{Tuple{}, 12, 24},
- {Signature{}, 44, 88},
- {Sum{}, 12, 24},
- {Interface{}, 60, 120},
+ {Signature{}, 28, 56},
+ {Union{}, 16, 32},
+ {Interface{}, 44, 88},
{Map{}, 16, 32},
{Chan{}, 12, 24},
- {Named{}, 68, 136},
+ {Named{}, 68, 128},
{TypeParam{}, 28, 48},
- {instance{}, 52, 96},
- {bottom{}, 0, 0},
- {top{}, 0, 0},
+ {term{}, 12, 24},
// Objects
{PkgName{}, 64, 104},
{Const{}, 64, 104},
{TypeName{}, 56, 88},
{Var{}, 60, 96},
- {Func{}, 60, 96},
+ {Func{}, 64, 104},
{Label{}, 60, 96},
{Builtin{}, 60, 96},
{Nil{}, 56, 88},
// Misc
- {Scope{}, 56, 96},
+ {Scope{}, 60, 104},
{Package{}, 40, 80},
+ {_TypeSet{}, 28, 56},
}
for _, test := range tests {
diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go
index aa0fbf40fced43115bd43e899920214bd11e5b47..6f981964be1afdfdaf9ac2d64918cbdbb4a73bb7 100644
--- a/src/cmd/compile/internal/types2/sizes.go
+++ b/src/cmd/compile/internal/types2/sizes.go
@@ -48,7 +48,7 @@ type StdSizes struct {
func (s *StdSizes) Alignof(T Type) int64 {
// For arrays and structs, alignment is defined in terms
// of alignment of the elements and fields, respectively.
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Array:
// spec: "For a variable x of array type: unsafe.Alignof(x)
// is the same as unsafe.Alignof(x[0]), but at least 1."
@@ -67,12 +67,17 @@ func (s *StdSizes) Alignof(T Type) int64 {
case *Slice, *Interface:
// Multiword data structures are effectively structs
// in which each element has size WordSize.
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Alignof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize
case *Basic:
// Strings are like slices and interfaces.
if t.Info()&IsString != 0 {
return s.WordSize
}
+ case *TypeParam, *Union:
+ unreachable()
}
a := s.Sizeof(T) // may be 0
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
@@ -118,7 +123,7 @@ var basicSizes = [...]byte{
}
func (s *StdSizes) Sizeof(T Type) int64 {
- switch t := optype(T).(type) {
+ switch t := under(T).(type) {
case *Basic:
assert(isTyped(T))
k := t.kind
@@ -148,10 +153,13 @@ func (s *StdSizes) Sizeof(T Type) int64 {
}
offsets := s.Offsetsof(t.fields)
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
- case *Sum:
- panic("Sizeof unimplemented for type sum")
case *Interface:
+ // Type parameters lead to variable sizes/alignments;
+ // StdSizes.Sizeof won't be called for them.
+ assert(!isTypeParam(T))
return s.WordSize * 2
+ case *TypeParam, *Union:
+ unreachable()
}
return s.WordSize // catch-all
}
@@ -241,7 +249,7 @@ func (conf *Config) offsetsof(T *Struct) []int64 {
func (conf *Config) offsetof(typ Type, index []int) int64 {
var o int64
for _, i := range index {
- s := asStruct(typ)
+ s := under(typ).(*Struct)
o += conf.offsetsof(s)[i]
typ = s.fields[i].typ
}
diff --git a/src/cmd/compile/internal/types2/slice.go b/src/cmd/compile/internal/types2/slice.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c22a6fb1b6853f06a03fa0c0f7967aeed1b1aeb
--- /dev/null
+++ b/src/cmd/compile/internal/types2/slice.go
@@ -0,0 +1,19 @@
+// Copyright 2011 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
+
+// A Slice represents a slice type.
+type Slice struct {
+ elem Type
+}
+
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elem }
+
+func (s *Slice) Underlying() Type { return s }
+func (s *Slice) String() string { return TypeString(s, nil) }
diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go
index cde35c17b6f717f97dd3781f914ee0fcfe0afc99..551611da552454ecf10757d9618d7da389976980 100644
--- a/src/cmd/compile/internal/types2/stdlib_test.go
+++ b/src/cmd/compile/internal/types2/stdlib_test.go
@@ -165,9 +165,11 @@ func TestStdTest(t *testing.T) {
testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
"cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
"directive.go", // tests compiler rejection of bad directive placement - ignore
+ "directive2.go", // tests compiler rejection of bad directive placement - ignore
"embedfunc.go", // tests //go:embed
"embedvers.go", // tests //go:embed
"linkname2.go", // types2 doesn't check validity of //go:xxx directives
+ "linkname3.go", // types2 doesn't check validity of //go:xxx directives
)
}
@@ -192,6 +194,10 @@ func TestStdFixed(t *testing.T) {
"issue20780.go", // types2 does not have constraints on stack size
"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
+ "issue49767.go", // go/types does not have constraints on channel element size
+ "issue49814.go", // go/types does not have constraints on array size
)
}
@@ -215,7 +221,7 @@ func typecheck(t *testing.T, path string, filenames []string) {
var files []*syntax.File
for _, filename := range filenames {
errh := func(err error) { t.Error(err) }
- file, err := syntax.ParseFile(filename, errh, nil, 0)
+ file, err := syntax.ParseFile(filename, errh, nil, syntax.AllowGenerics)
if err != nil {
return
}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index c3e646c80c142d91434991bc5f37d57ab20d4225..ab64882c0273a2c197f4c38fa46ab1810f8d244e 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -14,7 +14,7 @@ import (
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *syntax.BlockStmt, iota constant.Value) {
if check.conf.IgnoreFuncBodies {
- panic("internal error: function body not ignored")
+ panic("function body not ignored")
}
if check.conf.Trace {
@@ -28,13 +28,13 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
sig.scope.pos = body.Pos()
sig.scope.end = syntax.EndPos(body)
- // save/restore current context and setup function context
+ // save/restore current environment and set up function environment
// (and use 0 indentation at function start)
- defer func(ctxt context, indent int) {
- check.context = ctxt
+ defer func(env environment, indent int) {
+ check.environment = env
check.indent = indent
- }(check.context, check.indent)
- check.context = context{
+ }(check.environment, check.indent)
+ check.environment = environment{
decl: decl,
scope: sig.scope,
iota: iota,
@@ -52,11 +52,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
check.error(body.Rbrace, "missing return")
}
- // TODO(gri) Should we make it an error to declare generic functions
- // where the type parameters are not used?
- // 12/19/2018: Probably not - it can make sense to have an API with
- // all functions uniformly sharing the same type parameters.
-
// spec: "Implementation restriction: A compiler may make it illegal to
// declare a variable inside a function body if the variable is never used."
check.usage(sig.scope)
@@ -64,7 +59,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
func (check *Checker) usage(scope *Scope) {
var unused []*Var
- for _, elem := range scope.elems {
+ for name, elem := range scope.elems {
+ elem = resolve(name, elem)
if v, _ := elem.(*Var); v != nil && !v.used {
unused = append(unused, v)
}
@@ -174,7 +170,7 @@ func (check *Checker) closeScope() {
func (check *Checker) suspendedCall(keyword string, call *syntax.CallExpr) {
var x operand
var msg string
- switch check.rawExpr(&x, call, nil) {
+ switch check.rawExpr(&x, call, nil, false) {
case conversion:
msg = "requires function call, not conversion"
case expression:
@@ -255,7 +251,7 @@ L:
// look for duplicate types for a given value
// (quadratic algorithm, but these lists tend to be very short)
for _, vt := range seen[val] {
- if check.identical(v.typ, vt.typ) {
+ if Identical(v.typ, vt.typ) {
var err error_
err.errorf(&v, "duplicate case %s in expression switch", &v)
err.errorf(vt.pos, "previous case")
@@ -268,24 +264,38 @@ L:
}
}
+// isNil reports whether the expression e denotes the predeclared value nil.
+func (check *Checker) isNil(e syntax.Expr) bool {
+ // The only way to express the nil value is by literally writing nil (possibly in parentheses).
+ if name, _ := unparen(e).(*syntax.Name); name != nil {
+ _, ok := check.lookup(name.Value).(*Nil)
+ return ok
+ }
+ return false
+}
+
func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[Type]syntax.Expr) (T Type) {
+ var dummy operand
L:
for _, e := range types {
- T = check.typOrNil(e)
- if T == Typ[Invalid] {
- continue L
- }
- if T != nil {
- check.ordinaryType(e.Pos(), T)
+ // The spec allows the value nil instead of a type.
+ if check.isNil(e) {
+ T = nil
+ check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+ } else {
+ T = check.varType(e)
+ if T == Typ[Invalid] {
+ continue L
+ }
}
// look for duplicate types
// (quadratic algorithm, but type switches tend to be reasonably small)
for t, other := range seen {
- if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
// talk about "case" rather than "type" because of nil case
Ts := "nil"
if T != nil {
- Ts = T.String()
+ Ts = TypeString(T, check.qualifier)
}
var err error_
err.errorf(e, "duplicate case %s in type switch", Ts)
@@ -296,12 +306,53 @@ L:
}
seen[T] = e
if T != nil {
- check.typeAssertion(e.Pos(), x, xtyp, T)
+ check.typeAssertion(e, x, xtyp, T, true)
}
}
return
}
+// TODO(gri) Once we are certain that typeHash is correct in all situations, use this version of caseTypes instead.
+// (Currently it may be possible that different types have identical names and import paths due to ImporterFrom.)
+//
+// func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[string]syntax.Expr) (T Type) {
+// var dummy operand
+// L:
+// for _, e := range types {
+// // The spec allows the value nil instead of a type.
+// var hash string
+// if check.isNil(e) {
+// check.expr(&dummy, e) // run e through expr so we get the usual Info recordings
+// T = nil
+// hash = "" // avoid collision with a type named nil
+// } else {
+// T = check.varType(e)
+// if T == Typ[Invalid] {
+// continue L
+// }
+// hash = typeHash(T, nil)
+// }
+// // look for duplicate types
+// if other := seen[hash]; other != nil {
+// // talk about "case" rather than "type" because of nil case
+// Ts := "nil"
+// if T != nil {
+// Ts = TypeString(T, check.qualifier)
+// }
+// var err error_
+// err.errorf(e, "duplicate case %s in type switch", Ts)
+// err.errorf(other, "previous case")
+// check.report(&err)
+// continue L
+// }
+// seen[hash] = e
+// if T != nil {
+// check.typeAssertion(e, x, xtyp, T, true)
+// }
+// }
+// return
+// }
+
// stmt typechecks statement s.
func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
// statements must end with the same top scope as they started with
@@ -335,7 +386,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
// function and method calls and receive operations can appear
// in statement context. Such statements may be parenthesized."
var x operand
- kind := check.rawExpr(&x, s.X, nil)
+ kind := check.rawExpr(&x, s.X, nil, false)
var msg string
switch x.mode {
default:
@@ -351,25 +402,27 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
check.errorf(&x, "%s %s", &x, msg)
case *syntax.SendStmt:
- var ch, x operand
+ var ch, val operand
check.expr(&ch, s.Chan)
- check.expr(&x, s.Value)
- if ch.mode == invalid || x.mode == invalid {
+ check.expr(&val, s.Value)
+ if ch.mode == invalid || val.mode == invalid {
return
}
-
- tch := asChan(ch.typ)
- if tch == nil {
- check.errorf(s, invalidOp+"cannot send to non-chan type %s", ch.typ)
+ u := structuralType(ch.typ)
+ if u == nil {
+ check.errorf(s, invalidOp+"cannot send to %s: no structural type", &ch)
return
}
-
- if tch.dir == RecvOnly {
- check.errorf(s, invalidOp+"cannot send to receive-only type %s", tch)
+ uch, _ := u.(*Chan)
+ if uch == nil {
+ check.errorf(s, invalidOp+"cannot send to non-channel %s", &ch)
return
}
-
- check.assignment(&x, tch.elem, "send")
+ if uch.dir == RecvOnly {
+ check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch)
+ return
+ }
+ check.assignment(&val, uch.elem, "send")
case *syntax.AssignStmt:
lhs := unpackExpr(s.Lhs)
@@ -384,7 +437,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
}
@@ -413,7 +466,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
check.assignVar(lhs[0], &x)
case *syntax.CallStmt:
- // TODO(gri) get rid of this conversion to string
kind := "go"
if s.Tok == syntax.Defer {
kind = "defer"
@@ -441,7 +493,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
}
} else {
// return has results or result parameters are unnamed
- check.initVars(res.vars, results, s.Pos())
+ check.initVars(res.vars, results, s)
}
} else if len(results) > 0 {
check.error(results[0], "no result values expected")
@@ -498,7 +550,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)
@@ -587,7 +639,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")
}
}
@@ -596,9 +648,6 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
// declaration, but the post statement must not."
if s, _ := s.Post.(*syntax.AssignStmt); s != nil && s.Op == syntax.Def {
// The parser already reported an error.
- // Don't call useLHS here because we want to use the lhs in
- // this erroneous statement so that we don't get errors about
- // these lhs variables being declared but not used.
check.use(s.Lhs) // avoid follow-up errors
}
check.stmt(inner, s.Body)
@@ -684,16 +733,16 @@ func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, gu
if x.mode == invalid {
return
}
- // Caution: We're not using asInterface here because we don't want
- // to switch on a suitably constrained type parameter (for
- // now).
- // TODO(gri) Need to revisit this.
+ // TODO(gri) we may want to permit type switches on type parameter values at some point
+ if isTypeParam(x.typ) {
+ check.errorf(&x, "cannot use type switch on type parameter value %s", &x)
+ return
+ }
xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
- check.errorf(&x, "%s is not an interface type", &x)
+ check.errorf(&x, "%s is not an interface", &x)
return
}
- check.ordinaryType(x.Pos(), xtyp)
check.multipleSwitchDefaults(s.Body)
@@ -780,19 +829,28 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
// determine key/value types
var key, val Type
if x.mode != invalid {
- typ := optype(x.typ)
- if _, ok := typ.(*Chan); ok && sValue != nil {
- // TODO(gri) this also needs to happen for channels in generic variables
- check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
- // ok to continue
+ // Ranging over a type parameter is permitted if it has a structural type.
+ var cause string
+ u := structuralType(x.typ)
+ switch t := u.(type) {
+ case nil:
+ cause = check.sprintf("%s has no structural type", x.typ)
+ 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"
+ }
}
- var msg string
- key, val, msg = rangeKeyVal(typ, isVarName(sKey), isVarName(sValue))
- if key == nil || msg != "" {
- if msg != "" {
- msg = ": " + msg
+ 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
}
}
@@ -873,71 +931,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) {
- switch typ := typ.(type) {
+// 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, ""
- case *Pointer:
- if typ := asArray(typ.base); typ != nil {
- 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 *Sum:
- first := true
- var key, val Type
- var msg string
- typ.is(func(t Type) bool {
- k, v, m := rangeKeyVal(under(t), wantKey, wantVal)
- if k == nil || m != "" {
- key, val, msg = k, v, m
- return false
- }
- if first {
- key, val, msg = k, v, m
- first = false
- return true
- }
- if wantKey && !Identical(key, k) {
- key, val, msg = nil, nil, "all possible values must have the same key type"
- return false
- }
- if wantVal && !Identical(val, v) {
- key, val, msg = nil, nil, "all possible values must have the same element type"
- return false
- }
- return true
- })
- return key, val, msg
- }
- return nil, nil, ""
+ return typ.elem, Typ[Invalid]
+ }
+ return
}
diff --git a/src/cmd/compile/internal/types2/struct.go b/src/cmd/compile/internal/types2/struct.go
new file mode 100644
index 0000000000000000000000000000000000000000..31a3b1af5bc338bbbf015cd53fefe17bea8d47e6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/struct.go
@@ -0,0 +1,225 @@
+// 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"
+ "strconv"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Struct represents a struct type.
+type Struct struct {
+ fields []*Var // fields != nil indicates the struct is set up (possibly with len(fields) == 0)
+ tags []string // field tags; nil if there are no tags
+}
+
+// NewStruct returns a new struct with the given fields and corresponding field tags.
+// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
+// only as long as required to hold the tag with the largest index i. Consequently,
+// if no field has a tag, tags may be nil.
+func NewStruct(fields []*Var, tags []string) *Struct {
+ var fset objset
+ for _, f := range fields {
+ if f.name != "_" && fset.insert(f) != nil {
+ panic("multiple fields with the same name")
+ }
+ }
+ if len(tags) > len(fields) {
+ panic("more tags than fields")
+ }
+ s := &Struct{fields: fields, tags: tags}
+ s.markComplete()
+ return s
+}
+
+// NumFields returns the number of fields in the struct (including blank and embedded fields).
+func (s *Struct) NumFields() int { return len(s.fields) }
+
+// Field returns the i'th field for 0 <= i < NumFields().
+func (s *Struct) Field(i int) *Var { return s.fields[i] }
+
+// Tag returns the i'th field tag for 0 <= i < NumFields().
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+
+func (s *Struct) Underlying() Type { return s }
+func (s *Struct) String() string { return TypeString(s, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (s *Struct) markComplete() {
+ if s.fields == nil {
+ s.fields = make([]*Var, 0)
+ }
+}
+
+func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
+ if e.FieldList == nil {
+ styp.markComplete()
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Value
+ fld := NewField(pos, check.pkg, name, typ, embedded)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ }
+
+ // addInvalid adds an embedded field of invalid type to the struct for
+ // fields with errors; this keeps the number of struct fields in sync
+ // with the source as long as the fields are _ or have different names
+ // (issue #25627).
+ addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
+ typ = Typ[Invalid]
+ tag = ""
+ add(ident, true, pos)
+ }
+
+ var prev syntax.Expr
+ for i, f := range e.FieldList {
+ // Fields declared syntactically with the same type (e.g.: a, b, c T)
+ // share the same type expression. Only check type if it's a new type.
+ if i == 0 || f.Type != prev {
+ typ = check.varType(f.Type)
+ prev = f.Type
+ }
+ tag = ""
+ if i < len(e.TagList) {
+ tag = check.tag(e.TagList[i])
+ }
+ if f.Name != nil {
+ // named field
+ add(f.Name, false, f.Name.Pos())
+ } else {
+ // embedded field
+ // spec: "An embedded type must be specified as a type name T or as a
+ // pointer to a non-interface type name *T, and T itself may not be a
+ // pointer type."
+ pos := syntax.StartPos(f.Type)
+ name := embeddedFieldIdent(f.Type)
+ if name == nil {
+ check.errorf(pos, "invalid embedded field type %s", f.Type)
+ name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
+ addInvalid(name, pos)
+ continue
+ }
+ add(name, true, pos)
+
+ // Because we have a name, typ must be of the form T or *T, where T is the name
+ // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
+ // We must delay this check to the end because we don't want to instantiate
+ // (via under(t)) a possibly incomplete type.
+ embeddedTyp := typ // for closure below
+ embeddedPos := pos
+ check.later(func() {
+ t, isPtr := deref(embeddedTyp)
+ switch u := under(t).(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ return
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
+ }
+ case *Pointer:
+ check.error(embeddedPos, "embedded field type cannot be a pointer")
+ case *Interface:
+ if isTypeParam(t) {
+ check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter")
+ break
+ }
+ if isPtr {
+ check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
+ }
+ }
+ }).describef(embeddedPos, "check embedded type %s", embeddedTyp)
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+ styp.markComplete()
+}
+
+func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
+ switch e := e.(type) {
+ case *syntax.Name:
+ return e
+ case *syntax.Operation:
+ if base := ptrBase(e); base != nil {
+ // *T is valid, but **T is not
+ if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
+ return embeddedFieldIdent(e.X)
+ }
+ }
+ case *syntax.SelectorExpr:
+ return e.Sel
+ case *syntax.IndexExpr:
+ return embeddedFieldIdent(e.X)
+ }
+ return nil // invalid embedded field
+}
+
+func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ var err error_
+ err.errorf(pos, "%s redeclared", obj.Name())
+ err.recordAltDecl(alt)
+ check.report(&err)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) tag(t *syntax.BasicLit) string {
+ // If t.Bad, an error was reported during parsing.
+ if t != nil && !t.Bad {
+ if t.Kind == syntax.StringLit {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
+
+func ptrBase(x *syntax.Operation) syntax.Expr {
+ if x.Op == syntax.Mul && x.Y == nil {
+ return x.X
+ }
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index c8e428c1832311bed33fa8abf7965a01c3dce2a4..516f2481279597af6dd9137b169097b21f7ce603 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -2,215 +2,43 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This file implements instantiation of generic types
-// through substitution of type parameters by actual
-// types.
+// This file implements type parameter substitution.
package types2
-import (
- "bytes"
- "cmd/compile/internal/syntax"
- "fmt"
-)
-
-type substMap struct {
- // The targs field is currently needed for *Named type substitution.
- // TODO(gri) rewrite that code, get rid of this field, and make this
- // struct just the map (proj)
- targs []Type
- proj map[*TypeParam]Type
-}
+import "cmd/compile/internal/syntax"
+
+type substMap map[*TypeParam]Type
// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
// If targs[i] is nil, tpars[i] is not substituted.
-func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
+func makeSubstMap(tpars []*TypeParam, targs []Type) substMap {
assert(len(tpars) == len(targs))
- proj := make(map[*TypeParam]Type, len(tpars))
+ proj := make(substMap, len(tpars))
for i, tpar := range tpars {
- // We must expand type arguments otherwise *instance
- // types end up as components in composite types.
- // TODO(gri) explain why this causes problems, if it does
- targ := expand(targs[i]) // possibly nil
- targs[i] = targ
- proj[tpar.typ.(*TypeParam)] = targ
+ proj[tpar] = targs[i]
}
- return &substMap{targs, proj}
+ return proj
}
-func (m *substMap) String() string {
- return fmt.Sprintf("%s", m.proj)
+func (m substMap) empty() bool {
+ return len(m) == 0
}
-func (m *substMap) empty() bool {
- return len(m.proj) == 0
-}
-
-func (m *substMap) lookup(tpar *TypeParam) Type {
- if t := m.proj[tpar]; t != nil {
+func (m substMap) lookup(tpar *TypeParam) Type {
+ if t := m[tpar]; t != nil {
return t
}
return tpar
}
-func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslist []syntax.Pos) (res Type) {
- if check.conf.Trace {
- check.trace(pos, "-- instantiating %s with %s", typ, typeListString(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 = res.Underlying()
- }
- check.trace(pos, "=> %s (under = %s)", res, under)
- }()
- }
-
- assert(len(poslist) <= len(targs))
-
- // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
- var tparams []*TypeName
- switch t := typ.(type) {
- case *Named:
- tparams = t.tparams
- case *Signature:
- tparams = t.tparams
- defer func() {
- // If we had an unexpected failure somewhere don't panic below when
- // asserting res.(*Signature). Check for *Signature in case Typ[Invalid]
- // is returned.
- if _, ok := res.(*Signature); !ok {
- return
- }
- // If the signature doesn't use its type parameters, subst
- // will not make a copy. In that case, make a copy now (so
- // we can set tparams to nil w/o causing side-effects).
- if t == res {
- copy := *t
- res = ©
- }
- // After instantiating a generic signature, it is not generic
- // anymore; we need to set tparams to nil.
- res.(*Signature).tparams = nil
- }()
-
- default:
- check.dump("%v: cannot instantiate %v", pos, typ)
- unreachable() // only defined types and (defined) functions can be generic
-
- }
-
- // the number of supplied types must match the number of type parameters
- if len(targs) != len(tparams) {
- // TODO(gri) provide better error message
- check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams))
- return Typ[Invalid]
- }
-
- if len(tparams) == 0 {
- return typ // nothing to do (minor optimization)
- }
-
- smap := makeSubstMap(tparams, targs)
-
- // check bounds
- for i, tname := range tparams {
- tpar := tname.typ.(*TypeParam)
- iface := tpar.Bound()
- if iface.Empty() {
- continue // no type bound
- }
-
- targ := targs[i]
-
- // best position for error reporting
- pos := pos
- if i < len(poslist) {
- pos = poslist[i]
- }
-
- // The type parameter bound is parameterized with the same type parameters
- // as the instantiated type; before we can use it for bounds checking we
- // need to instantiate it with the type arguments with which we instantiate
- // the parameterized type.
- iface = check.subst(pos, iface, smap).(*Interface)
-
- // targ must implement iface (methods)
- // - check only if we have methods
- check.completeInterface(nopos, iface)
- if len(iface.allMethods) > 0 {
- // If the type argument is a pointer to a type parameter, the type argument's
- // method set is empty.
- // TODO(gri) is this what we want? (spec question)
- if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
- check.errorf(pos, "%s has no methods", targ)
- break
- }
- if m, wrong := check.missingMethod(targ, iface, true); m != nil {
- // TODO(gri) needs to print updated name to avoid major confusion in error message!
- // (print warning for now)
- // Old warning:
- // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
- if m.name == "==" {
- // We don't want to report "missing method ==".
- check.softErrorf(pos, "%s does not satisfy comparable", targ)
- } else if wrong != nil {
- // TODO(gri) This can still report uninstantiated types which makes the error message
- // more difficult to read then necessary.
- check.softErrorf(pos,
- "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
- targ, tpar.bound, wrong, m,
- )
- } else {
- check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
- }
- break
- }
- }
-
- // targ's underlying type must also be one of the interface types listed, if any
- if iface.allTypes == nil {
- continue // 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 := asTypeParam(targ); targ != nil {
- targBound := targ.Bound()
- if targBound.allTypes == nil {
- check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
- break
- }
- for _, t := range unpack(targBound.allTypes) {
- if !iface.isSatisfiedBy(t) {
- // TODO(gri) match this error message with the one below (or vice versa)
- check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
- break
- }
- }
- break
- }
-
- // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
- if !iface.isSatisfiedBy(targ) {
- check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes)
- break
- }
- }
-
- return check.subst(pos, typ, smap)
-}
-
-// subst returns the type typ with its type parameters tpars replaced by
-// the corresponding type arguments targs, recursively.
-// subst is functional in the sense that it doesn't modify the incoming
-// type. If a substitution took place, the result type is different from
+// subst returns the type typ with its type parameters tpars replaced by the
+// corresponding type arguments targs, recursively. subst doesn't modify the
+// incoming type. If a substitution took place, the result type is different
// from the incoming type.
-func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type {
+//
+// If the given context is non-nil, it is used in lieu of check.Config.Context.
+func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, ctxt *Context) Type {
if smap.empty() {
return typ
}
@@ -224,15 +52,20 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type {
}
// general case
- subst := subster{check, pos, make(map[Type]Type), smap}
+ subst := subster{
+ pos: pos,
+ smap: smap,
+ check: check,
+ ctxt: check.bestContext(ctxt),
+ }
return subst.typ(typ)
}
type subster struct {
- check *Checker
pos syntax.Pos
- cache map[Type]Type
- smap *substMap
+ smap substMap
+ check *Checker // nil if called via Instantiate
+ ctxt *Context
}
func (subst *subster) typ(typ Type) Type {
@@ -241,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, *bottom, *top:
+ case *Basic:
// nothing to do
case *Array:
@@ -258,7 +91,9 @@ func (subst *subster) typ(typ Type) Type {
case *Struct:
if fields, copied := subst.varList(t.fields); copied {
- return &Struct{fields: fields, tags: t.tags}
+ s := &Struct{fields: fields, tags: t.tags}
+ s.markComplete()
+ return s
}
case *Pointer:
@@ -279,10 +114,9 @@ func (subst *subster) typ(typ Type) Type {
if recv != t.recv || params != t.params || results != t.results {
return &Signature{
rparams: t.rparams,
- // TODO(gri) Why can't we nil out tparams here, rather than in
- // instantiate above?
- tparams: t.tparams,
- scope: t.scope,
+ // TODO(gri) why can't we nil out tparams here, rather than in instantiate?
+ tparams: t.tparams,
+ // instantiated signatures have a nil scope
recv: recv,
params: params,
results: results,
@@ -290,29 +124,20 @@ func (subst *subster) typ(typ Type) Type {
}
}
- case *Sum:
- types, copied := subst.typeList(t.types)
+ case *Union:
+ terms, copied := subst.termlist(t.terms)
if copied {
- // Don't do it manually, with a Sum literal: the new
- // types list may not be unique and NewSum may remove
- // duplicates.
- return NewSum(types)
+ // term list substitution may introduce duplicate terms (unlikely but possible).
+ // This is ok; lazy type set computation will determine the actual type set
+ // in normal form.
+ return &Union{terms, nil}
}
case *Interface:
methods, mcopied := subst.funcList(t.methods)
- types := t.types
- if t.types != nil {
- types = subst.typ(t.types)
- }
embeddeds, ecopied := subst.typeList(t.embeddeds)
- if mcopied || types != t.types || ecopied {
- iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
- if subst.check == nil {
- panic("internal error: cannot instantiate interfaces yet")
- }
- subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
- subst.check.completeInterface(nopos, iface)
+ if mcopied || ecopied {
+ iface := &Interface{methods: methods, embeddeds: embeddeds, implicit: t.implicit, complete: t.complete}
return iface
}
@@ -342,77 +167,69 @@ func (subst *subster) typ(typ Type) Type {
}
}
- if t.tparams == nil {
+ // subst is called by expandNamed, so in this function we need to be
+ // careful not to call any methods that would cause t to be expanded: doing
+ // so would result in deadlock.
+ //
+ // So we call t.orig.TypeParams() rather than t.TypeParams() here and
+ // below.
+ if t.orig.TypeParams().Len() == 0 {
dump(">>> %s is not parameterized", t)
return t // type is not parameterized
}
- var new_targs []Type
-
- if len(t.targs) > 0 {
- // already instantiated
- dump(">>> %s already instantiated", t)
- assert(len(t.targs) == len(t.tparams))
- // For each (existing) type argument targ, determine if it needs
- // to be substituted; i.e., if it is or contains a type parameter
- // that has a type argument for it.
- for i, targ := range t.targs {
- dump(">>> %d targ = %s", i, targ)
- new_targ := subst.typ(targ)
- if new_targ != targ {
- dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
- if new_targs == nil {
- new_targs = make([]Type, len(t.tparams))
- copy(new_targs, t.targs)
- }
- new_targs[i] = new_targ
+ var newTArgs []Type
+ if t.targs.Len() != t.orig.TypeParams().Len() {
+ return Typ[Invalid] // error reported elsewhere
+ }
+
+ // already instantiated
+ dump(">>> %s already instantiated", t)
+ // For each (existing) type argument targ, determine if it needs
+ // to be substituted; i.e., if it is or contains a type parameter
+ // that has a type argument for it.
+ for i, targ := range t.targs.list() {
+ dump(">>> %d targ = %s", i, targ)
+ new_targ := subst.typ(targ)
+ if new_targ != targ {
+ dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
+ if newTArgs == nil {
+ newTArgs = make([]Type, t.orig.TypeParams().Len())
+ copy(newTArgs, t.targs.list())
}
+ newTArgs[i] = new_targ
}
+ }
- if new_targs == nil {
- dump(">>> nothing to substitute in %s", t)
- return t // nothing to substitute
- }
- } else {
- // not yet instantiated
- dump(">>> first instantiation of %s", t)
- new_targs = subst.smap.targs
+ if newTArgs == nil {
+ dump(">>> nothing to substitute in %s", t)
+ return t // nothing to substitute
}
// before creating a new named type, check if we have this one already
- h := instantiatedHash(t, new_targs)
+ h := subst.ctxt.instanceHash(t.orig, newTArgs)
dump(">>> new type hash: %s", h)
- if subst.check != nil {
- if named, found := subst.check.typMap[h]; found {
- dump(">>> found %s", named)
- subst.cache[t] = named
- return named
- }
+ if named := subst.ctxt.lookup(h, t.orig, newTArgs); named != nil {
+ dump(">>> found %s", named)
+ return named
}
- // create a new named type and populate caches to avoid endless recursion
- tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
- named := subst.check.newNamed(tname, t, t.underlying, t.tparams, t.methods) // method signatures are updated lazily
- named.targs = new_targs
- if subst.check != nil {
- subst.check.typMap[h] = named
- }
- subst.cache[t] = named
-
- // do the substitution
- dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs)
- named.underlying = subst.typOrNil(t.underlying)
- named.fromRHS = named.underlying // for cycle detection (Checker.validType)
+ // Create a new instance and populate the context to avoid endless
+ // recursion. The position used here is irrelevant because validation only
+ // occurs on t (we don't call validType on named), but we use subst.pos to
+ // help with debugging.
+ t.orig.resolve(subst.ctxt)
+ return subst.check.instance(subst.pos, t.orig, newTArgs, subst.ctxt)
- return named
+ // Note that if we were to expose substitution more generally (not just in
+ // the context of a declaration), we'd have to substitute in
+ // named.underlying as well.
+ //
+ // But this is unnecessary for now.
case *TypeParam:
return subst.smap.lookup(t)
- case *instance:
- // TODO(gri) can we avoid the expansion here and just substitute the type parameters?
- return subst.typ(t.expand())
-
default:
unimplemented()
}
@@ -420,37 +237,6 @@ func (subst *subster) typ(typ Type) Type {
return typ
}
-// TODO(gri) Eventually, this should be more sophisticated.
-// It won't work correctly for locally declared types.
-func instantiatedHash(typ *Named, targs []Type) string {
- var buf bytes.Buffer
- writeTypeName(&buf, typ.obj, nil)
- buf.WriteByte('[')
- writeTypeList(&buf, targs, nil, nil)
- buf.WriteByte(']')
-
- // With respect to the represented type, whether a
- // type is fully expanded or stored as instance
- // does not matter - they are the same types.
- // Remove the instanceMarkers printed for instances.
- res := buf.Bytes()
- i := 0
- for _, b := range res {
- if b != instanceMarker {
- res[i] = b
- i++
- }
- }
-
- return string(res[:i])
-}
-
-func typeListString(list []Type) string {
- var buf bytes.Buffer
- writeTypeList(&buf, list, nil, nil)
- return buf.String()
-}
-
// typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
// A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
// where an array/slice element is accessed before it is set up.
@@ -545,3 +331,21 @@ func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
}
return
}
+
+func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
+ out = in
+ for i, t := range in {
+ if u := subst.typ(t.typ); u != t.typ {
+ if !copied {
+ // first function that got substituted => allocate new out slice
+ // and copy all functions
+ new := make([]*Term, len(in))
+ copy(new, out)
+ out = new
+ copied = true
+ }
+ out[i] = NewTerm(t.tilde, u)
+ }
+ }
+ return
+}
diff --git a/src/cmd/compile/internal/types2/termlist.go b/src/cmd/compile/internal/types2/termlist.go
new file mode 100644
index 0000000000000000000000000000000000000000..844e39e3bf4ae275c76cb98eef96fbff35cb17c1
--- /dev/null
+++ b/src/cmd/compile/internal/types2/termlist.go
@@ -0,0 +1,167 @@
+// 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 "bytes"
+
+// A termlist represents the type set represented by the union
+// t1 ∪ y2 ∪ ... tn of the type sets of the terms t1 to tn.
+// A termlist is in normal form if all terms are disjoint.
+// termlist operations don't require the operands to be in
+// normal form.
+type termlist []*term
+
+// allTermlist represents the set of all types.
+// It is in normal form.
+var allTermlist = termlist{new(term)}
+
+// String prints the termlist exactly (without normalization).
+func (xl termlist) String() string {
+ if len(xl) == 0 {
+ return "∅"
+ }
+ var buf bytes.Buffer
+ for i, x := range xl {
+ if i > 0 {
+ buf.WriteString(" ∪ ")
+ }
+ buf.WriteString(x.String())
+ }
+ return buf.String()
+}
+
+// isEmpty reports whether the termlist xl represents the empty set of types.
+func (xl termlist) isEmpty() bool {
+ // If there's a non-nil term, the entire list is not empty.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil {
+ return false
+ }
+ }
+ return true
+}
+
+// isAll reports whether the termlist xl represents the set of all types.
+func (xl termlist) isAll() bool {
+ // If there's a 𝓤 term, the entire list is 𝓤.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil && x.typ == nil {
+ return true
+ }
+ }
+ return false
+}
+
+// norm returns the normal form of xl.
+func (xl termlist) norm() termlist {
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ used := make([]bool, len(xl))
+ var rl termlist
+ for i, xi := range xl {
+ if xi == nil || used[i] {
+ continue
+ }
+ for j := i + 1; j < len(xl); j++ {
+ xj := xl[j]
+ if xj == nil || used[j] {
+ continue
+ }
+ if u1, u2 := xi.union(xj); u2 == nil {
+ // If we encounter a 𝓤 term, the entire list is 𝓤.
+ // Exit early.
+ // (Note that this is not just an optimization;
+ // if we continue, we may end up with a 𝓤 term
+ // and other terms and the result would not be
+ // in normal form.)
+ if u1.typ == nil {
+ return allTermlist
+ }
+ xi = u1
+ used[j] = true // xj is now unioned into xi - ignore it in future iterations
+ }
+ }
+ rl = append(rl, xi)
+ }
+ return rl
+}
+
+// If the type set represented by xl is specified by a single (non-𝓤) term,
+// 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
+ }
+ return nil
+}
+
+// union returns the union xl ∪ yl.
+func (xl termlist) union(yl termlist) termlist {
+ return append(xl, yl...).norm()
+}
+
+// intersect returns the intersection xl ∩ yl.
+func (xl termlist) intersect(yl termlist) termlist {
+ if xl.isEmpty() || yl.isEmpty() {
+ return nil
+ }
+
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ var rl termlist
+ for _, x := range xl {
+ for _, y := range yl {
+ if r := x.intersect(y); r != nil {
+ rl = append(rl, r)
+ }
+ }
+ }
+ return rl.norm()
+}
+
+// equal reports whether xl and yl represent the same type set.
+func (xl termlist) equal(yl termlist) bool {
+ // TODO(gri) this should be more efficient
+ return xl.subsetOf(yl) && yl.subsetOf(xl)
+}
+
+// includes reports whether t ∈ xl.
+func (xl termlist) includes(t Type) bool {
+ for _, x := range xl {
+ if x.includes(t) {
+ return true
+ }
+ }
+ return false
+}
+
+// supersetOf reports whether y ⊆ xl.
+func (xl termlist) supersetOf(y *term) bool {
+ for _, x := range xl {
+ if y.subsetOf(x) {
+ return true
+ }
+ }
+ return false
+}
+
+// subsetOf reports whether xl ⊆ yl.
+func (xl termlist) subsetOf(yl termlist) bool {
+ if yl.isEmpty() {
+ return xl.isEmpty()
+ }
+
+ // each term x of xl must be a subset of yl
+ for _, x := range xl {
+ if !yl.supersetOf(x) {
+ return false // x is not a subset yl
+ }
+ }
+ return true
+}
diff --git a/src/cmd/compile/internal/types2/termlist_test.go b/src/cmd/compile/internal/types2/termlist_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1bdf9e13860abc413aa7af9961449b4abe2e1643
--- /dev/null
+++ b/src/cmd/compile/internal/types2/termlist_test.go
@@ -0,0 +1,313 @@
+// 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 (
+ "strings"
+ "testing"
+)
+
+// maketl makes a term list from a string of the term list.
+func maketl(s string) termlist {
+ s = strings.ReplaceAll(s, " ", "")
+ names := strings.Split(s, "∪")
+ r := make(termlist, len(names))
+ for i, n := range names {
+ r[i] = testTerm(n)
+ }
+ return r
+}
+
+func TestTermlistAll(t *testing.T) {
+ if !allTermlist.isAll() {
+ t.Errorf("allTermlist is not the set of all types")
+ }
+}
+
+func TestTermlistString(t *testing.T) {
+ for _, want := range []string{
+ "∅",
+ "𝓤",
+ "int",
+ "~int",
+ "myInt",
+ "∅ ∪ ∅",
+ "𝓤 ∪ 𝓤",
+ "∅ ∪ 𝓤 ∪ int",
+ "∅ ∪ 𝓤 ∪ int ∪ myInt",
+ } {
+ if got := maketl(want).String(); got != want {
+ t.Errorf("(%v).String() == %v", want, got)
+ }
+ }
+}
+
+func TestTermlistIsEmpty(t *testing.T) {
+ for test, want := range map[string]bool{
+ "∅": true,
+ "∅ ∪ ∅": true,
+ "∅ ∪ ∅ ∪ 𝓤": false,
+ "∅ ∪ ∅ ∪ myInt": false,
+ "𝓤": false,
+ "𝓤 ∪ int": false,
+ "𝓤 ∪ myInt ∪ ∅": false,
+ } {
+ xl := maketl(test)
+ got := xl.isEmpty()
+ if got != want {
+ t.Errorf("(%v).isEmpty() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistIsAll(t *testing.T) {
+ for test, want := range map[string]bool{
+ "∅": false,
+ "∅ ∪ ∅": false,
+ "int ∪ ~string": false,
+ "~int ∪ myInt": false,
+ "∅ ∪ ∅ ∪ 𝓤": true,
+ "𝓤": true,
+ "𝓤 ∪ int": true,
+ "myInt ∪ 𝓤": true,
+ } {
+ xl := maketl(test)
+ got := xl.isAll()
+ if got != want {
+ t.Errorf("(%v).isAll() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistNorm(t *testing.T) {
+ for _, test := range []struct {
+ xl, want string
+ }{
+ {"∅", "∅"},
+ {"∅ ∪ ∅", "∅"},
+ {"∅ ∪ int", "int"},
+ {"∅ ∪ myInt", "myInt"},
+ {"𝓤 ∪ int", "𝓤"},
+ {"𝓤 ∪ myInt", "𝓤"},
+ {"int ∪ myInt", "int ∪ myInt"},
+ {"~int ∪ int", "~int"},
+ {"~int ∪ myInt", "~int"},
+ {"int ∪ ~string ∪ int", "int ∪ ~string"},
+ {"~int ∪ string ∪ 𝓤 ∪ ~string ∪ int", "𝓤"},
+ {"~int ∪ string ∪ myInt ∪ ~string ∪ int", "~int ∪ ~string"},
+ } {
+ xl := maketl(test.xl)
+ got := maketl(test.xl).norm()
+ if got.String() != test.want {
+ t.Errorf("(%v).norm() = %v; want %v", xl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSingleType(t *testing.T) {
+ // helper to deal with nil types
+ tstring := func(typ Type) string {
+ if typ == nil {
+ return "nil"
+ }
+ return typ.String()
+ }
+
+ for test, want := range map[string]string{
+ "∅": "nil",
+ "𝓤": "nil",
+ "int": "int",
+ "myInt": "myInt",
+ "~int": "int",
+ "~int ∪ string": "nil",
+ "~int ∪ myInt": "int",
+ "∅ ∪ int": "int",
+ "∅ ∪ ~int": "int",
+ "∅ ∪ ~int ∪ string": "nil",
+ } {
+ xl := maketl(test)
+ got := tstring(xl.singleType())
+ if got != want {
+ t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
+ }
+ }
+}
+
+func TestTermlistUnion(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl, want string
+ }{
+
+ {"∅", "∅", "∅"},
+ {"∅", "𝓤", "𝓤"},
+ {"∅", "int", "int"},
+ {"𝓤", "~int", "𝓤"},
+ {"int", "~int", "~int"},
+ {"int", "string", "int ∪ string"},
+ {"int", "myInt", "int ∪ myInt"},
+ {"~int", "myInt", "~int"},
+ {"int ∪ string", "~string", "int ∪ ~string"},
+ {"~int ∪ string", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ string ∪ ∅", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ myInt ∪ ∅", "~string ∪ int", "~int ∪ ~string"},
+ {"~int ∪ string ∪ 𝓤", "~string ∪ int", "𝓤"},
+ {"~int ∪ string ∪ myInt", "~string ∪ int", "~int ∪ ~string"},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.union(yl).String()
+ if got != test.want {
+ t.Errorf("(%v).union(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistIntersect(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl, want string
+ }{
+
+ {"∅", "∅", "∅"},
+ {"∅", "𝓤", "∅"},
+ {"∅", "int", "∅"},
+ {"∅", "myInt", "∅"},
+ {"𝓤", "~int", "~int"},
+ {"𝓤", "myInt", "myInt"},
+ {"int", "~int", "int"},
+ {"int", "string", "∅"},
+ {"int", "myInt", "∅"},
+ {"~int", "myInt", "myInt"},
+ {"int ∪ string", "~string", "string"},
+ {"~int ∪ string", "~string ∪ int", "int ∪ string"},
+ {"~int ∪ string ∪ ∅", "~string ∪ int", "int ∪ string"},
+ {"~int ∪ myInt ∪ ∅", "~string ∪ int", "int"},
+ {"~int ∪ string ∪ 𝓤", "~string ∪ int", "int ∪ ~string"},
+ {"~int ∪ string ∪ myInt", "~string ∪ int", "int ∪ string"},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.intersect(yl).String()
+ if got != test.want {
+ t.Errorf("(%v).intersect(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistEqual(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", false},
+ {"𝓤", "𝓤", true},
+ {"𝓤 ∪ int", "𝓤", true},
+ {"𝓤 ∪ int", "string ∪ 𝓤", true},
+ {"𝓤 ∪ myInt", "string ∪ 𝓤", true},
+ {"int ∪ ~string", "string ∪ int", false},
+ {"~int ∪ string", "string ∪ myInt", false},
+ {"int ∪ ~string ∪ ∅", "string ∪ int ∪ ~string", true},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.equal(yl)
+ if got != test.want {
+ t.Errorf("(%v).equal(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistIncludes(t *testing.T) {
+ for _, test := range []struct {
+ xl, typ string
+ want bool
+ }{
+ {"∅", "int", false},
+ {"𝓤", "int", true},
+ {"~int", "int", true},
+ {"int", "string", false},
+ {"~int", "string", false},
+ {"~int", "myInt", true},
+ {"int ∪ string", "string", true},
+ {"~int ∪ string", "int", true},
+ {"~int ∪ string", "myInt", true},
+ {"~int ∪ myInt ∪ ∅", "myInt", true},
+ {"myInt ∪ ∅ ∪ 𝓤", "int", true},
+ } {
+ xl := maketl(test.xl)
+ yl := testTerm(test.typ).typ
+ got := xl.includes(yl)
+ if got != test.want {
+ t.Errorf("(%v).includes(%v) = %v; want %v", test.xl, yl, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSupersetOf(t *testing.T) {
+ for _, test := range []struct {
+ xl, typ string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", false},
+ {"∅", "int", false},
+ {"𝓤", "∅", true},
+ {"𝓤", "𝓤", true},
+ {"𝓤", "int", true},
+ {"𝓤", "~int", true},
+ {"𝓤", "myInt", true},
+ {"~int", "int", true},
+ {"~int", "~int", true},
+ {"~int", "myInt", true},
+ {"int", "~int", false},
+ {"myInt", "~int", false},
+ {"int", "string", false},
+ {"~int", "string", false},
+ {"int ∪ string", "string", true},
+ {"int ∪ string", "~string", false},
+ {"~int ∪ string", "int", true},
+ {"~int ∪ string", "myInt", true},
+ {"~int ∪ string ∪ ∅", "string", true},
+ {"~string ∪ ∅ ∪ 𝓤", "myInt", true},
+ } {
+ xl := maketl(test.xl)
+ y := testTerm(test.typ)
+ got := xl.supersetOf(y)
+ if got != test.want {
+ t.Errorf("(%v).supersetOf(%v) = %v; want %v", test.xl, y, got, test.want)
+ }
+ }
+}
+
+func TestTermlistSubsetOf(t *testing.T) {
+ for _, test := range []struct {
+ xl, yl string
+ want bool
+ }{
+ {"∅", "∅", true},
+ {"∅", "𝓤", true},
+ {"𝓤", "∅", false},
+ {"𝓤", "𝓤", true},
+ {"int", "int ∪ string", true},
+ {"~int", "int ∪ string", false},
+ {"~int", "myInt ∪ string", false},
+ {"myInt", "~int ∪ string", true},
+ {"~int", "string ∪ string ∪ int ∪ ~int", true},
+ {"myInt", "string ∪ string ∪ ~int", true},
+ {"int ∪ string", "string", false},
+ {"int ∪ string", "string ∪ int", true},
+ {"int ∪ ~string", "string ∪ int", false},
+ {"myInt ∪ ~string", "string ∪ int ∪ 𝓤", true},
+ {"int ∪ ~string", "string ∪ int ∪ ∅ ∪ string", false},
+ {"int ∪ myInt", "string ∪ ~int ∪ ∅ ∪ string", true},
+ } {
+ xl := maketl(test.xl)
+ yl := maketl(test.yl)
+ got := xl.subsetOf(yl)
+ if got != test.want {
+ t.Errorf("(%v).subsetOf(%v) = %v; want %v", test.xl, test.yl, got, test.want)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/testdata/check/builtins.go2 b/src/cmd/compile/internal/types2/testdata/check/builtins.go2
index 3918d836b5277a8a71d46380b441b1a29d409bd7..48a39891bf85874821e6b77fc24504b8a9c6d9ff 100644
--- a/src/cmd/compile/internal/types2/testdata/check/builtins.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/builtins.go2
@@ -6,48 +6,269 @@
package builtins
-type Bmc interface {
- type map[rune]string, chan int
+import "unsafe"
+
+// close
+
+type C0 interface{ int }
+type C1 interface{ chan int }
+type C2 interface{ chan int | <-chan int }
+type C3 interface{ chan int | chan float32 }
+type C4 interface{ chan int | chan<- int }
+type C5[T any] interface{ ~chan T | chan<- T }
+
+func _[T any](ch T) {
+ close(ch /* ERROR cannot close non-channel */)
+}
+
+func _[T C0](ch T) {
+ close(ch /* ERROR cannot close non-channel */)
+}
+
+func _[T C1](ch T) {
+ close(ch)
+}
+
+func _[T C2](ch T) {
+ close(ch /* ERROR cannot close receive-only channel */)
+}
+
+func _[T C3](ch T) {
+ close(ch)
+}
+
+func _[T C4](ch T) {
+ close(ch)
}
-type Bms interface {
- type map[string]int, []int
+func _[T C5[X], X any](ch T) {
+ close(ch)
}
-type Bcs interface {
- type chan bool, []float64
+// copy
+
+func _[T any](x, y T) {
+ copy(x /* ERROR copy expects slice arguments */ , y)
}
-type Bss interface {
- type []int, []string
+func _[T ~[]byte](x, y T) {
+ copy(x, y)
+ copy(x, "foo")
+ copy("foo" /* ERROR expects slice arguments */ , y)
+
+ var x2 []byte
+ copy(x2, y) // element types are identical
+ copy(y, x2) // element types are identical
+
+ type myByte byte
+ var x3 []myByte
+ copy(x3 /* ERROR different element types */ , y)
+ copy(y, x3 /* ERROR different element types */ )
}
-func _[T any] () {
- _ = make(T /* ERROR invalid argument */ )
- _ = make(T /* ERROR invalid argument */ , 10)
- _ = make(T /* ERROR invalid argument */ , 10, 20)
+func _[T ~[]E, E any](x T, y []E) {
+ copy(x, y)
+ copy(x /* ERROR different element types */ , "foo")
}
-func _[T Bmc] () {
- _ = make(T)
- _ = make(T, 10)
- _ = make /* ERROR expects 1 or 2 arguments */ (T, 10, 20)
+func _[T ~string](x []byte, y T) {
+ copy(x, y)
+ copy(y /* ERROR expects slice arguments */ , x)
}
-func _[T Bms] () {
- _ = make /* ERROR expects 2 arguments */ (T)
- _ = make(T, 10)
- _ = make /* ERROR expects 2 arguments */ (T, 10, 20)
+func _[T ~[]byte|~string](x T, y []byte) {
+ copy(x /* ERROR expects slice arguments */ , y)
+ copy(y, x)
}
-func _[T Bcs] () {
- _ = make /* ERROR expects 2 arguments */ (T)
- _ = make(T, 10)
- _ = make /* ERROR expects 2 arguments */ (T, 10, 20)
+type L0 []int
+type L1 []int
+
+func _[T L0 | L1](x, y T) {
+ copy(x, y)
}
-func _[T Bss] () {
- _ = make /* ERROR expects 2 or 3 arguments */ (T)
- _ = make(T, 10)
- _ = make(T, 10, 20)
+// delete
+
+type M0 interface{ int }
+type M1 interface{ map[string]int }
+type M2 interface { map[string]int | map[string]float64 }
+type M3 interface{ map[string]int | map[rune]int }
+type M4[K comparable, V any] interface{ map[K]V | map[rune]V }
+
+func _[T any](m T) {
+ delete(m /* ERROR not a map */, "foo")
+}
+
+func _[T M0](m T) {
+ delete(m /* ERROR not a map */, "foo")
+}
+
+func _[T M1](m T) {
+ delete(m, "foo")
+}
+
+func _[T M2](m T) {
+ delete(m, "foo")
+ delete(m, 0 /* ERROR cannot use .* as string */)
+}
+
+func _[T M3](m T) {
+ delete(m /* ERROR must have identical key types */, "foo")
+}
+
+func _[T M4[rune, V], V any](m T) {
+ delete(m, 'k')
+}
+
+func _[T M4[K, V], K comparable, V any](m T) {
+ delete(m /* ERROR must have identical key types */, "foo")
+}
+
+// make
+
+type myChan chan int
+
+func _[
+ S1 ~[]int,
+ S2 ~[]int | ~chan int,
+
+ M1 ~map[string]int,
+ M2 ~map[string]int | ~chan int,
+
+ C1 ~chan int,
+ C2 ~chan int | ~chan string,
+ C3 chan int | myChan, // single underlying type
+]() {
+ type S0 []int
+ _ = make([]int, 10)
+ _ = make(S0, 10)
+ _ = make(S1, 10)
+ _ = make /* ERROR not enough arguments */ ()
+ _ = 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 S2: no structural type */ , 10)
+
+ type M0 map[string]int
+ _ = make(map[string]int)
+ _ = make(M0)
+ _ = make(M1)
+ _ = make(M1, 10)
+ _ = make/* ERROR expects 1 or 2 arguments */(M1, 10, 20)
+ _ = make(M2 /* ERROR cannot make M2: no structural type */ )
+
+ type C0 chan int
+ _ = make(chan int)
+ _ = make(C0)
+ _ = make(C1)
+ _ = make(C1, 10)
+ _ = make/* ERROR expects 1 or 2 arguments */(C1, 10, 20)
+ _ = make(C2 /* ERROR cannot make C2: no structural type */ )
+ _ = make(C3)
+}
+
+// unsafe.Alignof
+
+func _[T comparable]() {
+ var (
+ b int64
+ a [10]T
+ s struct{ f T }
+ p *T
+ l []T
+ f func(T)
+ i interface{ m() T }
+ c chan T
+ m map[T]T
+ t T
+ )
+
+ const bb = unsafe.Alignof(b)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Alignof(p)
+ assert(pp == 8)
+ const ll = unsafe.Alignof(l)
+ assert(ll == 8)
+ const ff = unsafe.Alignof(f)
+ assert(ff == 8)
+ const ii = unsafe.Alignof(i)
+ assert(ii == 8)
+ const cc = unsafe.Alignof(c)
+ assert(cc == 8)
+ const mm = unsafe.Alignof(m)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
+}
+
+// unsafe.Offsetof
+
+func _[T comparable]() {
+ var (
+ b struct{ _, f int64 }
+ a struct{ _, f [10]T }
+ s struct{ _, f struct{ f T } }
+ p struct{ _, f *T }
+ l struct{ _, f []T }
+ f struct{ _, f func(T) }
+ i struct{ _, f interface{ m() T } }
+ c struct{ _, f chan T }
+ m struct{ _, f map[T]T }
+ t struct{ _, f T }
+ )
+
+ const bb = unsafe.Offsetof(b.f)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Offsetof(p.f)
+ assert(pp == 8)
+ const ll = unsafe.Offsetof(l.f)
+ assert(ll == 24)
+ const ff = unsafe.Offsetof(f.f)
+ assert(ff == 8)
+ const ii = unsafe.Offsetof(i.f)
+ assert(ii == 16)
+ const cc = unsafe.Offsetof(c.f)
+ assert(cc == 8)
+ const mm = unsafe.Offsetof(m.f)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
+}
+
+// unsafe.Sizeof
+
+func _[T comparable]() {
+ var (
+ b int64
+ a [10]T
+ s struct{ f T }
+ p *T
+ l []T
+ f func(T)
+ i interface{ m() T }
+ c chan T
+ m map[T]T
+ t T
+ )
+
+ const bb = unsafe.Sizeof(b)
+ assert(bb == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(a)
+ const _ = unsafe /* ERROR not constant */ .Alignof(s)
+ const pp = unsafe.Sizeof(p)
+ assert(pp == 8)
+ const ll = unsafe.Sizeof(l)
+ assert(ll == 24)
+ const ff = unsafe.Sizeof(f)
+ assert(ff == 8)
+ const ii = unsafe.Sizeof(i)
+ assert(ii == 16)
+ const cc = unsafe.Sizeof(c)
+ assert(cc == 8)
+ const mm = unsafe.Sizeof(m)
+ assert(mm == 8)
+ const _ = unsafe /* ERROR not constant */ .Alignof(t)
}
diff --git a/src/cmd/compile/internal/types2/testdata/check/builtins.src b/src/cmd/compile/internal/types2/testdata/check/builtins.src
index 6d1f47129b9c16d2935a66c6eb97a740550041ae..de27f5c6322132acbddfd8dc7d84b3fa44e923e7 100644
--- a/src/cmd/compile/internal/types2/testdata/check/builtins.src
+++ b/src/cmd/compile/internal/types2/testdata/check/builtins.src
@@ -25,7 +25,7 @@ func append1() {
_ = append(s, b)
_ = append(s, x /* ERROR cannot use x */ )
_ = append(s, s /* ERROR cannot use s */ )
- _ = append(s... ) /* ERROR not enough arguments */
+ _ = append(s /* ERROR not enough arguments */ ...)
_ = append(s, b, s /* ERROR too many arguments */ ... )
_ = append(s, 1, 2, 3)
_ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6)
@@ -144,7 +144,7 @@ func close1() {
var r <-chan int
close() // ERROR not enough arguments
close(1, 2) // ERROR too many arguments
- close(42 /* ERROR not a channel */)
+ close(42 /* ERROR cannot close non-channel */)
close(r /* ERROR receive-only channel */)
close(c)
_ = close /* ERROR used as value */ (c)
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 0000000000000000000000000000000000000000..60eac97a3f2084ef2e58302f79ecd25bc7c19211
--- /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/const0.src b/src/cmd/compile/internal/types2/testdata/check/const0.src
index 5608b1549ba8d7774c2463fa9faab3702673f061..3cffdf904c87feb265ee5b1293d07be185e17372 100644
--- a/src/cmd/compile/internal/types2/testdata/check/const0.src
+++ b/src/cmd/compile/internal/types2/testdata/check/const0.src
@@ -27,7 +27,7 @@ const (
ub1 = true
ub2 = 2 < 1
ub3 = ui1 == uf1
- ub4 = true /* ERROR "cannot convert" */ == 0
+ ub4 = true /* ERROR "mismatched types untyped bool and untyped int" */ == 0
// integer values
ui0 = 0
diff --git a/src/cmd/compile/internal/types2/testdata/check/cycles.src b/src/cmd/compile/internal/types2/testdata/check/cycles.src
index b2ee8ecd5f6001076e336779a9b5150c1e524162..998f9f7da980116ccbae32f6fb796766fe4c19fa 100644
--- a/src/cmd/compile/internal/types2/testdata/check/cycles.src
+++ b/src/cmd/compile/internal/types2/testdata/check/cycles.src
@@ -45,6 +45,7 @@ type (
// pointers
P0 *P0
+ PP *struct{ PP.f /* ERROR no field or method f */ }
// functions
F0 func(F0)
diff --git a/src/cmd/compile/internal/types2/testdata/check/cycles4.src b/src/cmd/compile/internal/types2/testdata/check/cycles4.src
index 445babca68bc3db73a1cc192f366480171fbc659..924aabf475f9640cdfa6da8834fa9a8db6aa0a2b 100644
--- a/src/cmd/compile/internal/types2/testdata/check/cycles4.src
+++ b/src/cmd/compile/internal/types2/testdata/check/cycles4.src
@@ -4,6 +4,8 @@
package p
+import "unsafe"
+
// Check that all methods of T are collected before
// determining the result type of m (which embeds
// all methods of T).
@@ -13,7 +15,7 @@ type T interface {
E
}
-var _ = T.m(nil).m().e()
+var _ int = T.m(nil).m().e()
type E interface {
e() int
@@ -22,7 +24,7 @@ type E interface {
// Check that unresolved forward chains are followed
// (see also comment in resolver.go, checker.typeDecl).
-var _ = C.m(nil).m().e()
+var _ int = C.m(nil).m().e()
type A B
@@ -108,3 +110,12 @@ type Element interface {
type Event interface {
Target() Element
}
+
+// Check that accessing an interface method too early doesn't lead
+// to follow-on errors due to an incorrectly computed type set.
+
+type T8 interface {
+ m() [unsafe.Sizeof(T8.m /* ERROR undefined */ )]int
+}
+
+var _ = T8.m // no error expected here
diff --git a/src/cmd/compile/internal/types2/testdata/check/decls0.src b/src/cmd/compile/internal/types2/testdata/check/decls0.src
index e78d8867e0995b38ae8d2d936143937839a5a808..09e5d5c5ad84355af0bdea8552b1de283b5cf256 100644
--- a/src/cmd/compile/internal/types2/testdata/check/decls0.src
+++ b/src/cmd/compile/internal/types2/testdata/check/decls0.src
@@ -4,7 +4,7 @@
// type declarations
-package decls0
+package go1_17 // don't permit non-interface elements in interfaces
import "unsafe"
@@ -146,7 +146,7 @@ type (
m1(I5)
}
I6 interface {
- S0 /* ERROR "not an interface" */
+ S0 /* ERROR "non-interface type S0" */
}
I7 interface {
I1
@@ -185,10 +185,10 @@ func f2(x *f2 /* ERROR "not a type" */ ) {}
func f3() (x f3 /* ERROR "not a type" */ ) { return }
func f4() (x *f4 /* ERROR "not a type" */ ) { return }
-func (S0) m1(x S0 /* ERROR value .* is not a type */ .m1) {}
-func (S0) m2(x *S0 /* ERROR value .* is not a type */ .m2) {}
-func (S0) m3() (x S0 /* ERROR value .* is not a type */ .m3) { return }
-func (S0) m4() (x *S0 /* ERROR value .* is not a type */ .m4) { return }
+func (S0) m1(x S0 /* ERROR illegal cycle in method declaration */ .m1) {}
+func (S0) m2(x *S0 /* ERROR illegal cycle in method declaration */ .m2) {}
+func (S0) m3() (x S0 /* ERROR illegal cycle in method declaration */ .m3) { return }
+func (S0) m4() (x *S0 /* ERROR illegal cycle in method declaration */ .m4) { return }
// interfaces may not have any blank methods
type BlankI interface {
diff --git a/src/cmd/compile/internal/types2/testdata/check/decls1.src b/src/cmd/compile/internal/types2/testdata/check/decls1.src
index e6beb7835897bc12ae90eed4c1007b956ac33a21..1167ced366e2bff73453c5bf09559c54c8f0ece4 100644
--- a/src/cmd/compile/internal/types2/testdata/check/decls1.src
+++ b/src/cmd/compile/internal/types2/testdata/check/decls1.src
@@ -83,7 +83,7 @@ var (
// Constant expression initializations
var (
- v1 = 1 /* ERROR "cannot convert" */ + "foo"
+ v1 = 1 /* ERROR "mismatched types untyped int and untyped string" */ + "foo"
v2 = c + 255
v3 = c + 256 /* ERROR "overflows" */
v4 = r + 2147483647
diff --git a/src/cmd/compile/internal/types2/testdata/check/errors.src b/src/cmd/compile/internal/types2/testdata/check/errors.src
index ff929217c4c865ae18f8b918ee720762f15cf3cc..5f09197bde0f27d56ec3abb0543facc3261d1417 100644
--- a/src/cmd/compile/internal/types2/testdata/check/errors.src
+++ b/src/cmd/compile/internal/types2/testdata/check/errors.src
@@ -8,32 +8,38 @@ package errors
// (matching messages are regular expressions, hence the \'s).
func f(x int, m map[string]int) {
// no values
- _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m)
+ _ = f /* ERROR f\(0, m\) \(no value\) used as value */ (0, m)
// built-ins
- _ = println /* ERROR "println \(built-in\) must be called" */
+ _ = println // ERROR println \(built-in\) must be called
// types
- _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */
+ _ = complex128 // ERROR complex128 \(type\) is not an expression
// constants
const c1 = 991
const c2 float32 = 0.5
- 0 /* ERROR "0 \(untyped int constant\) is not used" */
- c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */
- c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */
- c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2
+ const c3 = "foo"
+ 0 // ERROR 0 \(untyped int constant\) is not used
+ 0.5 // ERROR 0.5 \(untyped float constant\) is not used
+ "foo" // ERROR "foo" \(untyped string constant\) is not used
+ c1 // ERROR c1 \(untyped int constant 991\) is not used
+ c2 // ERROR c2 \(constant 0.5 of type float32\) is not used
+ c1 /* ERROR c1 \+ c2 \(constant 991.5 of type float32\) is not used */ + c2
+ c3 // ERROR c3 \(untyped string constant "foo"\) is not used
// variables
- x /* ERROR "x \(variable of type int\) is not used" */
+ x // ERROR x \(variable of type int\) is not used
// values
- x /* ERROR "x != x \(untyped bool value\) is not used" */ != x
- x /* ERROR "x \+ x \(value of type int\) is not used" */ + x
+ nil // ERROR nil is not used
+ (*int)(nil) // ERROR \(\*int\)\(nil\) \(value of type \*int\) is not used
+ x /* ERROR x != x \(untyped bool value\) is not used */ != x
+ x /* ERROR x \+ x \(value of type int\) is not used */ + x
// value, ok's
const s = "foo"
- m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s]
+ m /* ERROR m\[s\] \(map index expression of type int\) is not used */ [s]
}
// Valid ERROR comments can have a variety of forms.
diff --git a/src/cmd/compile/internal/types2/testdata/check/expr1.src b/src/cmd/compile/internal/types2/testdata/check/expr1.src
index 4ead815158f61e2dbbb91dec8e7feabef4a3923c..85ad234bbbf46833d33c6328b38192c5a9bd88d4 100644
--- a/src/cmd/compile/internal/types2/testdata/check/expr1.src
+++ b/src/cmd/compile/internal/types2/testdata/check/expr1.src
@@ -111,10 +111,10 @@ type mystring string
func _(x, y string, z mystring) {
x = x + "foo"
x = x /* ERROR not defined */ - "foo"
- x = x + 1 // ERROR cannot convert
+ x = x + 1 // ERROR mismatched types string and untyped int
x = x + y
x = x /* ERROR not defined */ - y
- x = x * 10 // ERROR cannot convert
+ x = x * 10 // ERROR mismatched types string and untyped int
}
func f() (a, b int) { return }
diff --git a/src/cmd/compile/internal/types2/testdata/check/expr2.src b/src/cmd/compile/internal/types2/testdata/check/expr2.src
index 0c959e8011944f0ce42b89bc193e448628bc762f..8e5862319e650b9dc3afbae66435516471bfaef1 100644
--- a/src/cmd/compile/internal/types2/testdata/check/expr2.src
+++ b/src/cmd/compile/internal/types2/testdata/check/expr2.src
@@ -10,7 +10,7 @@ func _bool() {
const t = true == true
const f = true == false
_ = t /* ERROR "cannot compare" */ < f
- _ = 0 /* ERROR "cannot convert" */ == t
+ _ = 0 /* ERROR "mismatched types untyped int and untyped bool" */ == t
var b bool
var x, y float32
b = x < y
@@ -29,7 +29,7 @@ func arrays() {
_ = a == b
_ = a != b
_ = a /* ERROR < not defined */ < b
- _ = a == nil /* ERROR cannot convert */
+ _ = a == nil /* ERROR invalid operation.*mismatched types */
type C [10]int
var c C
@@ -53,7 +53,7 @@ func structs() {
_ = s == t
_ = s != t
_ = s /* ERROR < not defined */ < t
- _ = s == nil /* ERROR cannot convert */
+ _ = s == nil /* ERROR invalid operation.*mismatched types */
type S struct {
x int
diff --git a/src/cmd/compile/internal/types2/testdata/check/expr3.src b/src/cmd/compile/internal/types2/testdata/check/expr3.src
index eab3f72c4d5ff0d323151f3c3b9feff11cf29de3..646319e4c470713b760ca5068cc54f7c313e07a8 100644
--- a/src/cmd/compile/internal/types2/testdata/check/expr3.src
+++ b/src/cmd/compile/internal/types2/testdata/check/expr3.src
@@ -45,9 +45,9 @@ func indexes() {
_ = a[:10:10]
_ = a[:11 /* ERROR "index .* out of bounds" */ :10]
_ = a[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = a[10:0:10] /* ERROR "invalid slice indices" */
- _ = a[0:10:0] /* ERROR "invalid slice indices" */
- _ = a[10:0:0] /* ERROR "invalid slice indices" */
+ _ = a[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = a[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = a[10:0 /* ERROR "invalid slice indices" */:0]
_ = &a /* ERROR "cannot take address" */ [:10]
pa := &a
@@ -63,9 +63,9 @@ func indexes() {
_ = pa[:10:10]
_ = pa[:11 /* ERROR "index .* out of bounds" */ :10]
_ = pa[:10:11 /* ERROR "index .* out of bounds" */ ]
- _ = pa[10:0:10] /* ERROR "invalid slice indices" */
- _ = pa[0:10:0] /* ERROR "invalid slice indices" */
- _ = pa[10:0:0] /* ERROR "invalid slice indices" */
+ _ = pa[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = pa[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = pa[10:0 /* ERROR "invalid slice indices" */ :0]
_ = &pa /* ERROR "cannot take address" */ [:10]
var b [0]int
@@ -90,9 +90,9 @@ func indexes() {
_ = s[1 /* ERROR "overflows" */ <<100 : 1 /* ERROR "overflows" */ <<100]
_ = s[: /* ERROR "middle index required" */ : /* ERROR "final index required" */ ]
_ = s[:10:10]
- _ = s[10:0:10] /* ERROR "invalid slice indices" */
- _ = s[0:10:0] /* ERROR "invalid slice indices" */
- _ = s[10:0:0] /* ERROR "invalid slice indices" */
+ _ = s[10:0 /* ERROR "invalid slice indices" */ :10]
+ _ = s[0:10:0 /* ERROR "invalid slice indices" */ ]
+ _ = s[10:0 /* ERROR "invalid slice indices" */ :0]
_ = &s /* ERROR "cannot take address" */ [:10]
var m map[string]int
@@ -104,14 +104,14 @@ func indexes() {
var ok mybool
_, ok = m["bar"]
_ = ok
- _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "cannot convert"
+ _ = m[0 /* ERROR "cannot use 0" */ ] + "foo" // ERROR "mismatched types int and untyped string"
var t string
_ = t[- /* ERROR "negative" */ 1]
_ = t[- /* ERROR "negative" */ 1 :]
_ = t[: - /* ERROR "negative" */ 1]
- _ = t /* ERROR "3-index slice of string" */ [1:2:3]
- _ = "foo" /* ERROR "3-index slice of string" */ [1:2:3]
+ _ = t[1:2:3 /* ERROR "3-index slice of string" */ ]
+ _ = "foo"[1:2:3 /* ERROR "3-index slice of string" */ ]
var t0 byte
t0 = t[0]
_ = t0
@@ -459,7 +459,7 @@ func type_asserts() {
var t I
_ = t /* ERROR "use of .* outside type switch" */ .(type)
- _ = t /* ERROR "missing method m" */ .(T)
+ _ = t /* ERROR "method m has pointer receiver" */ .(T)
_ = t.(*T)
_ = t /* ERROR "missing method m" */ .(T1)
_ = t /* ERROR "wrong type for method m" */ .(T2)
@@ -494,23 +494,23 @@ func _calls() {
f1(0)
f1(x)
f1(10.0)
- f1() /* ERROR "not enough arguments" */
- f1(x, y /* ERROR "too many arguments" */ )
+ f1 /* ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)" */ ()
+ f1(x, y /* ERROR "too many arguments in call to f1\n\thave \(int, float32\)\n\twant \(int\)" */ )
f1(s /* ERROR "cannot use .* in argument" */ )
f1(x ... /* ERROR "cannot use ..." */ )
f1(g0 /* ERROR "used as value" */ ())
f1(g1())
- f1(g2 /* ERROR "too many arguments" */ ())
+ f1(g2 /* ERROR "too many arguments in call to f1\n\thave \(float32, string\)\n\twant \(int\)" */ ())
- f2() /* ERROR "not enough arguments" */
- f2(3.14) /* ERROR "not enough arguments" */
+ f2 /* ERROR "not enough arguments in call to f2\n\thave \(\)\n\twant \(float32, string\)" */ ()
+ f2(3.14 /* ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(float32, string\)" */ )
f2(3.14, "foo")
f2(x /* ERROR "cannot use .* in argument" */ , "foo")
f2(g0 /* ERROR "used as value" */ ())
- f2(g1()) /* ERROR "not enough arguments" */
+ f2(g1 /* ERROR "not enough arguments in call to f2\n\thave \(int\)\n\twant \(float32, string\)" */ ())
f2(g2())
- fs() /* ERROR "not enough arguments" */
+ fs /* ERROR "not enough arguments" */ ()
fs(g0 /* ERROR "used as value" */ ())
fs(g1 /* ERROR "cannot use .* in argument" */ ())
fs(g2 /* ERROR "too many arguments" */ ())
diff --git a/src/cmd/compile/internal/types2/testdata/check/tinference.go2 b/src/cmd/compile/internal/types2/testdata/check/funcinference.go2
similarity index 58%
rename from src/cmd/compile/internal/types2/testdata/check/tinference.go2
rename to src/cmd/compile/internal/types2/testdata/check/funcinference.go2
index a53fde0a2a944decd986e8a1046e2be71865a376..7160e18b19c51cc717c287efbc98ed88cabf1df6 100644
--- a/src/cmd/compile/internal/types2/testdata/check/tinference.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/funcinference.go2
@@ -2,42 +2,43 @@
// 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{}
-func f0[A any, B interface{type C}, C interface{type D}, D interface{type A}](A, B, C, D)
+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")
+ f("a", nil, nil, nil)
+ f0("a", nil, nil, nil)
}
-func f1[A any, B interface{type A}](A, B)
+func f1[A any, B interface{~*A}](A, B) {}
func _() {
f := f1[int]
- f(int(0), int(0))
- f1(int(0), int(0))
+ f(int(0), new(int))
+ f1(int(0), new(int))
}
-func f2[A any, B interface{type []A}](A, B)
+func f2[A any, B interface{~[]A}](A, B) {}
func _() {
f := f2[byte]
f(byte(0), []byte{})
f2(byte(0), []byte{})
}
-func f3[A any, B interface{type C}, C interface{type *A}](A, B, C)
-func _() {
- f := f3[int]
- var x int
- f(x, &x, &x)
- f3(x, &x, &x)
-}
+// Embedding stand-alone type parameters is not permitted for now. Disabled.
+// func f3[A any, B interface{~C}, C interface{~*A}](A, B, C)
+// func _() {
+// f := f3[int]
+// var x int
+// f(x, &x, &x)
+// f3(x, &x, &x)
+// }
-func f4[A any, B interface{type []C}, C interface{type *A}](A, B, C)
+func f4[A any, B interface{~[]C}, C interface{~*A}](A, B, C) {}
func _() {
f := f4[int]
var x int
@@ -45,26 +46,24 @@ func _() {
f4(x, []*int{}, &x)
}
-func f5[A interface{type struct{b B; c C}}, B any, C interface{type *B}](x B) A
+func f5[A interface{~struct{b B; c C}}, B any, C interface{~*B}](x B) A { panic(0) }
func _() {
x := f5(1.2)
var _ float64 = x.b
var _ float64 = *x.c
}
-func f6[A any, B interface{type struct{f []A}}](B) A
+func f6[A any, B interface{~struct{f []A}}](B) A { panic(0) }
func _() {
x := f6(struct{f []string}{})
var _ string = x
}
-// TODO(gri) Need to flag invalid recursive constraints. At the
-// moment these cause infinite recursions and stack overflow.
-// func f7[A interface{type B}, B interface{type A}]()
+func f7[A interface{*B}, B interface{~*A}]() {}
// More realistic examples
-func Double[S interface{ type []E }, E interface{ type int, int8, int16, int32, int64 }](s S) S {
+func Double[S interface{ ~[]E }, E interface{ ~int | ~int8 | ~int16 | ~int32 | ~int64 }](s S) S {
r := make(S, len(s))
for i, v := range s {
r[i] = v + v
@@ -80,7 +79,7 @@ var _ = Double(MySlice{1})
type Setter[B any] interface {
Set(string)
- type *B
+ ~*B
}
func FromStrings[T interface{}, PT Setter[T]](s []string) []T {
diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.go2 b/src/cmd/compile/internal/types2/testdata/check/issues.go2
index 1c73b5da9219c04c1b2a479f594fc7d315ab3948..76f9cc5010af6d1879faeb843641edd01c674c17 100644
--- a/src/cmd/compile/internal/types2/testdata/check/issues.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/issues.go2
@@ -24,25 +24,23 @@ func _() {
eql[io.Reader](nil, nil)
}
-// If we have a receiver of pointer type (below: *T) we must ignore
-// the pointer in the implementation of the method lookup because
-// the type bound of T is an interface and pointer to interface types
-// have no methods and then the lookup would fail.
+// If we have a receiver of pointer to type parameter type (below: *T)
+// we don't have any methods, like for interfaces.
type C[T any] interface {
m()
}
// using type bound C
func _[T C[T]](x *T) {
- x.m()
+ x.m /* ERROR x\.m undefined */ ()
}
// using an interface literal as bound
func _[T interface{ m() }](x *T) {
- x.m()
+ x.m /* ERROR x\.m undefined */ ()
}
-func f2[_ interface{ m1(); m2() }]()
+func f2[_ interface{ m1(); m2() }]() {}
type T struct{}
func (T) m1()
@@ -57,15 +55,15 @@ func _() {
// type with a type list constraint, all of the type argument's types in its
// bound, but at least one (!), must be in the type list of the bound of the
// corresponding parameterized type's type parameter.
-type T1[P interface{type uint}] struct{}
+type T1[P interface{~uint}] struct{}
func _[P any]() {
- _ = T1[P /* ERROR P has no type constraints */ ]{}
+ _ = T1[P /* ERROR empty interface P does not implement interface{~uint} */ ]{}
}
// This is the original (simplified) program causing the same issue.
type Unsigned interface {
- type uint
+ ~uint
}
type T2[U Unsigned] struct {
@@ -76,8 +74,8 @@ func (u T2[U]) Add1() U {
return u.s + 1
}
-func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] {
- return T2[U /* ERROR U has no type constraints */ ]{}
+func NewT2[U any]() T2[U /* ERROR empty interface U does not implement Unsigned */ ] {
+ return T2[U /* ERROR empty interface U does not implement Unsigned */ ]{}
}
func _() {
@@ -147,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
@@ -156,7 +154,7 @@ type inf2[T any] struct{ inf2 /* ERROR illegal cycle */ [T] }
// predicate disjunction in the implementation was wrong because if a type list
// contains both an integer and a floating-point type, the type parameter is
// neither an integer or a floating-point number.
-func convert[T1, T2 interface{type int, uint, float32}](v T1) T2 {
+func convert[T1, T2 interface{~int | ~uint | ~float32}](v T1) T2 {
return T2(v)
}
@@ -168,12 +166,12 @@ func _() {
// both numeric, or both strings. The implementation had the same problem
// with this check as the conversion issue above (issue #39623).
-func issue39623[T interface{type int, string}](x, y T) T {
+func issue39623[T interface{~int | ~string}](x, y T) T {
return x + y
}
// Simplified, from https://go2goplay.golang.org/p/efS6x6s-9NI:
-func Sum[T interface{type int, string}](s []T) (sum T) {
+func Sum[T interface{~int | ~string}](s []T) (sum T) {
for _, v := range s {
sum += v
}
@@ -182,19 +180,19 @@ func Sum[T interface{type int, string}](s []T) (sum T) {
// Assignability of an unnamed pointer type to a type parameter that
// has a matching underlying type.
-func _[T interface{}, PT interface{type *T}] (x T) PT {
+func _[T interface{}, PT interface{~*T}] (x T) PT {
return &x
}
// Indexing of generic types containing type parameters in their type list:
-func at[T interface{ type []E }, E interface{}](x T, i int) E {
+func at[T interface{ ~[]E }, E interface{}](x T, i int) E {
return x[i]
}
// A generic type inside a function acts like a named type. Its underlying
// type is itself, its "operational type" is defined by the type list in
// the tybe bound, if any.
-func _[T interface{type int}](x T) {
+func _[T interface{~int}](x T) {
type myint int
var _ int = int(x)
var _ T = 42
@@ -203,24 +201,31 @@ func _[T interface{type int}](x T) {
// Indexing a generic type with an array type bound checks length.
// (Example by mdempsky@.)
-func _[T interface { type [10]int }](x T) {
+func _[T interface { ~[10]int }](x T) {
_ = x[9] // ok
_ = x[20 /* ERROR out of bounds */ ]
}
// Pointer indirection of a generic type.
-func _[T interface{ type *int }](p T) int {
+func _[T interface{ ~*int }](p T) int {
return *p
}
// Channel sends and receives on generic types.
-func _[T interface{ type chan int }](ch T) int {
+func _[T interface{ ~chan int }](ch T) int {
ch <- 0
return <- ch
}
// Calling of a generic variable.
-func _[T interface{ type func() }](f T) {
+func _[T interface{ ~func() }](f T) {
+ f()
+ go f()
+}
+
+type F1 func()
+type F2 func()
+func _[T interface{ func()|F1|F2 }](f T) {
f()
go f()
}
@@ -232,9 +237,9 @@ func _[T interface{ type func() }](f T) {
// type parameter that was substituted with a defined type.
// Test case from an (originally) failing example.
-type sliceOf[E any] interface{ type []E }
+type sliceOf[E any] interface{ ~[]E }
-func append[T interface{}, S sliceOf[T], T2 interface{ type T }](s S, t ...T2) S
+func append[T interface{}, S sliceOf[T], T2 interface{}](s S, t ...T2) S { panic(0) }
var f func()
var cancelSlice []context.CancelFunc
@@ -242,7 +247,7 @@ var _ = append[context.CancelFunc, []context.CancelFunc, context.CancelFunc](can
// A generic function must be instantiated with a type, not a value.
-func g[T any](T) T
+func g[T any](T) T { panic(0) }
var _ = g[int]
var _ = g[nil /* ERROR is not a type */ ]
diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.src b/src/cmd/compile/internal/types2/testdata/check/issues.src
index 21aa208cc769d2b872dbf1c46cacf1e7ae9d2382..f4b6199b82e53ed556b4daac2b6ee97cc9ff9ea8 100644
--- a/src/cmd/compile/internal/types2/testdata/check/issues.src
+++ b/src/cmd/compile/internal/types2/testdata/check/issues.src
@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package issues
+package go1_17 // don't permit non-interface elements in interfaces
import (
"fmt"
- syn "cmd/compile/internal/syntax"
+ syn "regexp/syntax"
t1 "text/template"
t2 "html/template"
)
@@ -79,11 +79,11 @@ func issue9473(a []int, b ...int) {
// Check that embedding a non-interface type in an interface results in a good error message.
func issue10979() {
type _ interface {
- int /* ERROR int is not an interface */
+ int /* ERROR non-interface type int */
}
type T struct{}
type _ interface {
- T /* ERROR T is not an interface */
+ T /* ERROR non-interface type T */
}
type _ interface {
nosuchtype /* ERROR undeclared name: nosuchtype */
@@ -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 \(method foo 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 */
@@ -280,7 +280,7 @@ type issue25301b /* ERROR cycle */ = interface {
}
type issue25301c interface {
- notE // ERROR struct\{\} is not an interface
+ notE // ERROR non-interface type struct\{\}
}
type notE = struct{}
@@ -329,10 +329,10 @@ func (... /* ERROR can only use ... with final parameter in list */ TT) f()
func issue28281g() (... /* ERROR can only use ... with final parameter in list */ TT)
// Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output
-func issue26234a(f *syn.File) {
+func issue26234a(f *syn.Prog) {
// The error message below should refer to the actual package name (syntax)
// not the local package name (syn).
- f.foo /* ERROR f.foo undefined \(type \*syntax.File has no field or method foo\) */
+ f.foo /* ERROR f\.foo undefined \(type \*syntax\.Prog has no field or method foo\) */
}
type T struct {
@@ -357,11 +357,11 @@ func issue35895() {
var _ T = 0 // ERROR cannot use 0 \(untyped int constant\) as T
// There is only one package with name syntax imported, only use the (global) package name in error messages.
- var _ *syn.File = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.File
+ var _ *syn.Prog = 0 // ERROR cannot use 0 \(untyped int constant\) as \*syntax.Prog
// Because both t1 and t2 have the same global package name (template),
// qualify packages with full path name in this case.
- var _ t1.Template = t2 /* ERROR cannot use .* \(value of type "html/template".Template\) as "text/template".Template */ .Template{}
+ var _ t1.Template = t2 /* ERROR cannot use .* \(value of type .html/template.\.Template\) as .text/template.\.Template */ .Template{}
}
func issue42989(s uint) {
diff --git a/src/cmd/compile/internal/types2/testdata/check/linalg.go2 b/src/cmd/compile/internal/types2/testdata/check/linalg.go2
index 0d27603a5837a2747922d9c1e52802b840ca8c4e..f02e773dbeeeebbafde08ab38ac4cef93f11897a 100644
--- a/src/cmd/compile/internal/types2/testdata/check/linalg.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/linalg.go2
@@ -4,15 +4,13 @@
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 {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64,
- complex64, complex128
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ ~float32 | ~float64 |
+ ~complex64 | ~complex128
}
func DotProduct[T Numeric](s1, s2 []T) T {
@@ -42,42 +40,43 @@ func AbsDifference[T NumericAbs[T]](a, b T) T {
// OrderedNumeric is a type bound that matches numeric types that support the < operator.
type OrderedNumeric interface {
- type int, int8, int16, int32, int64,
- uint, uint8, uint16, uint32, uint64, uintptr,
- float32, float64
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+ ~float32 | ~float64
}
// Complex is a type bound that matches the two complex types, which do not have a < operator.
type Complex interface {
- type 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
+ ~complex64 | ~complex128
}
-// 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/main.go2 b/src/cmd/compile/internal/types2/testdata/check/main.go2
index b7ddeaa1a881b0ec79ac318ff474819fd61ecf3e..395e3bfec8076e3eeaf09c7ddebb475d9b69dcc2 100644
--- a/src/cmd/compile/internal/types2/testdata/check/main.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/main.go2
@@ -4,4 +4,4 @@
package main
-func /* ERROR "func main must have no type parameters" */ main[T any]() {}
+func main [T /* ERROR "func main must have no type parameters" */ any]() {}
diff --git a/src/cmd/compile/internal/types2/testdata/check/map2.go2 b/src/cmd/compile/internal/types2/testdata/check/map2.go2
index 2833445662de813d6d3eeed95978c8342316b29f..be2c49f621efeff4b22a42ca683a5084224a210a 100644
--- a/src/cmd/compile/internal/types2/testdata/check/map2.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/map2.go2
@@ -114,7 +114,7 @@ func (it *Iterator[K, V]) Next() (K, V, bool) {
// chans
-func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T])
+func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T]) { panic(0) }
// A sender is used to send values to a Receiver.
type chans_Sender[T any] struct {
diff --git a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2
index c2f282bae11a9817620365e445a9bc8b3f3b310f..1b406593f888a47fc8eb7682c142c9725402a9bc 100644
--- a/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/mtypeparams.go2
@@ -10,7 +10,7 @@ package p
type S struct{}
-func (S) m[T any](v T)
+func (S) m[T any](v T) {}
// TODO(gri) Once we collect interface method type parameters
// in the parser, we can enable these tests again.
diff --git a/src/cmd/compile/internal/types2/testdata/check/stmt0.src b/src/cmd/compile/internal/types2/testdata/check/stmt0.src
index bedcbe5fce3573f8aea5c8c8b2fb73786c0a9b17..8b18d676ac258513eb68f2a7a254219c3b7ff606 100644
--- a/src/cmd/compile/internal/types2/testdata/check/stmt0.src
+++ b/src/cmd/compile/internal/types2/testdata/check/stmt0.src
@@ -29,10 +29,10 @@ func assignments0() (int, int) {
a, b, c = <- /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ ch
- return /* ERROR "wrong number of return values" */
- return /* ERROR "wrong number of return values" */ 1
+ return /* ERROR "not enough return values\n\thave \(\)\n\twant \(int, int\)" */
+ return 1 /* ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)" */
return 1, 2
- return /* ERROR "wrong number of return values" */ 1, 2, 3
+ return 1, 2, 3 /* ERROR "too many return values\n\thave \(number, number, number\)\n\twant \(int, int\)" */
}
func assignments1() {
@@ -49,18 +49,18 @@ func assignments1() {
b = true
i += 1
- i += "foo" /* ERROR "cannot convert.*int" */
+ i += "foo" /* ERROR "mismatched types int and untyped string" */
f -= 1
f /= 0
f = float32(0)/0 /* ERROR "division by zero" */
- f -= "foo" /* ERROR "cannot convert.*float64" */
+ f -= "foo" /* ERROR "mismatched types float64 and untyped string" */
c *= 1
c /= 0
s += "bar"
- s += 1 /* ERROR "cannot convert.*string" */
+ s += 1 /* ERROR "mismatched types string and untyped int" */
var u64 uint64
u64 += 1< 0 {
x0 = x[0]
@@ -118,9 +118,9 @@ func max[T interface{ type int }](x ...T) T {
// Thus even if a type can be inferred successfully, the function
// call may not be valid.
-func fboth[T any](chan T)
-func frecv[T any](<-chan T)
-func fsend[T any](chan<- T)
+func fboth[T any](chan T) {}
+func frecv[T any](<-chan T) {}
+func fsend[T any](chan<- T) {}
func _() {
var both chan int
@@ -140,9 +140,9 @@ func _() {
fsend(send)
}
-func ffboth[T any](func(chan T))
-func ffrecv[T any](func(<-chan T))
-func ffsend[T any](func(chan<- T))
+func ffboth[T any](func(chan T)) {}
+func ffrecv[T any](func(<-chan T)) {}
+func ffsend[T any](func(chan<- T)) {}
func _() {
var both func(chan int)
@@ -169,9 +169,9 @@ func _() {
// assignment is permitted, parameter passing is permitted as well,
// so type inference should be able to handle these cases well.
-func g1[T any]([]T)
-func g2[T any]([]T, T)
-func g3[T any](*T, ...T)
+func g1[T any]([]T) {}
+func g2[T any]([]T, T) {}
+func g3[T any](*T, ...T) {}
func _() {
type intSlize []int
@@ -195,7 +195,7 @@ func _() {
// Here's a realistic example.
-func append[T any](s []T, t ...T) []T
+func append[T any](s []T, t ...T) []T { panic(0) }
func _() {
var f func()
@@ -208,8 +208,12 @@ func _() {
// (that would indicate a slice type). Thus, generic functions cannot
// have empty type parameter lists, either. This is a syntax error.
-func h[] /* ERROR empty type parameter list */ ()
+func h[] /* ERROR empty type parameter list */ () {}
func _() {
h[] /* ERROR operand */ ()
}
+
+// Parameterized functions must have a function body.
+
+func _ /* ERROR missing function body */ [P any]()
diff --git a/src/cmd/compile/internal/types2/testdata/examples/inference.go2 b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
index b47ce75805480539dcdf5fbef2ff350f6fc9cafe..0732f06a39ab1c6afb9862771052cc610fcecf97 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/inference.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
@@ -7,10 +7,10 @@
package p
type Ordered interface {
- type int, float64, string
+ ~int|~float64|~string
}
-func min[T Ordered](x, y T) T
+func min[T Ordered](x, y T) T { panic(0) }
func _() {
// min can be called with explicit instantiation.
@@ -37,7 +37,7 @@ func _() {
_ = min("foo", "bar")
}
-func mixed[T1, T2, T3 any](T1, T2, T3)
+func mixed[T1, T2, T3 any](T1, T2, T3) {}
func _() {
// mixed can be called with explicit instantiation.
@@ -54,7 +54,7 @@ func _() {
mixed[int, string](1.1 /* ERROR cannot use 1.1 */ , "", false)
}
-func related1[Slice interface{type []Elem}, Elem any](s Slice, e Elem)
+func related1[Slice interface{~[]Elem}, Elem any](s Slice, e Elem) {}
func _() {
// related1 can be called with explicit instantiation.
@@ -78,7 +78,7 @@ func _() {
related1(si, "foo" /* ERROR cannot use "foo" */ )
}
-func related2[Elem any, Slice interface{type []Elem}](e Elem, s Slice)
+func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
func _() {
// related2 can be called with explicit instantiation.
@@ -97,5 +97,28 @@ func _() {
// last.
related2(1.2, []float64{})
related2(1.0, []int{})
- related2( /* ERROR does not satisfy */ float64(1.0), []int{}) // TODO(gri) fix error position
+ related2( /* ERROR does not implement */ 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 76c6539e1b7e7de019b7031025274eee082be8d7..1d76d553dcc44a1b57f2f983edefa404c39ab534 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/methods.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/methods.go2
@@ -94,3 +94,19 @@ func (_ T2[_, _, _]) _() int { return 42 }
type T0 struct{}
func (T0) _() {}
func (T1[A]) _() {}
+
+// 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/operations.go2 b/src/cmd/compile/internal/types2/testdata/examples/operations.go2
new file mode 100644
index 0000000000000000000000000000000000000000..18e4d6080c59f0307c55e94bf389e4109eb88a7f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/operations.go2
@@ -0,0 +1,29 @@
+// 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
+
+// indirection
+
+func _[P any](p P) {
+ _ = *p // ERROR cannot indirect p
+}
+
+func _[P interface{ int }](p P) {
+ _ = *p // ERROR cannot indirect p
+}
+
+func _[P interface{ *int }](p P) {
+ _ = *p
+}
+
+func _[P interface{ *int | *string }](p P) {
+ _ = *p // ERROR must have identical base types
+}
+
+type intPtr *int
+
+func _[P interface{ *int | intPtr } ](p P) {
+ var _ int = *p
+}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/types.go2 b/src/cmd/compile/internal/types2/testdata/examples/types.go2
index a7825ed2d9bf7c421c23c9fb78549ce889d2fe91..077fcfdbb76119caf45f104d6751e648347512b7 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/types.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/types.go2
@@ -102,6 +102,7 @@ func _() {
// Generic types cannot be used without instantiation.
var _ T // ERROR cannot use generic type T
+var _ = T /* ERROR cannot use generic type T */ (0)
// In type context, generic (parameterized) types cannot be parenthesized before
// being instantiated. See also NOTES entry from 12/4/2019.
@@ -113,7 +114,7 @@ type I1[T any] interface{
}
// There is no such thing as a variadic generic type.
-type _[T ... /* ERROR invalid use of ... */ interface{}] struct{}
+type _[T ... /* ERROR invalid use of ... */ any] struct{}
// Generic interfaces may be embedded as one would expect.
type I2 interface {
@@ -155,31 +156,42 @@ type _ struct {
List /* ERROR List redeclared */ [int]
}
+// Issue #45639: We don't allow this anymore. Keep this code
+// in case we decide to revisit this decision.
+//
// It's possible to declare local types whose underlying types
// are type parameters. As with ordinary type definitions, the
// types underlying properties are "inherited" but the methods
// are not.
-func _[T interface{ m(); type int }]() {
- type L T
- var x L
-
- // m is not defined on L (it is not "inherited" from
- // its underlying type).
- x.m /* ERROR x.m undefined */ ()
-
- // But the properties of T, such that as that it supports
- // the operations of the types given by its type bound,
- // are also the properties of L.
- x++
- _ = x - x
-
- // On the other hand, if we define a local alias for T,
- // that alias stands for T as expected.
- type A = T
- var y A
- y.m()
- _ = y < 0
-}
+// func _[T interface{ m(); ~int }]() {
+// type L T
+// var x L
+//
+// // m is not defined on L (it is not "inherited" from
+// // its underlying type).
+// x.m /* ERROR x.m undefined */ ()
+//
+// // But the properties of T, such that as that it supports
+// // the operations of the types given by its type bound,
+// // are also the properties of L.
+// x++
+// _ = x - x
+//
+// // On the other hand, if we define a local alias for T,
+// // that alias stands for T as expected.
+// type A = T
+// var y A
+// y.m()
+// _ = y < 0
+// }
+
+// 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
@@ -202,19 +214,19 @@ func Sum[T Adder[T]](list []T) T {
}
// Valid and invalid variations.
-type B0 interface {}
-type B1[_ any] interface{}
-type B2[_, _ any] interface{}
+type B0 any
+type B1[_ any] any
+type B2[_, _ any] any
-func _[T1 B0]()
-func _[T1 B1[T1]]()
-func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+func _[T1 B0]() {}
+func _[T1 B1[T1]]() {}
+func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {}
-func _[T1, T2 B0]()
-func _[T1 B1[T1], T2 B1[T2]]()
-func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+func _[T1, T2 B0]() {}
+func _[T1 B1[T1], T2 B1[T2]]() {}
+func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]() {}
-func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2
+func _[T1 B0, T2 B1[T2]]() {} // here B1 applies to T2
// When the type argument is left away, the type bound is
// instantiated for each type parameter with that type
@@ -232,11 +244,11 @@ func _[A Adder[A], B Adder[B], C Adder[A]]() {
// The type of variables (incl. parameters and return values) cannot
// be an interface with type constraints or be/embed comparable.
type I interface {
- type int
+ ~int
}
var (
- _ interface /* ERROR contains type constraints */ {type int}
+ _ interface /* ERROR contains type constraints */ {~int}
_ I /* ERROR contains type constraints */
)
@@ -264,10 +276,10 @@ 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 { type int, float64 }]() {
+func _[T interface{~int|~float64}]() {
// not valid
const _ = T /* ERROR not constant */ (0)
const _ T /* ERROR invalid constant type T */ = 1
@@ -277,3 +289,27 @@ func _[T interface { type int, float64 }]() {
var _ T = 1
_ = T(0)
}
+
+// It is possible to create composite literals of type parameter
+// type as long as it's possible to create a composite literal
+// of the structural type of the type parameter's constraint.
+func _[P interface{ ~[]int }]() P {
+ return P{}
+ return P{1, 2, 3}
+}
+
+func _[P interface{ ~[]E }, E interface{ map[string]P } ]() P {
+ x := P{}
+ return P{{}}
+ return P{E{}}
+ 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/examples/typesets.go2 b/src/cmd/compile/internal/types2/testdata/examples/typesets.go2
new file mode 100644
index 0000000000000000000000000000000000000000..e19dcf8da378f3e1f4b1748d5a46ec092c057c0e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/typesets.go2
@@ -0,0 +1,60 @@
+// 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 shows some examples of constraint literals with elided interfaces.
+// These examples are permitted if proposal issue #48424 is accepted.
+
+package p
+
+// Constraint type sets of the form T, ~T, or A|B may omit the interface.
+type (
+ _[T int] struct{}
+ _[T ~int] struct{}
+ _[T int|string] struct{}
+ _[T ~int|~string] struct{}
+)
+
+func min[T int|string](x, y T) T {
+ if x < y {
+ return x
+ }
+ return y
+}
+
+func lookup[M ~map[K]V, K comparable, V any](m M, k K) V {
+ return m[k]
+}
+
+func deref[P ~*E, E any](p P) E {
+ return *p
+}
+
+func _() int {
+ p := new(int)
+ return deref(p)
+}
+
+func addrOfCopy[V any, P ~*V](v V) P {
+ return &v
+}
+
+func _() *int {
+ return addrOfCopy(0)
+}
+
+// A type parameter may not be embedded in an interface;
+// so it can also not be used as a constraint.
+func _[A any, B A /* ERROR cannot use a type parameter as constraint */ ]() {}
+func _[A any, B, C A /* ERROR cannot use a type parameter as constraint */ ]() {}
+
+
+// Error messages refer to the type constraint as it appears in the source.
+// (No implicit interface should be exposed.)
+func _[T string](x T) T {
+ return x /* ERROR constrained by string */ * x
+}
+
+func _[T int|string](x T) T {
+ return x /* ERROR constrained by int|string */ * x
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
index 2c1299feb09519d7499d09d937abf4abfb3f00bd..9a98f7f955e8c24e3e2eaf4d0ead54099a96c51c 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39634.go2
@@ -31,13 +31,14 @@ type x7[A any] struct{ foo7 }
func main7() { var _ foo7 = x7[int]{} }
// crash 8
-type foo8[A any] interface { type A }
-func bar8[A foo8[A]](a A) {}
-func main8() {}
+// Embedding stand-alone type parameters is not permitted for now. Disabled.
+// type foo8[A any] interface { ~A }
+// func bar8[A foo8[A]](a A) {}
+// func main8() {}
// crash 9
-type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] }
-func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [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 */
@@ -49,7 +50,7 @@ func (G15 /* ERROR generic type .* without instantiation */ ) p()
// crash 16
type Foo16[T any] r16 /* ERROR not a type */
-func r16[T any]() Foo16[Foo16[T]]
+func r16[T any]() Foo16[Foo16[T]] { panic(0) }
// crash 17
type Y17 interface{ c() }
@@ -57,7 +58,7 @@ type Z17 interface {
c() Y17
Y17 /* ERROR duplicate method */
}
-func F17[T Z17](T)
+func F17[T Z17](T) {}
// crash 18
type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ])
@@ -73,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
@@ -84,8 +86,8 @@ var x T25 /* ERROR without instantiation */ .m1
// crash 26
type T26 = interface{ F26[ /* ERROR cannot have type parameters */ Z any]() }
-func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ /* ERROR missing method */ [] /* ERROR operand */ }
+func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ [] /* ERROR operand */ }
// crash 27
-func e27[T any]() interface{ x27 /* ERROR not a type */ }
+func e27[T any]() interface{ x27 /* ERROR not a type */ } { panic(0) }
func x27() { e27( /* ERROR cannot infer T */ ) }
\ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2
index 9bc26f35461345eb2f0d5728ba39fe793774723d..e56bc3547582d190f110b6f70a231e9e38012572 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39680.go2
@@ -4,16 +4,19 @@
package p
+// Embedding stand-alone type parameters is not permitted for now. Disabled.
+
+/*
import "fmt"
// Minimal test case.
-func _[T interface{type T}](x T) T{
+func _[T interface{~T}](x T) T{
return x
}
// Test case from issue.
type constr[T any] interface {
- type T
+ ~T
}
func Print[T constr[T]](s []T) {
@@ -25,3 +28,4 @@ func Print[T constr[T]](s []T) {
func f() {
Print([]string{"Hello, ", "playground\n"})
}
+*/
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2
index 316ab1982e89cf3635b1b1b16b3356f70a4d2b4f..301c13be41cce2b5fce7cec228cac0d9f1504f63 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39693.go2
@@ -4,11 +4,20 @@
package p
-type Number interface {
- int /* ERROR int is not an interface */
- float64 /* ERROR float64 is not an interface */
+type Number1 interface {
+ // embedding non-interface types is permitted
+ int
+ float64
}
-func Add[T Number](a, b T) T {
+func Add1[T Number1](a, b T) T {
return a /* ERROR not defined */ + b
}
+
+type Number2 interface {
+ int|float64
+}
+
+func Add2[T Number2](a, b T) T {
+ return a + b
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2
index 75491e7e26f00cd5b1d0ce771d471724ae7cdf9e..72f83997c2460a5496556cb1d294b9e260550c5d 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39699.go2
@@ -8,7 +8,7 @@ type T0 interface{
}
type T1 interface{
- type int
+ ~int
}
type T2 interface{
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
index df621a4c1730db66648dd23c898a1238bdfce332..8f3101235410c091910641c928113718e66d2bc4 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39711.go2
@@ -4,8 +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 {
- type interface{ Error() string }, interface{ String() string }
+ // TODO(gri) Once we have full type sets we can enable this again.
+ // 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/issue39723.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2
index 55464e6b7759f09016050e541868a72b50e1dca1..00885238e69c9a26ce5ef6f44539c0cba86152b1 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39723.go2
@@ -6,4 +6,4 @@ package p
// A constraint must be an interface; it cannot
// be a type parameter, for instance.
-func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]()
+func _[A interface{ ~int }, B A /* ERROR cannot use a type parameter as constraint */ ]() {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2
index e19b6770bfe4bd21462434f97ace29659a02c4ed..62dc45a59601eecc43e0b23fd09123cf00fbf57d 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39725.go2
@@ -4,13 +4,13 @@
package p
-func f1[T1, T2 any](T1, T2, struct{a T1; b T2})
+func f1[T1, T2 any](T1, T2, struct{a T1; b T2}) {}
func _() {
f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{})
}
// simplified test case from issue
-func f2[T any](_ []T, _ func(T))
+func f2[T any](_ []T, _ func(T)) {}
func _() {
f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {})
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2
index f70b8d0ce0388757004f3797bac5cae749429177..9edd239d7d97c2e3e00bd6031327f180939494b0 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39754.go2
@@ -16,8 +16,6 @@ func f[V interface{}, A, B Box[V]]() {}
func _() {
f[int, Optional[int], Optional[int]]()
- _ = f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]]
- // TODO(gri) Provide better position information here.
- // See TODO in call.go, Checker.arguments.
- f[int, Optional[int], Optional[string]]( /* ERROR does not satisfy Box */ )
+ _ = f[int, Optional[int], Optional /* ERROR does not implement Box */ [string]]
+ _ = f[int, Optional[int], Optional /* ERROR Optional.* does not implement Box.* */ [string]]
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2
index b7ab68818e9587897e7f916730644a3dc1f0af8b..257b73a2fbeff15a934f4daf4798952578bad9d5 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39755.go2
@@ -4,14 +4,14 @@
package p
-func _[T interface{type map[string]int}](x T) {
+func _[T interface{~map[string]int}](x T) {
_ = x == nil
}
// simplified test case from issue
type PathParamsConstraint interface {
- type map[string]string, []struct{key, value string}
+ ~map[string]string | ~[]struct{key, value string}
}
type PathParams[T PathParamsConstraint] struct {
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
index abac141d7f08dd359a26ba18c36b696d7970d40a..696d9d9beef9d2f9961456e1ddf3ed6f31c51889 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39768.go2
@@ -4,17 +4,18 @@
package p
-type T[P any] P
-type A = T
-var x A[int]
-var _ A /* ERROR cannot use generic type */
-
-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
type Vector[T any] []T
-type VectorAlias = Vector
+type VectorAlias = Vector // ERROR cannot use generic type
var v Vector[int]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39938.go2
index 76e7e369ca12bb6718272a2de41dfc7f6617d848..114646786d5b74324413b74cbbca0c211f3c6cb2 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 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/issue39948.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2
index c2b460902cc643b5537834e489f3213f551fe6f7..e38e57268d63d1fd00012274bd8ea23bc2a7c7df 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39948.go2
@@ -5,5 +5,5 @@
package p
type T[P any] interface{
- P // ERROR P is a type parameter, not an interface
+ P // ERROR cannot embed a type parameter
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2
index 3db4eae0123930db81b5ff193e58ff802678878a..d703da90a2176a3e975f5a62cad5cf5b3f665faf 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue39976.go2
@@ -7,7 +7,7 @@ package p
type policy[K, V any] interface{}
type LRU[K, V any] struct{}
-func NewCache[K, V any](p policy[K, V])
+func NewCache[K, V any](p policy[K, V]) {}
func _() {
var lru LRU[int, string]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2
index 8948d61caa477fff40ac7f773946d0377df734e4..0981a335da3aa91cc12538ddc976f1e34e428e6d 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40038.go2
@@ -8,7 +8,7 @@ type A[T any] int
func (A[T]) m(A[T])
-func f[P interface{m(P)}]()
+func f[P interface{m(P)}]() {}
func _() {
_ = f[A[int]]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2
index 747aab49dd1639b79bbf7eb0efcf84787b337c4c..a3f3eecca098c6dbbace1dbc19eee13069484b6c 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40056.go2
@@ -10,6 +10,6 @@ func _() {
type S struct {}
-func NewS[T any]() *S
+func NewS[T any]() *S { panic(0) }
func (_ *S /* ERROR S is not a generic type */ [T]) M()
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2
index 5d97855f8a172ff414ae86c5112b500059a3142b..c78f9a1fa040892e7ebec27753100db2867752b2 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40301.go2
@@ -7,6 +7,6 @@ package p
import "unsafe"
func _[T any](x T) {
- _ = unsafe /* ERROR undefined */ .Alignof(x)
- _ = unsafe /* ERROR undefined */ .Sizeof(x)
+ _ = unsafe.Alignof(x)
+ _ = unsafe.Sizeof(x)
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2
index 0269c3a62ce6d0d10d92f0f2cb4e0ce8e82129cf..58d0f69f6503164328feb8990baa1d4bbf201c52 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40684.go2
@@ -6,8 +6,8 @@ package p
type T[_ any] int
-func f[_ any]()
-func g[_, _ any]()
+func f[_ any]() {}
+func g[_, _ any]() {}
func _() {
_ = f[T /* ERROR without instantiation */ ]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2
new file mode 100644
index 0000000000000000000000000000000000000000..9eea4ad60a6647380606f70fd998febab618db8c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue40789.go2
@@ -0,0 +1,37 @@
+// 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"
+
+func main() {
+ m := map[string]int{
+ "a": 6,
+ "b": 7,
+ }
+ fmt.Println(copyMap[map[string]int, string, int](m))
+}
+
+type Map[K comparable, V any] interface {
+ map[K] V
+}
+
+func copyMap[M Map[K, V], K comparable, V any](m M) M {
+ m1 := make(M)
+ for k, v := range m {
+ m1[k] = v
+ }
+ return m1
+}
+
+// simpler test case from the same issue
+
+type A[X comparable] interface {
+ []X
+}
+
+func f[B A[X], X comparable]() B {
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
index 61f766bcbd7891ef16f8ca6396349e1ff43159f6..7f55ba85a6b42d4580a030337e5bba3e1da71713 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
@@ -6,13 +6,13 @@ package p
// Test case from issue.
-type Nat interface {
- type Zero, Succ
+type Nat /* ERROR cycle */ interface {
+ Zero|Succ
}
type Zero struct{}
type Succ struct{
- Nat // ERROR interface contains type constraints
+ Nat // Nat contains type constraints but is invalid, so no error
}
// Struct tests.
@@ -22,7 +22,7 @@ type I1 interface {
}
type I2 interface {
- type int
+ ~int
}
type I3 interface {
@@ -47,7 +47,7 @@ type _ struct{
}
type _ struct{
- I3 // ERROR interface contains type constraints
+ I3 // ERROR interface is .* comparable
}
// General composite types.
@@ -59,19 +59,19 @@ type (
_ []I1 // ERROR interface is .* comparable
_ []I2 // ERROR interface contains type constraints
- _ *I3 // ERROR interface contains type constraints
+ _ *I3 // ERROR interface is .* comparable
_ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
- _ chan I3 // ERROR interface contains type constraints
+ _ chan I3 // ERROR interface is .* comparable
_ func(I1 /* ERROR interface is .* comparable */ )
_ func() I2 // ERROR interface contains type constraints
)
// Other cases.
-var _ = [...]I3 /* ERROR interface contains type constraints */ {}
+var _ = [...]I3 /* ERROR interface is .* comparable */ {}
func _(x interface{}) {
- _ = x.(I3 /* ERROR interface contains type constraints */ )
+ _ = x.(I3 /* ERROR interface is .* comparable */ )
}
type T1[_ any] struct{}
@@ -79,9 +79,9 @@ type T3[_, _, _ any] struct{}
var _ T1[I2 /* ERROR interface contains type constraints */ ]
var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32]
-func f1[_ any]() int
+func f1[_ any]() int { panic(0) }
var _ = f1[I2 /* ERROR interface contains type constraints */ ]()
-func f3[_, _, _ any]() int
+func f3[_, _, _ any]() int { panic(0) }
var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]()
func _(x interface{}) {
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2
index 698cb8a16bad26194a40f5893e7a1d493b30083c..dd66e9648b56326f364b4ade15b459a506a5bb9d 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue42758.go2
@@ -17,7 +17,7 @@ func _[T any](x interface{}){
}
type constraint interface {
- type int
+ ~int
}
func _[T constraint](x interface{}){
@@ -28,6 +28,6 @@ func _[T constraint](x interface{}){
}
func _(x constraint /* ERROR contains type constraints */ ) {
- switch x /* ERROR contains type constraints */ .(type) {
+ switch x.(type) { // no need to report another error
}
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2
new file mode 100644
index 0000000000000000000000000000000000000000..e4bcee51fe5f90420af7c385a53e748f485a4edc
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43527.go2
@@ -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.
+
+package p
+
+const L = 10
+
+type (
+ _ [L]struct{}
+ _ [A /* ERROR undeclared name A for array length */ ]struct{}
+ _ [B /* ERROR not an expression */ ]struct{}
+ _[A any] struct{}
+
+ B int
+)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43671.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43671.go2
new file mode 100644
index 0000000000000000000000000000000000000000..46ac51ebdd2df27b2007ce66c953b460e693c4e8
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue43671.go2
@@ -0,0 +1,58 @@
+// 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 C0 interface{ int }
+type C1 interface{ chan int }
+type C2 interface{ chan int | <-chan int }
+type C3 interface{ chan int | chan float32 }
+type C4 interface{ chan int | chan<- int }
+type C5[T any] interface{ ~chan T | <-chan T }
+
+func _[T any](ch T) {
+ <-ch // ERROR cannot receive from ch .* no structural type
+}
+
+func _[T C0](ch T) {
+ <-ch // ERROR cannot receive from non-channel ch
+}
+
+func _[T C1](ch T) {
+ <-ch
+}
+
+func _[T C2](ch T) {
+ <-ch
+}
+
+func _[T C3](ch T) {
+ <-ch // ERROR cannot receive from ch .* no structural type
+}
+
+func _[T C4](ch T) {
+ <-ch // ERROR cannot receive from send-only channel
+}
+
+func _[T C5[X], X any](ch T, x X) {
+ x = <-ch
+}
+
+// test case from issue, slightly modified
+type RecvChan[T any] interface {
+ ~chan T | ~<-chan T
+}
+
+func _[T any, C RecvChan[T]](ch C) T {
+ return <-ch
+}
+
+func f[T any, C interface{ chan T }](ch C) T {
+ return <-ch
+}
+
+func _(ch chan int) {
+ var x int = f(ch) // test constraint type inference for this case
+ _ = x
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2
index b1e42497e8586c500d11985f0df6c70249927949..b8ba0ad4a70915b4edaca42bb24ca60f1088a2bd 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45548.go2
@@ -4,7 +4,7 @@
package p
-func f[F interface{type *Q}, G interface{type *R}, Q, R any](q Q, r R) {}
+func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
func _() {
f[*float64, *int](1, 2)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45550.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45550.go2
new file mode 100644
index 0000000000000000000000000000000000000000..3eeaca0957a9bd54ee9dfcdd253e68d6a37f3fd9
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45550.go2
@@ -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
+
+type Builder /* ERROR illegal cycle */ [T interface{ struct{ Builder[T] } }] struct{}
+type myBuilder struct {
+ Builder[myBuilder]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2
index 65662cdc7662938f72e41f4770149277cbdaedda..29379591057a6148a38cc0ff6ede246c527ee79e 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45635.go2
@@ -13,7 +13,7 @@ type N[T any] struct{}
var _ N[] /* ERROR expecting type */
type I interface {
- type map[int]int, []int
+ ~[]int
}
func _[T I](i, j int) {
@@ -27,6 +27,5 @@ func _[T I](i, j int) {
_ = s[i, j /* ERROR more than one index */ ]
var t T
- // TODO(gri) fix multiple error below
- _ = t[i, j /* ERROR more than one index */ /* ERROR more than one index */ ]
+ _ = t[i, j /* ERROR more than one index */ ]
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
new file mode 100644
index 0000000000000000000000000000000000000000..80148fe4819862f4761eb439da062e735204aa20
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
@@ -0,0 +1,13 @@
+// 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
+
+// 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/issue45920.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45920.go2
new file mode 100644
index 0000000000000000000000000000000000000000..b113e104bc8e5ee5f84608aa287101965a6671ae
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45920.go2
@@ -0,0 +1,17 @@
+// 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 f1[T any, C chan T | <-chan T](ch C) {}
+
+func _(ch chan int) { f1(ch) }
+func _(ch <-chan int) { f1(ch) }
+func _(ch chan<- int) { f1( /* ERROR chan<- int does not implement chan int\|<-chan int */ ch) }
+
+func f2[T any, C chan T | chan<- T](ch C) {}
+
+func _(ch chan int) { f2(ch) }
+func _(ch <-chan int) { f2( /* ERROR <-chan int does not implement chan int\|chan<- int */ ch) }
+func _(ch chan<- int) { f2(ch) }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
index 7678e348ef99ce2733eaa741fcd3e3b1d13e0e46..cea8c1498321c7182c490d262cd9b4f58a03190f 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45985.go2
@@ -4,11 +4,10 @@
package issue45985
-// TODO(gri): this error should be on app[int] below.
-func app[S /* ERROR "type S = S does not match" */ interface{ type []T }, T any](s S, e T) S {
+func app[S interface{ ~[]T }, T any](s S, e T) S {
return append(s, e)
}
func _() {
- _ = app[int]
+ _ = app[/* ERROR "S does not match" */int]
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2
new file mode 100644
index 0000000000000000000000000000000000000000..81b31974c8dc0207e0833d51c845b978ce26cef4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46090.go2
@@ -0,0 +1,9 @@
+// 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.
+
+// The predeclared type comparable is not visible before Go 1.18.
+
+package go1_17
+
+type _ comparable // ERROR undeclared
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46275.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46275.go2
new file mode 100644
index 0000000000000000000000000000000000000000..f41ae26e4bdda6fd5815434d0c96887006154a9b
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46275.go2
@@ -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 issue46275
+
+type N[T any] struct {
+ *N[T]
+ t T
+}
+
+func (n *N[T]) Elem() T {
+ return n.t
+}
+
+type I interface {
+ Elem() string
+}
+
+func _() {
+ var n1 *N[string]
+ var _ I = n1
+ type NS N[string]
+ var n2 *NS
+ var _ I = n2
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46461.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46461.go2
new file mode 100644
index 0000000000000000000000000000000000000000..4432402a300e9a4cec961fa762d81d0b6317c6d0
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46461.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
+
+// test case 1
+type T /* ERROR illegal cycle */ [U interface{ M() T[U] }] int
+
+type X int
+
+func (X) M() T[X] { return 0 }
+
+// test case 2
+type A /* ERROR illegal cycle */ [T interface{ A[T] }] interface{}
+
+// test case 3
+type A2 /* ERROR illegal cycle */ [U interface{ A2[U] }] interface{ M() A2[U] }
+
+type I interface{ A2[I]; M() A2[I] }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46583.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46583.src
new file mode 100644
index 0000000000000000000000000000000000000000..da1f1ffbbaf6c2d29054d9d65f9226199e426363
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue46583.src
@@ -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 p
+
+type T1 struct{}
+func (t T1) m(int) {}
+var f1 func(T1)
+
+type T2 struct{}
+func (t T2) m(x int) {}
+var f2 func(T2)
+
+type T3 struct{}
+func (T3) m(int) {}
+var f3 func(T3)
+
+type T4 struct{}
+func (T4) m(x int) {}
+var f4 func(T4)
+
+func _() {
+ f1 = T1 /* ERROR func\(T1, int\) */ .m
+ f2 = T2 /* ERROR func\(t T2, x int\) */ .m
+ f3 = T3 /* ERROR func\(T3, int\) */ .m
+ f4 = T4 /* ERROR func\(_ T4, x int\) */ .m
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47031.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47031.go2
new file mode 100644
index 0000000000000000000000000000000000000000..b184f9b5b7dfbf7255d9bd6587fb78acce371fb6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47031.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 Mer interface { M() }
+
+func F[T Mer](p *T) {
+ p.M /* ERROR p\.M undefined */ ()
+}
+
+type MyMer int
+
+func (MyMer) M() {}
+
+func _() {
+ F(new(MyMer))
+ F[Mer](nil)
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47115.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47115.go2
new file mode 100644
index 0000000000000000000000000000000000000000..83a8f3a5da64356cf10b747692018c379e712939
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47115.go2
@@ -0,0 +1,40 @@
+// 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 C0 interface{ int }
+type C1 interface{ chan int }
+type C2 interface{ chan int | <-chan int }
+type C3 interface{ chan int | chan float32 }
+type C4 interface{ chan int | chan<- int }
+type C5[T any] interface{ ~chan T | chan<- T }
+
+func _[T any](ch T) {
+ ch /* ERROR cannot send to ch .* no structural type */ <- 0
+}
+
+func _[T C0](ch T) {
+ ch /* ERROR cannot send to non-channel */ <- 0
+}
+
+func _[T C1](ch T) {
+ ch <- 0
+}
+
+func _[T C2](ch T) {
+ ch /* ERROR cannot send to receive-only channel */ <- 0
+}
+
+func _[T C3](ch T) {
+ ch /* ERROR cannot send to ch .* no structural type */ <- 0
+}
+
+func _[T C4](ch T) {
+ ch <- 0
+}
+
+func _[T C5[X], X any](ch T, x X) {
+ ch <- x
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go2
new file mode 100644
index 0000000000000000000000000000000000000000..108d600a38a6c9ce9075ca792ab7abf4077f1d8e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47127.go2
@@ -0,0 +1,37 @@
+// 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.
+
+// Embedding of stand-alone type parameters is not permitted.
+
+package p
+
+type (
+ _[P any] interface{ *P | []P | chan P | map[string]P }
+ _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }
+)
+
+func _[P any]() {
+ type (
+ _[P any] interface{ *P | []P | chan P | map[string]P }
+ _[P any] interface{ P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ ~P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ int | P /* ERROR "cannot embed a type parameter" */ }
+ _[P any] interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }
+
+ _ interface{ *P | []P | chan P | map[string]P }
+ _ interface{ P /* ERROR "cannot embed a type parameter" */ }
+ _ interface{ ~P /* ERROR "cannot embed a type parameter" */ }
+ _ interface{ int | P /* ERROR "cannot embed a type parameter" */ }
+ _ interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }
+ )
+}
+
+func _[P any, Q interface{ *P | []P | chan P | map[string]P }]() {}
+func _[P any, Q interface{ P /* ERROR "cannot embed a type parameter" */ }]() {}
+func _[P any, Q interface{ ~P /* ERROR "cannot embed a type parameter" */ }]() {}
+func _[P any, Q interface{ int | P /* ERROR "cannot embed a type parameter" */ }]() {}
+func _[P any, Q interface{ int | ~P /* ERROR "cannot embed a type parameter" */ }]() {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2
new file mode 100644
index 0000000000000000000000000000000000000000..ce5db0a615ff0622d783efc8d95b28aa8f4110ef
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47411.go2
@@ -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 p
+
+func f[_ comparable]() {}
+func g[_ interface{interface{comparable; ~int|~string}}]() {}
+
+func _[P comparable,
+ Q interface{ comparable; ~int|~string },
+ R any, // not comparable
+ S interface{ comparable; ~func() }, // not comparable
+]() {
+ _ = f[int]
+ _ = f[P]
+ _ = f[Q]
+ _ = f[func( /* ERROR does not implement comparable */ )]
+ _ = f[R /* ERROR empty interface R does not implement comparable */ ]
+
+ _ = g[int]
+ _ = g[P /* ERROR P does not implement interface{interface{comparable; ~int\|~string} */ ]
+ _ = g[Q]
+ _ = g[func( /* ERROR does not implement comparable */ )]
+ _ = g[R /* ERROR empty interface R does not implement interface{interface{comparable; ~int\|~string} */ ]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
new file mode 100644
index 0000000000000000000000000000000000000000..6a2e787bf99848bc662654db7b6dd97b55bcec93
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
@@ -0,0 +1,71 @@
+// 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
+
+// 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) {
+ x.m()
+ // (&x).m doesn't exist because &x is of type *P
+ // and pointers to type parameters don't have methods
+ (&x).m /* ERROR \*P has no field or method m */ ()
+}
+
+
+type T2 interface{ m() }
+
+func _(x *T2) {
+ // x.m doesn't exists because x is of type *T2
+ // and pointers to interfaces don't have methods
+ x.m /* ERROR \*T2 has no field or method m */()
+}
+
+// Test case 1 from issue
+
+type Fooer1[t any] interface {
+ Foo(Barer[t])
+}
+type Barer[t any] interface {
+ Bar(t)
+}
+
+// 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
+
+// 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/issue47796.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47796.go2
new file mode 100644
index 0000000000000000000000000000000000000000..6667ba4fec08551412c380b4cc0a34d413625ca6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47796.go2
@@ -0,0 +1,33 @@
+// 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
+
+// parameterized types with self-recursive constraints
+type (
+ T1 /* ERROR illegal cycle */ [P T1[P]] interface{}
+ T2 /* ERROR illegal cycle */ [P, Q T2[P, Q]] interface{}
+ T3[P T2[P, Q], Q interface{ ~string }] interface{}
+
+ T4a /* ERROR illegal cycle */ [P T4a[P]] interface{ ~int }
+ T4b /* ERROR illegal cycle */ [P T4b[int]] interface{ ~int }
+ T4c /* ERROR illegal cycle */ [P T4c[string]] interface{ ~int }
+
+ // mutually recursive constraints
+ T5 /* ERROR illegal cycle */ [P T6[P]] interface{ int }
+ T6[P T5[P]] interface{ int }
+)
+
+// verify that constraints are checked as expected
+var (
+ _ T1[int]
+ _ T2[int, string]
+ _ T3[int, string]
+)
+
+// test case from issue
+
+type Eq /* ERROR illegal cycle */ [a Eq[a]] interface {
+ Equal(that a) bool
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2
new file mode 100644
index 0000000000000000000000000000000000000000..2631118baed94a7f8163693a3f928c95c080a778
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47818.go2
@@ -0,0 +1,61 @@
+// 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.
+
+// Parser accepts type parameters but the type checker
+// needs to report any operations that are not permitted
+// before Go 1.18.
+
+package go1_17
+
+import "constraints"
+
+type T[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ] struct{}
+
+// for init (and main, but we're not in package main) we should only get one error
+func init[P /* ERROR func init must have no type parameters */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ]() {}
+func main[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ]() {}
+
+func f[P /* ERROR type parameters require go1\.18 or later */ any /* ERROR undeclared name: any \(requires version go1\.18 or later\) */ ](x P) {
+ var _ T[ /* ERROR type instantiation requires go1\.18 or later */ int]
+ var _ (T[ /* ERROR type instantiation requires go1\.18 or later */ int])
+ _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int]{}
+ _ = T[ /* ERROR type instantiation requires go1\.18 or later */ int](struct{}{})
+}
+
+func (T[ /* ERROR type instantiation requires go1\.18 or later */ P]) g(x int) {
+ f[ /* ERROR function instantiation requires go1\.18 or later */ int](0) // explicit instantiation
+ (f[ /* ERROR function instantiation requires go1\.18 or later */ int])(0) // parentheses (different code path)
+ f( /* ERROR implicit function instantiation requires go1\.18 or later */ x) // implicit instantiation
+}
+
+type C1 interface {
+ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+}
+
+type C2 interface {
+ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ int // ERROR embedding non-interface type int requires go1\.18 or later
+ ~ /* ERROR embedding interface element ~int requires go1\.18 or later */ int
+ int /* ERROR embedding interface element int\|~string requires go1\.18 or later */ | ~string
+}
+
+type _ interface {
+ // errors for these were reported with their declaration
+ C1
+ C2
+}
+
+type (
+ _ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ // errors for these were reported with their declaration
+ _ C1
+ _ C2
+
+ _ = comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
+ // errors for these were reported with their declaration
+ _ = C1
+ _ = C2
+)
+
+type Ordered constraints /* ERROR using type constraint constraints\.Ordered requires go1\.18 or later */ .Ordered
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2
new file mode 100644
index 0000000000000000000000000000000000000000..4c4fc2fda8f8a3140ae9f9b540eb5b7a0781f2e2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47887.go2
@@ -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 p
+
+type Fooer[t any] interface {
+ foo(Barer[t])
+}
+type Barer[t any] interface {
+ bar(Bazer[t])
+}
+type Bazer[t any] interface {
+ Fooer[t]
+ baz(t)
+}
+
+type Int int
+
+func (n Int) baz(int) {}
+func (n Int) foo(b Barer[int]) { b.bar(n) }
+
+type F[t any] interface { f(G[t]) }
+type G[t any] interface { g(H[t]) }
+type H[t any] interface { F[t] }
+
+type T struct{}
+func (n T) f(b G[T]) { b.g(n) }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2
new file mode 100644
index 0000000000000000000000000000000000000000..711e50a55a1d5eeb9661ff259a03ea78618dd82e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47968.go2
@@ -0,0 +1,21 @@
+// 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 T[P any] struct{}
+
+func (T[P]) m1()
+
+type A1 = T // ERROR cannot use generic type
+
+func (A1[P]) m2() {}
+
+type A2 = T[int]
+
+func (A2 /* ERROR cannot define methods on instantiated type T\[int\] */) m3() {}
+func (_ /* ERROR cannot define methods on instantiated type T\[int\] */ A2) m4() {}
+
+func (T[int]) m5() {} // int is the type parameter name, not an instantiation
+func (T[* /* ERROR must be an identifier */ int]) m6() {} // syntax error
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2
new file mode 100644
index 0000000000000000000000000000000000000000..2c4b6610fecb69b9f3be3de784631648fa34caf6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47996.go2
@@ -0,0 +1,8 @@
+// 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
+
+// don't crash
+func T /* ERROR missing */ [P] /* ERROR missing */ m /* ERROR unexpected */ () /* ERROR \) */ { /* ERROR { */ } /* ERROR } */
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48008.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48008.go2
new file mode 100644
index 0000000000000000000000000000000000000000..6c14c78e4c9cfc3a56bf285c8af9a1f929f8fa27
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48008.go2
@@ -0,0 +1,60 @@
+// 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 T[P any] struct{}
+
+func _(x interface{}) {
+ switch x.(type) {
+ case nil:
+ case int:
+
+ case T[int]:
+ case []T[int]:
+ case [10]T[int]:
+ case struct{T[int]}:
+ case *T[int]:
+ case func(T[int]):
+ case interface{m(T[int])}:
+ case map[T[int]] string:
+ case chan T[int]:
+
+ case T /* ERROR cannot use generic type T\[P any\] without instantiation */ :
+ case []T /* ERROR cannot use generic type */ :
+ case [10]T /* ERROR cannot use generic type */ :
+ case struct{T /* ERROR cannot use generic type */ }:
+ case *T /* ERROR cannot use generic type */ :
+ case func(T /* ERROR cannot use generic type */ ):
+ case interface{m(T /* ERROR cannot use generic type */ )}:
+ case map[T /* ERROR cannot use generic type */ ] string:
+ case chan T /* ERROR cannot use generic type */ :
+
+ case T /* ERROR cannot use generic type */ , *T /* ERROR cannot use generic type */ :
+ }
+}
+
+// Make sure a parenthesized nil is ok.
+
+func _(x interface{}) {
+ switch x.(type) {
+ case ((nil)), int:
+ }
+}
+
+// Make sure we look for the predeclared nil.
+
+func _(x interface{}) {
+ type nil int
+ switch x.(type) {
+ case nil: // ok - this is the type nil
+ }
+}
+
+func _(x interface{}) {
+ var nil int
+ switch x.(type) {
+ case nil /* ERROR not a type */ : // not ok - this is the variable nil
+ }
+}
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 0000000000000000000000000000000000000000..e6ccc6b9be703584177333b01e0d9afc780e7a9d
--- /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/issue48048.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48048.go2
new file mode 100644
index 0000000000000000000000000000000000000000..f4013306215ec3aaeb69ff6855132784178e72bd
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48048.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 T[P any] struct{}
+
+func (T[_]) A() {}
+
+var _ = (T[int]).A
+var _ = (*T[int]).A
+
+var _ = (T /* ERROR cannot use generic type */).A
+var _ = (*T /* ERROR cannot use generic type */).A
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48082.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48082.src
new file mode 100644
index 0000000000000000000000000000000000000000..5395154978ebe1864ab02ced4fe56b57110da8db
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48082.src
@@ -0,0 +1,7 @@
+// 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 issue48082
+
+import "init" /* ERROR init must be a func */ /* ERROR could not import init */
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48083.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48083.go2
new file mode 100644
index 0000000000000000000000000000000000000000..3dae51415df0a2178ca9c2b2206c2dc58f0cda27
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48083.go2
@@ -0,0 +1,9 @@
+// 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 T[P any] struct{}
+
+type _ interface{ int | T /* ERROR cannot use generic type */ }
\ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48136.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48136.go2
new file mode 100644
index 0000000000000000000000000000000000000000..0ab92df40fabc07b89d7e1ff0f6de1623392bb9e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48136.go2
@@ -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 p
+
+func f1[P interface{ *P }]() {}
+func f2[P interface{ func(P) }]() {}
+func f3[P, Q interface{ func(Q) P }]() {}
+func f4[P interface{ *Q }, Q interface{ func(P) }]() {}
+func f5[P interface{ func(P) }]() {}
+func f6[P interface { *Tree[P] }, Q any ]() {}
+
+func _() {
+ f1( /* ERROR cannot infer P */ )
+ f2( /* ERROR cannot infer P */ )
+ f3( /* ERROR cannot infer P */ )
+ f4( /* ERROR cannot infer P */ )
+ f5( /* ERROR cannot infer P */ )
+ f6( /* ERROR cannot infer P */ )
+}
+
+type Tree[P any] struct {
+ left, right *Tree[P]
+ data P
+}
+
+// test case from issue
+
+func foo[Src interface { func() Src }]() Src {
+ return foo[Src]
+}
+
+func _() {
+ foo( /* ERROR cannot infer Src */ )
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2
new file mode 100644
index 0000000000000000000000000000000000000000..e069930c42d99c3caa36a9acff90d60cf6031fa4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48234.go2
@@ -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
+
+var _ = interface{
+ m()
+ m /* ERROR "duplicate method" */ ()
+}(nil)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2
new file mode 100644
index 0000000000000000000000000000000000000000..2d908f4c8b56c7c6ab799080a5c863bfd5a234d5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48472.go2
@@ -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.
+
+package p
+
+func g() {
+ var s string
+ 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/issue48529.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48529.go2
new file mode 100644
index 0000000000000000000000000000000000000000..a3653fa19c09618b6565c51df29555b756549d17
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48529.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
+
+type T /* ERROR illegal cycle */ [U interface{ M() T[U, int] }] int
+
+type X int
+
+func (X) M() T[X] { return 0 }
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48582.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48582.go2
new file mode 100644
index 0000000000000000000000000000000000000000..c12091be7902f460ee8e3022a94e151f69faeab5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48582.go2
@@ -0,0 +1,29 @@
+// 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 N /* ERROR cycle */ interface {
+ int | N
+}
+
+type A /* ERROR cycle */ interface {
+ int | B
+}
+
+type B interface {
+ int | A
+}
+
+type S /* ERROR cycle */ struct {
+ I // ERROR interface contains type constraints
+}
+
+type I interface {
+ int | S
+}
+
+type P interface {
+ *P // ERROR interface contains type constraints
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48619.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48619.go2
new file mode 100644
index 0000000000000000000000000000000000000000..870bacd0bd25ab2d368906d091c66e3db1235e39
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48619.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.
+
+// This issue has been re-opened.
+
+package p
+
+func f[P any](a, _ P) {
+ // var x int
+ // f(a, x /* ERROR type int of x does not match P */)
+ // f(x, a /* ERROR type P of a does not match inferred type int for P */)
+}
+
+func g[P any](a, b P) {
+ // g(a, b)
+ // g(&a, &b)
+ // g([]P{}, []P{})
+}
+
+func h[P any](a, b P) {
+ // h(&a, &b)
+ // h([]P{a}, []P{b})
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48656.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48656.go2
new file mode 100644
index 0000000000000000000000000000000000000000..652f8ce37a3c98ac5bf322135bc9b878467f9728
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48656.go2
@@ -0,0 +1,12 @@
+// 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 issue is still open.
+
+package p
+
+func f[P *Q, Q any](p P, q Q) {
+ // _ = f[P]
+ // _ = f[/* ERROR cannot infer P */ *P]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48695.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48695.go2
new file mode 100644
index 0000000000000000000000000000000000000000..9f4a76851d5dad0ec983630cbb97abf263011f88
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48695.go2
@@ -0,0 +1,14 @@
+// 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 g[P ~func(T) P, T any](P) {}
+
+func _() {
+ type F func(int) F
+ var f F
+ g(f)
+ _ = g[F]
+}
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 0000000000000000000000000000000000000000..8a32c1ecf22a3c375abc3ae1f9b5ddf3f85abcb4
--- /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/issue48819.src b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48819.src
new file mode 100644
index 0000000000000000000000000000000000000000..9262110ea09e71e96c3494186beaba6c6c5f4db5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48819.src
@@ -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
+
+import "unsafe"
+
+type T /* ERROR illegal cycle in declaration of T */ struct {
+ T
+}
+
+func _(t T) {
+ _ = unsafe.Sizeof(t) // should not go into infinite recursion here
+}
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 0000000000000000000000000000000000000000..cf02cc130aa6cb44089d3abedf05734ea6fd0d42
--- /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 0000000000000000000000000000000000000000..d8ff7c8cf46d1b957f72bd8c1a856f693a30c9c3
--- /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 0000000000000000000000000000000000000000..ece1a27bb927f4bdf9b14a8674bca0e3488b6dff
--- /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 0000000000000000000000000000000000000000..f152e7f55ce27547ded9b31d157f0d0f7fa0e537
--- /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 method M\) */ (*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 method M\) */ 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 0000000000000000000000000000000000000000..c37b0f126783f6f0e3fc8ab5d0c088e9d96e0ecf
--- /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/issue49179.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2
new file mode 100644
index 0000000000000000000000000000000000000000..75bea1807226079867458b918a6b88d69630f290
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2
@@ -0,0 +1,37 @@
+// 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 f1[P int | string]() {}
+func f2[P ~int | string | float64]() {}
+func f3[P int](x P) {}
+
+type myInt int
+type myFloat float64
+
+func _() {
+ _ = f1[int]
+ _ = f1[myInt /* ERROR possibly missing ~ for int in constraint int\|string */]
+ _ = f2[myInt]
+ _ = f2[myFloat /* ERROR possibly missing ~ for float64 in constraint int\|string|float64 */]
+ var x myInt
+ f3( /* ERROR myInt does not implement int \(possibly missing ~ for int in constraint int\) */ x)
+}
+
+// test case from the issue
+
+type SliceConstraint[T any] interface {
+ []T
+}
+
+func Map[S SliceConstraint[E], E any](s S, f func(E) E) S {
+ return s
+}
+
+type MySlice []int
+
+func f(s MySlice) {
+ Map[MySlice /* ERROR MySlice does not implement SliceConstraint\[int\] \(possibly missing ~ for \[\]int in constraint SliceConstraint\[int\]\) */, int](s, nil)
+}
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 0000000000000000000000000000000000000000..524a0cbae364ad110ace20437441a1c39e7f6575
--- /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 0000000000000000000000000000000000000000..3f25e0ee35526adb7fa4d3edf398e1e6006f3daa
--- /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/issue49276.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go
new file mode 100644
index 0000000000000000000000000000000000000000..8839087b50671221dd56a6b05dad3bfa199ef33a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49276.go
@@ -0,0 +1,46 @@
+// 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"
+
+type S /* ERROR illegal cycle in declaration of S */ struct {
+ _ [unsafe.Sizeof(s)]byte
+}
+
+var s S
+
+// Since f is a pointer, this case could be valid.
+// But it's pathological and not worth the expense.
+type T struct {
+ f *[unsafe.Sizeof(T /* ERROR illegal cycle in type declaration */ {})]int
+}
+
+// a mutually recursive case using unsafe.Sizeof
+type (
+ A1 struct {
+ _ [unsafe.Sizeof(B1{})]int
+ }
+
+ B1 struct {
+ _ [unsafe.Sizeof(A1 /* ERROR illegal cycle in type declaration */ {})]int
+ }
+)
+
+// a mutually recursive case using len
+type (
+ A2 struct {
+ f [len(B2{}.f)]int
+ }
+
+ B2 struct {
+ f [len(A2 /* ERROR illegal cycle in type declaration */ {}.f)]int
+ }
+)
+
+// test case from issue
+type a struct {
+ _ [42 - unsafe.Sizeof(a /* ERROR illegal cycle in type declaration */ {})]byte
+}
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 0000000000000000000000000000000000000000..eaa8e4dc7d8ad6d33c582f79c89d903da03b8721
--- /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 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/fixedbugs/issue49439.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49439.go2
new file mode 100644
index 0000000000000000000000000000000000000000..6cc838b3b30104c299e86b74ad604282dbb9b8c4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49439.go2
@@ -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 p
+
+import "unsafe"
+
+type T0 /* ERROR illegal cycle */ [P T0[P]] struct{}
+
+type T1 /* ERROR illegal cycle */ [P T2[P]] struct{}
+type T2[P T1[P]] struct{}
+
+type T3 /* ERROR illegal cycle */ [P interface{ ~struct{ f T3[int] } }] struct{}
+
+// valid cycle in M
+type N[P M[P]] struct{}
+type M[Q any] struct { F *M[Q] }
+
+// "crazy" case
+type TC[P [unsafe.Sizeof(func() {
+ type T [P [unsafe.Sizeof(func(){})]byte] struct{}
+})]byte] struct{}
+
+// test case from issue
+type X /* ERROR illegal cycle */ [T any, PT X[T]] interface{}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49579.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49579.go2
new file mode 100644
index 0000000000000000000000000000000000000000..9e20ae5468db76e8604dcd93cea7c74f05480c8d
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49579.go2
@@ -0,0 +1,17 @@
+// 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 I[F any] interface {
+ Q(*F)
+}
+
+func G[F any]() I[any] {
+ return g /* ERROR "missing method Q \(Q has pointer receiver\)" */ [F]{}
+}
+
+type g[F any] struct{}
+
+func (*g[F]) Q(*any) {}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49592.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49592.go2
new file mode 100644
index 0000000000000000000000000000000000000000..846deaa89aac62bf1d7b404ca8e78d01965587f9
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49592.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
+
+func _() {
+ var x *interface{}
+ var y interface{}
+ _ = x == y
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49705.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49705.go2
new file mode 100644
index 0000000000000000000000000000000000000000..2b991b872211af0466e433ae7aecd699d78ff52d
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49705.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 "constraints"
+
+func shl[I constraints.Integer](n int) I {
+ return 1 << n
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49739.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49739.go2
new file mode 100644
index 0000000000000000000000000000000000000000..46b1e71a3b95cfb43cfd760524c89d4054738b9c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49739.go2
@@ -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.
+
+// Verify that we get an empty type set (not just an error)
+// when using an invalid ~A.
+
+package p
+
+type A int
+type C interface {
+ ~ /* ERROR invalid use of ~ */ A
+}
+
+func f[_ C]() {}
+func g[_ interface{ C }]() {}
+func h[_ C | int]() {}
+
+func _() {
+ _ = f[int /* ERROR cannot implement C \(empty type set\) */]
+ _ = g[int /* ERROR cannot implement interface{C} \(empty type set\) */]
+ _ = h[int]
+}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49864.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49864.go2
new file mode 100644
index 0000000000000000000000000000000000000000..0437e74a643fab57fdd6bd1c26bbd295c3e4764c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49864.go2
@@ -0,0 +1,9 @@
+// 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, Q any](p P) {
+ _ = Q(p /* 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 0000000000000000000000000000000000000000..507fe6d021f7ad37cae9cb33202fcc4be57787e9
--- /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 nil
+ a = nil // ERROR cannot use nil
+ l = nil
+ s = nil // ERROR cannot use nil
+ p = nil
+ f = nil
+ i = nil
+ m = nil
+ c = nil
+ d = nil // ERROR cannot use nil
+
+ B = nil // ERROR cannot use nil
+ A = nil // ERROR cannot use nil
+ L = nil
+ S = nil // ERROR cannot use nil
+ P = nil
+ F = nil
+ I = nil
+ M = nil
+ C = nil
+ D = nil // ERROR cannot use nil
+ X = nil // ERROR cannot use 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 0000000000000000000000000000000000000000..fde332f34bd13e091ecffc7831da54c6db900dc8
--- /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/tuple.go b/src/cmd/compile/internal/types2/tuple.go
new file mode 100644
index 0000000000000000000000000000000000000000..1356aae0b018a464ef6c9deed88138e5d7115d7c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/tuple.go
@@ -0,0 +1,34 @@
+// Copyright 2011 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
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{vars: x}
+ }
+ return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+func (t *Tuple) Underlying() Type { return t }
+func (t *Tuple) String() string { return TypeString(t, nil) }
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index e6c260ff6790c6663438ad62ce3e21b232712a1a..9487ac5a84482ae5edcbc586802cc87cf05f0cf5 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -4,990 +4,123 @@
package types2
-import (
- "cmd/compile/internal/syntax"
- "fmt"
- "sync/atomic"
-)
-
// A Type represents a type of Go.
// All types implement the Type interface.
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
}
-// BasicKind describes the kind of basic type.
-type BasicKind int
-
-const (
- Invalid BasicKind = iota // type is invalid
-
- // predeclared types
- Bool
- Int
- Int8
- Int16
- Int32
- Int64
- Uint
- Uint8
- Uint16
- Uint32
- Uint64
- Uintptr
- Float32
- Float64
- Complex64
- Complex128
- String
- UnsafePointer
-
- // types for untyped values
- UntypedBool
- UntypedInt
- UntypedRune
- UntypedFloat
- UntypedComplex
- UntypedString
- UntypedNil
-
- // aliases
- Byte = Uint8
- Rune = Int32
-)
-
-// BasicInfo is a set of flags describing properties of a basic type.
-type BasicInfo int
-
-// Properties of basic types.
-const (
- IsBoolean BasicInfo = 1 << iota
- IsInteger
- IsUnsigned
- IsFloat
- IsComplex
- IsString
- IsUntyped
-
- IsOrdered = IsInteger | IsFloat | IsString
- IsNumeric = IsInteger | IsFloat | IsComplex
- IsConstType = IsBoolean | IsNumeric | IsString
-)
-
-// A Basic represents a basic type.
-type Basic struct {
- kind BasicKind
- info BasicInfo
- name string
-}
-
-// Kind returns the kind of basic type b.
-func (b *Basic) Kind() BasicKind { return b.kind }
-
-// Info returns information about properties of basic type b.
-func (b *Basic) Info() BasicInfo { return b.info }
-
-// Name returns the name of basic type b.
-func (b *Basic) Name() string { return b.name }
-
-// An Array represents an array type.
-type Array struct {
- len int64
- elem Type
-}
-
-// NewArray returns a new array type for the given element type and length.
-// A negative length indicates an unknown length.
-func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
-
-// Len returns the length of array a.
-// A negative result indicates an unknown length.
-func (a *Array) Len() int64 { return a.len }
-
-// Elem returns element type of array a.
-func (a *Array) Elem() Type { return a.elem }
-
-// A Slice represents a slice type.
-type Slice struct {
- elem Type
-}
-
-// NewSlice returns a new slice type for the given element type.
-func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
-
-// Elem returns the element type of slice s.
-func (s *Slice) Elem() Type { return s.elem }
-
-// A Struct represents a struct type.
-type Struct struct {
- fields []*Var
- tags []string // field tags; nil if there are no tags
-}
-
-// NewStruct returns a new struct with the given fields and corresponding field tags.
-// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
-// only as long as required to hold the tag with the largest index i. Consequently,
-// if no field has a tag, tags may be nil.
-func NewStruct(fields []*Var, tags []string) *Struct {
- var fset objset
- for _, f := range fields {
- if f.name != "_" && fset.insert(f) != nil {
- panic("multiple fields with the same name")
- }
- }
- if len(tags) > len(fields) {
- panic("more tags than fields")
- }
- return &Struct{fields: fields, tags: tags}
-}
-
-// NumFields returns the number of fields in the struct (including blank and embedded fields).
-func (s *Struct) NumFields() int { return len(s.fields) }
-
-// Field returns the i'th field for 0 <= i < NumFields().
-func (s *Struct) Field(i int) *Var { return s.fields[i] }
-
-// Tag returns the i'th field tag for 0 <= i < NumFields().
-func (s *Struct) Tag(i int) string {
- if i < len(s.tags) {
- return s.tags[i]
- }
- return ""
-}
-
-// A Pointer represents a pointer type.
-type Pointer struct {
- base Type // element type
-}
-
-// NewPointer returns a new pointer type for the given element (base) type.
-func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
-
-// Elem returns the element type for the given pointer p.
-func (p *Pointer) Elem() Type { return p.base }
-
-// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
-// Tuples are used as components of signatures and to represent the type of multiple
-// assignments; they are not first class types of Go.
-type Tuple struct {
- vars []*Var
-}
-
-// NewTuple returns a new tuple for the given variables.
-func NewTuple(x ...*Var) *Tuple {
- if len(x) > 0 {
- return &Tuple{vars: x}
- }
- // TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
- // it's too subtle and causes problems.
- return nil
-}
-
-// Len returns the number variables of tuple t.
-func (t *Tuple) Len() int {
- if t != nil {
- return len(t.vars)
- }
- return 0
-}
-
-// At returns the i'th variable of tuple t.
-func (t *Tuple) At(i int) *Var { return t.vars[i] }
-
-// A Signature represents a (non-builtin) function or method type.
-// The receiver is ignored when comparing signatures for identity.
-type Signature struct {
- // We need to keep the scope in Signature (rather than passing it around
- // and store it in the Func Object) because when type-checking a function
- // literal we call the general type checker which returns a general Type.
- // We then unpack the *Signature and use the scope for the literal body.
- rparams []*TypeName // receiver type parameters from left to right; or nil
- tparams []*TypeName // type parameters from left to right; or nil
- scope *Scope // function scope, present for package-local signatures
- recv *Var // nil if not a method
- params *Tuple // (incoming) parameters from left to right; or nil
- results *Tuple // (outgoing) results from left to right; or nil
- variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
-}
-
-// NewSignature returns a new function type for the given receiver, parameters,
-// and results, either of which may be nil. If variadic is set, the function
-// is variadic, it must have at least one parameter, and the last parameter
-// must be of unnamed slice type.
-func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
- if variadic {
- n := params.Len()
- if n == 0 {
- panic("types2.NewSignature: variadic function must have at least one parameter")
- }
- if _, ok := params.At(n - 1).typ.(*Slice); !ok {
- panic("types2.NewSignature: variadic parameter must be of unnamed slice type")
- }
- }
- return &Signature{recv: recv, params: params, results: results, variadic: variadic}
-}
-
-// Recv returns the receiver of signature s (if a method), or nil if a
-// function. It is ignored when comparing signatures for identity.
-//
-// For an abstract method, Recv returns the enclosing interface either
-// as a *Named or an *Interface. Due to embedding, an interface may
-// contain methods whose receiver type is a different interface.
-func (s *Signature) Recv() *Var { return s.recv }
-
-// TParams returns the type parameters of signature s, or nil.
-func (s *Signature) TParams() []*TypeName { return s.tparams }
-
-// RParams returns the receiver type params of signature s, or nil.
-func (s *Signature) RParams() []*TypeName { return s.rparams }
-
-// SetTParams sets the type parameters of signature s.
-func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams }
-
-// Params returns the parameters of signature s, or nil.
-func (s *Signature) Params() *Tuple { return s.params }
-
-// Results returns the results of signature s, or nil.
-func (s *Signature) Results() *Tuple { return s.results }
-
-// Variadic reports whether the signature s is variadic.
-func (s *Signature) Variadic() bool { return s.variadic }
-
-// A Sum represents a set of possible types.
-// Sums are currently used to represent type lists of interfaces
-// and thus the underlying types of type parameters; they are not
-// first class types of Go.
-type Sum struct {
- types []Type // types are unique
-}
-
-// NewSum returns a new Sum type consisting of the provided
-// types if there are more than one. If there is exactly one
-// type, it returns that type. If the list of types is empty
-// the result is nil.
-func NewSum(types []Type) Type {
- if len(types) == 0 {
- return nil
- }
-
- // What should happen if types contains a sum type?
- // Do we flatten the types list? For now we check
- // and panic. This should not be possible for the
- // current use case of type lists.
- // TODO(gri) Come up with the rules for sum types.
- for _, t := range types {
- if _, ok := t.(*Sum); ok {
- panic("sum type contains sum type - unimplemented")
- }
- }
-
- if len(types) == 1 {
- return types[0]
+// 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
+// to be fully set up.
+func under(t Type) Type {
+ if t, _ := t.(*Named); t != nil {
+ return t.under()
}
- return &Sum{types: types}
+ return t.Underlying()
}
-// is reports whether all types in t satisfy pred.
-func (s *Sum) is(pred func(Type) bool) bool {
- if s == nil {
- return false
+// If t is not a type parameter, structuralType returns the underlying type.
+// If t is a type parameter, structuralType returns the single underlying
+// type of all types in its type set if it exists, or nil otherwise. If the
+// type set contains only unrestricted and restricted channel types (with
+// identical element types), the single underlying type is the restricted
+// channel type if the restrictions are always the same, or nil otherwise.
+func structuralType(t Type) Type {
+ tpar, _ := t.(*TypeParam)
+ if tpar == nil {
+ return under(t)
}
- for _, t := range s.types {
- if !pred(t) {
- return false
- }
- }
- return true
-}
-
-// An Interface represents an interface type.
-type Interface struct {
- methods []*Func // ordered list of explicitly declared methods
- types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
- embeddeds []Type // ordered list of explicitly embedded types
- allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
- allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
-
- obj Object // type declaration defining this interface; or nil (for better error messages)
-}
-
-// unpack unpacks a type into a list of types.
-// TODO(gri) Try to eliminate the need for this function.
-func unpack(typ Type) []Type {
- if typ == nil {
- return nil
- }
- if sum := asSum(typ); sum != nil {
- return sum.types
- }
- return []Type{typ}
-}
-
-// is reports whether interface t represents types that all satisfy pred.
-func (t *Interface) is(pred func(Type) bool) bool {
- if t.allTypes == nil {
- return false // we must have at least one type! (was bug)
- }
- for _, t := range unpack(t.allTypes) {
- if !pred(t) {
+ var su Type
+ if tpar.underIs(func(u Type) bool {
+ if u == nil {
return false
}
- }
- return true
-}
-
-// emptyInterface represents the empty (completed) interface
-var emptyInterface = Interface{allMethods: markComplete}
-
-// markComplete is used to mark an empty interface as completely
-// set up by setting the allMethods field to a non-nil empty slice.
-var markComplete = make([]*Func, 0)
-
-// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
-// Each embedded type must have an underlying type of interface type.
-// NewInterface takes ownership of the provided methods and may modify their types by setting
-// missing receivers. To compute the method set of the interface, Complete must be called.
-//
-// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types
-// to be embedded. This is necessary for interfaces that embed alias type names referring to
-// non-defined (literal) interface types.
-func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
- tnames := make([]Type, len(embeddeds))
- for i, t := range embeddeds {
- tnames[i] = t
- }
- return NewInterfaceType(methods, tnames)
-}
-
-// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types.
-// Each embedded type must have an underlying type of interface type (this property is not
-// verified for defined types, which may be in the process of being set up and which don't
-// have a valid underlying type yet).
-// NewInterfaceType takes ownership of the provided methods and may modify their types by setting
-// missing receivers. To compute the method set of the interface, Complete must be called.
-func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
- if len(methods) == 0 && len(embeddeds) == 0 {
- return &emptyInterface
- }
-
- // set method receivers if necessary
- typ := new(Interface)
- for _, m := range methods {
- if sig := m.typ.(*Signature); sig.recv == nil {
- sig.recv = NewVar(m.pos, m.pkg, "", typ)
- }
- }
-
- // All embedded types should be interfaces; however, defined types
- // may not yet be fully resolved. Only verify that non-defined types
- // are interfaces. This matches the behavior of the code before the
- // fix for #25301 (issue #25596).
- for _, t := range embeddeds {
- if _, ok := t.(*Named); !ok && !IsInterface(t) {
- panic("embedded type is not an interface")
- }
- }
-
- // sort for API stability
- sortMethods(methods)
- sortTypes(embeddeds)
-
- typ.methods = methods
- typ.embeddeds = embeddeds
- return typ
-}
-
-// NumExplicitMethods returns the number of explicitly declared methods of interface t.
-func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
-
-// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
-// The methods are ordered by their unique Id.
-func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
-
-// NumEmbeddeds returns the number of embedded types in interface t.
-func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
-
-// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
-// The result is nil if the i'th embedded type is not a defined type.
-//
-// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
-func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
-
-// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
-func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
-
-// NumMethods returns the total number of methods of interface t.
-// The interface must have been completed.
-func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) }
-
-func (t *Interface) assertCompleteness() {
- if t.allMethods == nil {
- panic("interface is incomplete")
- }
-}
-
-// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
-// The methods are ordered by their unique Id.
-// The interface must have been completed.
-func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] }
-
-// Empty reports whether t is the empty interface.
-func (t *Interface) Empty() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- // A non-nil allTypes may still be empty and represents the bottom type.
- return len(t.allMethods) == 0 && t.allTypes == nil
- }
- return !t.iterate(func(t *Interface) bool {
- return len(t.methods) > 0 || t.types != nil
- }, nil)
-}
-
-// HasTypeList reports whether interface t has a type list, possibly from an embedded type.
-func (t *Interface) HasTypeList() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- return t.allTypes != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- return t.types != nil
- }, nil)
-}
-
-// IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
-func (t *Interface) IsComparable() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- _, m := lookupMethod(t.allMethods, nil, "==")
- return m != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- _, m := lookupMethod(t.methods, nil, "==")
- return m != nil
- }, nil)
-}
-
-// IsConstraint reports t.HasTypeList() || t.IsComparable().
-func (t *Interface) IsConstraint() bool {
- if t.allMethods != nil {
- // interface is complete - quick test
- if t.allTypes != nil {
- return true
- }
- _, m := lookupMethod(t.allMethods, nil, "==")
- return m != nil
- }
-
- return t.iterate(func(t *Interface) bool {
- if t.types != nil {
- return true
- }
- _, m := lookupMethod(t.methods, nil, "==")
- return m != nil
- }, nil)
-}
-
-// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true.
-// iterate reports whether any call to f returned true.
-func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool {
- if f(t) {
- return true
- }
- for _, e := range t.embeddeds {
- // e should be an interface but be careful (it may be invalid)
- if e := asInterface(e); e != nil {
- // Cyclic interfaces such as "type E interface { E }" are not permitted
- // but they are still constructed and we need to detect such cycles.
- if seen[e] {
- continue
- }
- if seen == nil {
- seen = make(map[*Interface]bool)
- }
- seen[e] = true
- if e.iterate(f, seen) {
- return true
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
}
}
- }
- return false
-}
-
-// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
-// If the type list is empty (absent), typ trivially satisfies the interface.
-// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
-// "implements" predicate.
-func (t *Interface) isSatisfiedBy(typ Type) bool {
- t.Complete()
- if t.allTypes == nil {
+ // su == nil || match(su, u) != nil
+ su = u
return true
+ }) {
+ return su
}
- types := unpack(t.allTypes)
- return includes(types, typ) || includes(types, under(typ))
+ return nil
}
-// Complete computes the interface's method set. It must be called by users of
-// NewInterfaceType and NewInterface after the interface's embedded types are
-// fully defined and before using the interface type in any way other than to
-// form other types. The interface must not contain duplicate methods or a
-// panic occurs. Complete returns the receiver.
-func (t *Interface) Complete() *Interface {
- // TODO(gri) consolidate this method with Checker.completeInterface
- if t.allMethods != nil {
- return t
+// structuralString is like structuralType but also considers []byte
+// and strings as identical. In this case, if successful and we saw
+// a string, the result is of type (possibly untyped) string.
+func structuralString(t Type) Type {
+ tpar, _ := t.(*TypeParam)
+ if tpar == nil {
+ return under(t) // string or untyped string
}
- t.allMethods = markComplete // avoid infinite recursion
-
- var todo []*Func
- var methods []*Func
- var seen objset
- addMethod := func(m *Func, explicit bool) {
- switch other := seen.insert(m); {
- case other == nil:
- methods = append(methods, m)
- case explicit:
- panic("duplicate method " + m.name)
- default:
- // check method signatures after all locally embedded interfaces are computed
- todo = append(todo, m, other.(*Func))
- }
- }
-
- for _, m := range t.methods {
- addMethod(m, true)
- }
-
- allTypes := t.types
-
- for _, typ := range t.embeddeds {
- utyp := under(typ)
- etyp := asInterface(utyp)
- if etyp == nil {
- if utyp != Typ[Invalid] {
- panic(fmt.Sprintf("%s is not an interface", typ))
- }
- continue
+ var su Type
+ hasString := false
+ if tpar.underIs(func(u Type) bool {
+ if u == nil {
+ return false
}
- etyp.Complete()
- for _, m := range etyp.allMethods {
- addMethod(m, false)
+ if isString(u) {
+ u = NewSlice(universeByte)
+ hasString = true
}
- allTypes = intersect(allTypes, etyp.allTypes)
- }
-
- for i := 0; i < len(todo); i += 2 {
- m := todo[i]
- other := todo[i+1]
- if !Identical(m.typ, other.typ) {
- panic("duplicate method " + m.name)
+ if su != nil {
+ u = match(su, u)
+ if u == nil {
+ return false
+ }
}
- }
-
- if methods != nil {
- sortMethods(methods)
- t.allMethods = methods
- }
- t.allTypes = allTypes
-
- return t
-}
-
-// A Map represents a map type.
-type Map struct {
- key, elem Type
-}
-
-// NewMap returns a new map for the given key and element types.
-func NewMap(key, elem Type) *Map {
- return &Map{key: key, elem: elem}
-}
-
-// Key returns the key type of map m.
-func (m *Map) Key() Type { return m.key }
-
-// Elem returns the element type of map m.
-func (m *Map) Elem() Type { return m.elem }
-
-// A Chan represents a channel type.
-type Chan struct {
- dir ChanDir
- elem Type
-}
-
-// A ChanDir value indicates a channel direction.
-type ChanDir int
-
-// The direction of a channel is indicated by one of these constants.
-const (
- SendRecv ChanDir = iota
- SendOnly
- RecvOnly
-)
-
-// NewChan returns a new channel type for the given direction and element type.
-func NewChan(dir ChanDir, elem Type) *Chan {
- return &Chan{dir: dir, elem: elem}
-}
-
-// Dir returns the direction of channel c.
-func (c *Chan) Dir() ChanDir { return c.dir }
-
-// Elem returns the element type of channel c.
-func (c *Chan) Elem() Type { return c.elem }
-
-// TODO(gri) Clean up Named struct below; specifically the fromRHS field (can we use underlying?).
-
-// A Named represents a named (defined) type.
-type Named struct {
- check *Checker // for Named.under implementation
- info typeInfo // for cycle detection
- obj *TypeName // corresponding declared object
- orig *Named // original, uninstantiated type
- fromRHS Type // type (on RHS of declaration) this *Named type is derived from (for cycle reporting)
- underlying Type // possibly a *Named during setup; never a *Named once set up completely
- tparams []*TypeName // type parameters, or nil
- targs []Type // type arguments (after instantiation), or nil
- methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
-}
-
-// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
-// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
-// The underlying type must not be a *Named.
-func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
- if _, ok := underlying.(*Named); ok {
- panic("types2.NewNamed: underlying type must not be *Named")
- }
- return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
-}
-
-// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
-func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams []*TypeName, methods []*Func) *Named {
- typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
- if typ.orig == nil {
- typ.orig = typ
- }
- if obj.typ == nil {
- obj.typ = typ
- }
- return typ
-}
-
-// Obj returns the type name for the named type t.
-func (t *Named) Obj() *TypeName { return t.obj }
-
-// Orig returns the original generic type an instantiated type is derived from.
-// If t is not an instantiated type, the result is t.
-func (t *Named) Orig() *Named { return t.orig }
-
-// TODO(gri) Come up with a better representation and API to distinguish
-// between parameterized instantiated and non-instantiated types.
-
-// TParams returns the type parameters of the named type t, or nil.
-// The result is non-nil for an (originally) parameterized type even if it is instantiated.
-func (t *Named) TParams() []*TypeName { return t.tparams }
-
-// SetTParams sets the type parameters of the named type t.
-func (t *Named) SetTParams(tparams []*TypeName) { t.tparams = tparams }
-
-// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
-func (t *Named) TArgs() []Type { return t.targs }
-
-// SetTArgs sets the type arguments of the named type t.
-func (t *Named) SetTArgs(args []Type) { t.targs = args }
-
-// NumMethods returns the number of explicit methods whose receiver is named type t.
-func (t *Named) NumMethods() int { return len(t.methods) }
-
-// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
-func (t *Named) Method(i int) *Func { return t.methods[i] }
-
-// SetUnderlying sets the underlying type and marks t as complete.
-func (t *Named) SetUnderlying(underlying Type) {
- if underlying == nil {
- panic("types2.Named.SetUnderlying: underlying type must not be nil")
- }
- if _, ok := underlying.(*Named); ok {
- panic("types2.Named.SetUnderlying: underlying type must not be *Named")
- }
- t.underlying = underlying
-}
-
-// AddMethod adds method m unless it is already in the method list.
-func (t *Named) AddMethod(m *Func) {
- if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
- t.methods = append(t.methods, m)
- }
-}
-
-// Note: This is a uint32 rather than a uint64 because the
-// respective 64 bit atomic instructions are not available
-// on all platforms.
-var lastId uint32
-
-// nextId returns a value increasing monotonically by 1 with
-// each call, starting with 1. It may be called concurrently.
-func nextId() uint64 { return uint64(atomic.AddUint32(&lastId, 1)) }
-
-// A TypeParam represents a type parameter type.
-type TypeParam struct {
- check *Checker // for lazy type bound completion
- id uint64 // unique id, for debugging only
- obj *TypeName // corresponding type name
- index int // type parameter index in source order, starting at 0
- bound Type // *Named or *Interface; underlying type is always *Interface
-}
-
-// Obj returns the type name for the type parameter t.
-func (t *TypeParam) Obj() *TypeName { return t.obj }
-
-// NewTypeParam returns a new TypeParam.
-func (check *Checker) NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
- assert(bound != nil)
- typ := &TypeParam{check: check, id: nextId(), obj: obj, index: index, bound: bound}
- if obj.typ == nil {
- obj.typ = typ
- }
- return typ
-}
-
-func (t *TypeParam) Bound() *Interface {
- iface := asInterface(t.bound)
- // use the type bound position if we have one
- pos := nopos
- if n, _ := t.bound.(*Named); n != nil {
- pos = n.obj.pos
- }
- // TODO(gri) switch this to an unexported method on Checker.
- t.check.completeInterface(pos, iface)
- return iface
-}
-
-// 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 determined
-// by the corresponding type bound's type list. The
-// result may be the bottom or top type, but it is never
-// the incoming type parameter.
-func optype(typ Type) Type {
- if t := asTypeParam(typ); t != nil {
- // 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.Bound().allTypes; u != nil && u != typ {
- // u != typ and u is a type parameter => under(u) != typ, so this is ok
- return under(u)
+ // su == nil || match(su, u) != nil
+ su = u
+ return true
+ }) {
+ if hasString {
+ return Typ[String]
}
- return theTop
+ return su
}
- return under(typ)
-}
-
-// An instance represents an instantiated generic type syntactically
-// (without expanding the instantiation). Type instances appear only
-// during type-checking and are replaced by their fully instantiated
-// (expanded) types before the end of type-checking.
-type instance struct {
- check *Checker // for lazy instantiation
- pos syntax.Pos // position of type instantiation; for error reporting only
- base *Named // parameterized type to be instantiated
- targs []Type // type arguments
- poslist []syntax.Pos // position of each targ; for error reporting only
- value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set
+ return nil
}
-// expand returns the instantiated (= expanded) type of t.
-// The result is either an instantiated *Named type, or
-// Typ[Invalid] if there was an error.
-func (t *instance) expand() Type {
- v := t.value
- if v == nil {
- v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist)
- if v == nil {
- v = Typ[Invalid]
+// If x and y are identical, match returns x.
+// If x and y are identical channels but for their direction
+// and one of them is unrestricted, match returns the channel
+// with the restricted direction.
+// In all other cases, match returns nil.
+func match(x, y Type) Type {
+ // Common case: we don't have channels.
+ if Identical(x, y) {
+ return x
+ }
+
+ // We may have channels that differ in direction only.
+ if x, _ := x.(*Chan); x != nil {
+ if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) {
+ // We have channels that differ in direction only.
+ // If there's an unrestricted channel, select the restricted one.
+ switch {
+ case x.dir == SendRecv:
+ return y
+ case y.dir == SendRecv:
+ return x
+ }
}
- t.value = v
- }
- // After instantiation we must have an invalid or a *Named type.
- if debug && v != Typ[Invalid] {
- _ = v.(*Named)
- }
- return v
-}
-
-// expand expands a type instance into its instantiated
-// type and leaves all other types alone. expand does
-// not recurse.
-func expand(typ Type) Type {
- if t, _ := typ.(*instance); t != nil {
- return t.expand()
- }
- return typ
-}
-
-// expandf is set to expand.
-// Call expandf when calling expand causes compile-time cycle error.
-var expandf func(Type) Type
-
-func init() { expandf = expand }
-
-// bottom represents the bottom of the type lattice.
-// It is the underlying type of a type parameter that
-// cannot be satisfied by any type, usually because
-// the intersection of type constraints left nothing).
-type bottom struct{}
-
-// theBottom is the singleton bottom type.
-var theBottom = &bottom{}
-
-// 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),
-// usually because the type constraint has no type
-// list.
-type top struct{}
-
-// theTop is the singleton top type.
-var theTop = &top{}
-
-// Type-specific implementations of Underlying.
-func (t *Basic) Underlying() Type { return t }
-func (t *Array) Underlying() Type { return t }
-func (t *Slice) Underlying() Type { return t }
-func (t *Struct) Underlying() Type { return t }
-func (t *Pointer) Underlying() Type { return t }
-func (t *Tuple) Underlying() Type { return t }
-func (t *Signature) Underlying() Type { return t }
-func (t *Sum) Underlying() Type { return t }
-func (t *Interface) Underlying() Type { return t }
-func (t *Map) Underlying() Type { return t }
-func (t *Chan) Underlying() Type { return t }
-func (t *Named) Underlying() Type { return t.underlying }
-func (t *TypeParam) Underlying() Type { return t }
-func (t *instance) Underlying() Type { return t }
-func (t *bottom) Underlying() Type { return t }
-func (t *top) Underlying() Type { return t }
-
-// Type-specific implementations of String.
-func (t *Basic) String() string { return TypeString(t, nil) }
-func (t *Array) String() string { return TypeString(t, nil) }
-func (t *Slice) String() string { return TypeString(t, nil) }
-func (t *Struct) String() string { return TypeString(t, nil) }
-func (t *Pointer) String() string { return TypeString(t, nil) }
-func (t *Tuple) String() string { return TypeString(t, nil) }
-func (t *Signature) String() string { return TypeString(t, nil) }
-func (t *Sum) String() string { return TypeString(t, nil) }
-func (t *Interface) String() string { return TypeString(t, nil) }
-func (t *Map) String() string { return TypeString(t, nil) }
-func (t *Chan) String() string { return TypeString(t, nil) }
-func (t *Named) String() string { return TypeString(t, nil) }
-func (t *TypeParam) String() string { return TypeString(t, nil) }
-func (t *instance) String() string { return TypeString(t, nil) }
-func (t *bottom) String() string { return TypeString(t, nil) }
-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
-// to be fully set up.
-func under(t Type) Type {
- // TODO(gri) is this correct for *Sum?
- if n := asNamed(t); n != nil {
- return n.under()
}
- return t
-}
-
-// 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.
-
-func asBasic(t Type) *Basic {
- op, _ := optype(t).(*Basic)
- return op
-}
-
-func asArray(t Type) *Array {
- op, _ := optype(t).(*Array)
- return op
-}
-
-func asSlice(t Type) *Slice {
- op, _ := optype(t).(*Slice)
- return op
-}
-
-func asStruct(t Type) *Struct {
- op, _ := optype(t).(*Struct)
- return op
-}
-
-func asPointer(t Type) *Pointer {
- op, _ := optype(t).(*Pointer)
- return op
-}
-
-// asTuple is not needed - not provided
-func asSignature(t Type) *Signature {
- op, _ := optype(t).(*Signature)
- return op
-}
-
-func asSum(t Type) *Sum {
- op, _ := optype(t).(*Sum)
- return op
-}
-
-func asInterface(t Type) *Interface {
- op, _ := optype(t).(*Interface)
- return op
-}
-
-func asMap(t Type) *Map {
- op, _ := optype(t).(*Map)
- return op
-}
-
-func asChan(t Type) *Chan {
- op, _ := optype(t).(*Chan)
- return op
-}
-
-// If the argument to asNamed and asTypeParam is of the respective types
-// (possibly after expanding an instance type), these methods return that type.
-// Otherwise the result is nil.
-
-func asNamed(t Type) *Named {
- e, _ := expand(t).(*Named)
- return e
-}
-
-func asTypeParam(t Type) *TypeParam {
- u, _ := under(t).(*TypeParam)
- return u
+ // types are different
+ return nil
}
-
-// Exported for the compiler.
-
-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) }
diff --git a/src/cmd/compile/internal/types2/typelists.go b/src/cmd/compile/internal/types2/typelists.go
new file mode 100644
index 0000000000000000000000000000000000000000..0b77edbde251afaa5bc5094a1c0e1d32b0793e38
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typelists.go
@@ -0,0 +1,80 @@
+// 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 "bytes"
+
+// TypeParamList holds a list of type parameters.
+type TypeParamList struct{ tparams []*TypeParam }
+
+// Len returns the number of type parameters in the list.
+// It is safe to call on a nil receiver.
+func (l *TypeParamList) Len() int { return len(l.list()) }
+
+// At returns the i'th type parameter in the list.
+func (l *TypeParamList) At(i int) *TypeParam { return l.tparams[i] }
+
+// list is for internal use where we expect a []*TypeParam.
+// TODO(rfindley): list should probably be eliminated: we can pass around a
+// TypeParamList instead.
+func (l *TypeParamList) list() []*TypeParam {
+ if l == nil {
+ return nil
+ }
+ return l.tparams
+}
+
+// TypeList holds a list of types.
+type TypeList struct{ types []Type }
+
+// newTypeList returns a new TypeList with the types in list.
+func newTypeList(list []Type) *TypeList {
+ if len(list) == 0 {
+ return nil
+ }
+ return &TypeList{list}
+}
+
+// Len returns the number of types in the list.
+// It is safe to call on a nil receiver.
+func (l *TypeList) Len() int { return len(l.list()) }
+
+// At returns the i'th type in the list.
+func (l *TypeList) At(i int) Type { return l.types[i] }
+
+// list is for internal use where we expect a []Type.
+// TODO(rfindley): list should probably be eliminated: we can pass around a
+// TypeList instead.
+func (l *TypeList) list() []Type {
+ if l == nil {
+ return nil
+ }
+ return l.types
+}
+
+func (l *TypeList) String() string {
+ if l == nil || len(l.types) == 0 {
+ return "[]"
+ }
+ var buf bytes.Buffer
+ newTypeWriter(&buf, nil).typeList(l.types)
+ return buf.String()
+}
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func bindTParams(list []*TypeParam) *TypeParamList {
+ if len(list) == 0 {
+ return nil
+ }
+ for i, typ := range list {
+ if typ.index >= 0 {
+ panic("type parameter bound more than once")
+ }
+ typ.index = i
+ }
+ return &TypeParamList{tparams: list}
+}
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
new file mode 100644
index 0000000000000000000000000000000000000000..e32063a0af1dc85dbd9d00ffc53bacbfa6109e50
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -0,0 +1,160 @@
+// Copyright 2011 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 "sync/atomic"
+
+// Note: This is a uint32 rather than a uint64 because the
+// respective 64 bit atomic instructions are not available
+// on all platforms.
+var lastID uint32
+
+// nextID returns a value increasing monotonically by 1 with
+// each call, starting with 1. It may be called concurrently.
+func nextID() uint64 { return uint64(atomic.AddUint32(&lastID, 1)) }
+
+// A TypeParam represents a type parameter type.
+type TypeParam struct {
+ check *Checker // for lazy type bound completion
+ id uint64 // unique id, for debugging only
+ obj *TypeName // corresponding type name
+ index int // type parameter index in source order, starting at 0
+ bound Type // any type, but underlying is eventually *Interface for correct programs (see TypeParam.iface)
+}
+
+// Obj returns the type name for the type parameter t.
+func (t *TypeParam) Obj() *TypeName { return t.obj }
+
+// NewTypeParam returns a new TypeParam. Type parameters may be set on a Named
+// or Signature type by calling SetTypeParams. Setting a type parameter on more
+// than one type will result in a panic.
+//
+// The constraint argument can be nil, and set later via SetConstraint.
+func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
+ return (*Checker)(nil).newTypeParam(obj, constraint)
+}
+
+func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
+ // Always increment lastID, even if it is not used.
+ id := nextID()
+ if check != nil {
+ check.nextID++
+ id = check.nextID
+ }
+ typ := &TypeParam{check: check, id: id, obj: obj, index: -1, bound: constraint}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ // iface may mutate typ.bound, so we must ensure that iface() is called
+ // at least once before the resulting TypeParam escapes.
+ if check != nil {
+ check.later(func() {
+ typ.iface()
+ })
+ } else if constraint != nil {
+ typ.iface()
+ }
+ return typ
+}
+
+// Index returns the index of the type param within its param list, or -1 if
+// the type parameter has not yet been bound to a type.
+func (t *TypeParam) Index() int {
+ return t.index
+}
+
+// Constraint returns the type constraint specified for t.
+func (t *TypeParam) Constraint() Type {
+ return t.bound
+}
+
+// SetConstraint sets the type constraint for t.
+//
+// SetConstraint should not be called concurrently, but once SetConstraint
+// returns the receiver t is safe for concurrent use.
+func (t *TypeParam) SetConstraint(bound Type) {
+ if bound == nil {
+ panic("nil constraint")
+ }
+ t.bound = bound
+ // iface may mutate t.bound (if bound is not an interface), so ensure that
+ // this is done before returning.
+ t.iface()
+}
+
+func (t *TypeParam) Underlying() Type {
+ return t.iface()
+}
+
+func (t *TypeParam) String() string { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// iface returns the constraint interface of t.
+// TODO(gri) If we make tparamIsIface the default, this should be renamed to under
+// (similar to Named.under).
+func (t *TypeParam) iface() *Interface {
+ bound := t.bound
+
+ // determine constraint interface
+ var ityp *Interface
+ switch u := under(bound).(type) {
+ case *Basic:
+ if u == Typ[Invalid] {
+ // error is reported elsewhere
+ return &emptyInterface
+ }
+ case *Interface:
+ if isTypeParam(bound) {
+ // error is reported in Checker.collectTypeParams
+ return &emptyInterface
+ }
+ ityp = u
+ }
+
+ // If we don't have an interface, wrap constraint into an implicit interface.
+ if ityp == nil {
+ ityp = NewInterfaceType(nil, []Type{bound})
+ ityp.implicit = true
+ t.bound = ityp // update t.bound for next time (optimization)
+ }
+
+ // compute type set if necessary
+ if ityp.tset == nil {
+ // use the (original) type bound position if we have one
+ pos := nopos
+ if n, _ := bound.(*Named); n != nil {
+ pos = n.obj.pos
+ }
+ computeInterfaceTypeSet(t.check, pos, ityp)
+ }
+
+ return ityp
+}
+
+// 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/types_test.go b/src/cmd/compile/internal/types2/types_test.go
index 096402148d38ad481815ad916e00664dfd0419c2..11dca0b53de86421a3a34f5ae505d90dca61d3f2 100644
--- a/src/cmd/compile/internal/types2/types_test.go
+++ b/src/cmd/compile/internal/types2/types_test.go
@@ -4,14 +4,6 @@
package types2
-import "sync/atomic"
-
func init() {
acceptMethodTypeParams = true
}
-
-// Upon calling ResetId, nextId starts with 1 again.
-// It may be called concurrently. This is only needed
-// for tests where we may want to have a consistent
-// numbering for each individual test case.
-func ResetId() { atomic.StoreUint32(&lastId, 0) }
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
new file mode 100644
index 0000000000000000000000000000000000000000..eaf614da6410a5cd1381a28ff1bd5bf412162ffe
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -0,0 +1,401 @@
+// 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 (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "sort"
+)
+
+// ----------------------------------------------------------------------------
+// API
+
+// A _TypeSet represents the type set of an interface.
+type _TypeSet struct {
+ comparable bool // if set, the interface is or embeds comparable
+ // TODO(gri) consider using a set for the methods for faster lookup
+ methods []*Func // all methods of the interface; sorted by unique ID
+ terms termlist // type terms of the type set
+}
+
+// IsEmpty reports whether type set s is the empty set.
+func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
+
+// IsAll reports whether type set s is the set of all types (corresponding to the empty interface).
+func (s *_TypeSet) IsAll() bool {
+ return !s.comparable && len(s.methods) == 0 && s.terms.isAll()
+}
+
+// IsMethodSet reports whether the interface t is fully described by its method set.
+func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
+
+// IsComparable reports whether each type in the set is comparable.
+func (s *_TypeSet) IsComparable() bool {
+ if s.terms.isAll() {
+ return s.comparable
+ }
+ return s.is(func(t *term) bool {
+ return t != nil && Comparable(t.typ)
+ })
+}
+
+// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
+
+// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
+func (s *_TypeSet) IsTypeSet() bool {
+ return !s.comparable && len(s.methods) == 0
+}
+
+// NumMethods returns the number of methods available.
+func (s *_TypeSet) NumMethods() int { return len(s.methods) }
+
+// Method returns the i'th method of type set s for 0 <= i < s.NumMethods().
+// The methods are ordered by their unique ID.
+func (s *_TypeSet) Method(i int) *Func { return s.methods[i] }
+
+// LookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func (s *_TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
+ // TODO(gri) s.methods is sorted - consider binary search
+ return lookupMethod(s.methods, pkg, name)
+}
+
+func (s *_TypeSet) String() string {
+ switch {
+ case s.IsEmpty():
+ return "∅"
+ case s.IsAll():
+ return "𝓤"
+ }
+
+ hasMethods := len(s.methods) > 0
+ hasTerms := s.hasTerms()
+
+ var buf bytes.Buffer
+ buf.WriteByte('{')
+ if s.comparable {
+ buf.WriteString("comparable")
+ if hasMethods || hasTerms {
+ buf.WriteString("; ")
+ }
+ }
+ for i, m := range s.methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.String())
+ }
+ if hasMethods && hasTerms {
+ buf.WriteString("; ")
+ }
+ if hasTerms {
+ buf.WriteString(s.terms.String())
+ }
+ buf.WriteString("}")
+ return buf.String()
+}
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// 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.
+// TODO(gri) This function is not used anywhere anymore. Remove once we
+// are clear that we don't need it elsewhere in the future.
+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
+
+// 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 !s.hasTerms() {
+ return f(nil)
+ }
+ for _, t := range s.terms {
+ assert(t.typ != nil)
+ if !f(t) {
+ return false
+ }
+ }
+ 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 !s.hasTerms() {
+ return f(nil)
+ }
+ for _, t := range s.terms {
+ assert(t.typ != nil)
+ // x == under(x) for ~x terms
+ u := t.typ
+ if !t.tilde {
+ u = under(u)
+ }
+ if debug {
+ assert(Identical(u, under(u)))
+ }
+ if !f(u) {
+ return false
+ }
+ }
+ return true
+}
+
+// topTypeSet may be used as type set for the empty interface.
+var topTypeSet = _TypeSet{terms: allTermlist}
+
+// computeInterfaceTypeSet may be called with check == nil.
+func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_TypeSet {
+ if ityp.tset != nil {
+ return ityp.tset
+ }
+
+ // If the interface is not fully set up yet, the type set will
+ // not be complete, which may lead to errors when using the the
+ // type set (e.g. missing method). Don't compute a partial type
+ // set (and don't store it!), so that we still compute the full
+ // type set eventually. Instead, return the top type set and
+ // let any follow-on errors play out.
+ if !ityp.complete {
+ return &topTypeSet
+ }
+
+ if check != nil && check.conf.Trace {
+ // Types don't generally have position information.
+ // If we don't have a valid pos provided, try to use
+ // one close enough.
+ if !pos.IsKnown() && len(ityp.methods) > 0 {
+ pos = ityp.methods[0].pos
+ }
+
+ check.trace(pos, "type set for %s", ityp)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(pos, "=> %s ", ityp.typeSet())
+ }()
+ }
+
+ // An infinitely expanding interface (due to a cycle) is detected
+ // elsewhere (Checker.validType), so here we simply assume we only
+ // have valid interfaces. Mark the interface as complete to avoid
+ // infinite recursion if the validType check occurs later for some
+ // reason.
+ ityp.tset = &_TypeSet{terms: allTermlist} // TODO(gri) is this sufficient?
+
+ // Methods of embedded interfaces are collected unchanged; i.e., the identity
+ // of a method I.m's Func Object of an interface I is the same as that of
+ // the method m in an interface that embeds interface I. On the other hand,
+ // if a method is embedded via multiple overlapping embedded interfaces, we
+ // don't provide a guarantee which "original m" got chosen for the embedding
+ // interface. See also issue #34421.
+ //
+ // If we don't care to provide this identity guarantee anymore, instead of
+ // reusing the original method in embeddings, we can clone the method's Func
+ // Object and give it the position of a corresponding embedded interface. Then
+ // we can get rid of the mpos map below and simply use the cloned method's
+ // position.
+
+ var todo []*Func
+ var seen objset
+ var methods []*Func
+ mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
+ addMethod := func(pos syntax.Pos, m *Func, explicit bool) {
+ switch other := seen.insert(m); {
+ case other == nil:
+ methods = append(methods, m)
+ mpos[m] = pos
+ case explicit:
+ if check == nil {
+ panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name))
+ }
+ // check != nil
+ var err error_
+ err.errorf(pos, "duplicate method %s", m.name)
+ err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
+ check.report(&err)
+ default:
+ // We have a duplicate method name in an embedded (not explicitly declared) method.
+ // Check method signatures after all types are computed (issue #33656).
+ // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
+ // error here as well (even though we could do it eagerly) because it's the same
+ // error message.
+ if check == nil {
+ // check method signatures after all locally embedded interfaces are computed
+ todo = append(todo, m, other.(*Func))
+ break
+ }
+ // check != nil
+ check.later(func() {
+ if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
+ var err error_
+ err.errorf(pos, "duplicate method %s", m.name)
+ err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
+ check.report(&err)
+ }
+ })
+ }
+ }
+
+ for _, m := range ityp.methods {
+ addMethod(m.pos, m, true)
+ }
+
+ // collect embedded elements
+ var allTerms = allTermlist
+ for i, typ := range ityp.embeddeds {
+ // The embedding position is nil for imported interfaces
+ // and also for interface copies after substitution (but
+ // in that case we don't need to report errors again).
+ var pos syntax.Pos // embedding position
+ if ityp.embedPos != nil {
+ pos = (*ityp.embedPos)[i]
+ }
+ var terms termlist
+ switch u := under(typ).(type) {
+ case *Interface:
+ // For now we don't permit type parameters as constraints.
+ assert(!isTypeParam(typ))
+ tset := computeInterfaceTypeSet(check, pos, u)
+ // If typ is local, an error was already reported where typ is specified/defined.
+ if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ)
+ continue
+ }
+ if tset.comparable {
+ ityp.tset.comparable = true
+ }
+ for _, m := range tset.methods {
+ addMethod(pos, m, false) // use embedding position pos rather than m.pos
+ }
+ terms = tset.terms
+ case *Union:
+ if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(pos, "go1.18", "embedding interface element %s", u)
+ continue
+ }
+ tset := computeUnionTypeSet(check, pos, u)
+ if tset == &invalidTypeSet {
+ continue // ignore invalid unions
+ }
+ terms = tset.terms
+ default:
+ if u == Typ[Invalid] {
+ continue
+ }
+ if check != nil && !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(pos, "go1.18", "embedding non-interface type %s", typ)
+ continue
+ }
+ terms = termlist{{false, typ}}
+ }
+ // The type set of an interface is the intersection
+ // of the type sets of all its elements.
+ // Intersection cannot produce longer termlists and
+ // thus cannot overflow.
+ allTerms = allTerms.intersect(terms)
+ }
+ ityp.embedPos = nil // not needed anymore (errors have been reported)
+
+ // process todo's (this only happens if check == nil)
+ for i := 0; i < len(todo); i += 2 {
+ m := todo[i]
+ other := todo[i+1]
+ if !Identical(m.typ, other.typ) {
+ panic(fmt.Sprintf("%s: duplicate method %s", m.pos, m.name))
+ }
+ }
+
+ if methods != nil {
+ sortMethods(methods)
+ ityp.tset.methods = methods
+ }
+ ityp.tset.terms = allTerms
+
+ return ityp.tset
+}
+
+func sortMethods(list []*Func) {
+ sort.Sort(byUniqueMethodName(list))
+}
+
+func assertSortedMethods(list []*Func) {
+ if !debug {
+ panic("assertSortedMethods called outside debug mode")
+ }
+ if !sort.IsSorted(byUniqueMethodName(list)) {
+ panic("methods not sorted")
+ }
+}
+
+// byUniqueMethodName method lists can be sorted by their unique method names.
+type byUniqueMethodName []*Func
+
+func (a byUniqueMethodName) Len() int { return len(a) }
+func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(&a[j].object) }
+func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// invalidTypeSet is a singleton type set to signal an invalid type set
+// due to an error. It's also a valid empty type set, so consumers of
+// type sets may choose to ignore it.
+var invalidTypeSet _TypeSet
+
+// computeUnionTypeSet may be called with check == nil.
+// The result is &invalidTypeSet if the union overflows.
+func computeUnionTypeSet(check *Checker, pos syntax.Pos, utyp *Union) *_TypeSet {
+ if utyp.tset != nil {
+ return utyp.tset
+ }
+
+ // avoid infinite recursion (see also computeInterfaceTypeSet)
+ utyp.tset = new(_TypeSet)
+
+ var allTerms termlist
+ for _, t := range utyp.terms {
+ var terms termlist
+ u := under(t.typ)
+ if ui, _ := u.(*Interface); ui != nil {
+ // For now we don't permit type parameters as constraints.
+ assert(!isTypeParam(t.typ))
+ terms = computeInterfaceTypeSet(check, pos, ui).terms
+ } else if t.typ == Typ[Invalid] {
+ continue
+ } else {
+ if t.tilde && !Identical(t.typ, u) {
+ // There is no underlying type which is t.typ.
+ // The corresponding type set is empty.
+ t = nil // ∅ term
+ }
+ terms = termlist{(*term)(t)}
+ }
+ // The type set of a union expression is the union
+ // of the type sets of each term.
+ allTerms = allTerms.union(terms)
+ if len(allTerms) > maxTermCount {
+ if check != nil {
+ check.errorf(pos, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+ }
+ utyp.tset = &invalidTypeSet
+ return utyp.tset
+ }
+ }
+ utyp.tset.terms = allTerms
+
+ return utyp.tset
+}
diff --git a/src/cmd/compile/internal/types2/typeset_test.go b/src/cmd/compile/internal/types2/typeset_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f7cc06db9773c7f09cef849888de81c5bd18499
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typeset_test.go
@@ -0,0 +1,80 @@
+// 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"
+ "strings"
+ "testing"
+)
+
+func TestInvalidTypeSet(t *testing.T) {
+ if !invalidTypeSet.IsEmpty() {
+ t.Error("invalidTypeSet is not empty")
+ }
+}
+
+func TestTypeSetString(t *testing.T) {
+ for body, want := range map[string]string{
+ "{}": "𝓤",
+ "{int}": "{int}",
+ "{~int}": "{~int}",
+ "{int|string}": "{int ∪ string}",
+ "{int; string}": "∅",
+
+ "{comparable}": "{comparable}",
+ "{comparable; int}": "{comparable; int}",
+ "{~int; comparable}": "{comparable; ~int}",
+ "{int|string; comparable}": "{comparable; int ∪ string}",
+ "{comparable; int; string}": "∅",
+
+ "{m()}": "{func (p.T).m()}",
+ "{m1(); m2() int }": "{func (p.T).m1(); func (p.T).m2() int}",
+ "{error}": "{func (error).Error() string}",
+ "{m(); comparable}": "{comparable; func (p.T).m()}",
+ "{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}",
+ "{comparable; error}": "{comparable; func (error).Error() string}",
+
+ "{m(); comparable; int|float32|string}": "{comparable; func (p.T).m(); int ∪ float32 ∪ string}",
+ "{m1(); int; m2(); comparable }": "{comparable; func (p.T).m1(); func (p.T).m2(); int}",
+
+ "{E}; type E interface{}": "𝓤",
+ "{E}; type E interface{int;string}": "∅",
+ "{E}; type E interface{comparable}": "{comparable}",
+ } {
+ // parse
+ errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
+ src := "package p; type T interface" + body
+ file, err := syntax.Parse(nil, strings.NewReader(src), errh, nil, syntax.AllowGenerics)
+ if err != nil {
+ t.Fatalf("%s: %v (invalid test case)", body, err)
+ }
+
+ // type check
+ var conf Config
+ pkg, err := conf.Check(file.PkgName.Value, []*syntax.File{file}, nil)
+ if err != nil {
+ t.Fatalf("%s: %v (invalid test case)", body, err)
+ }
+
+ // lookup T
+ obj := pkg.scope.Lookup("T")
+ if obj == nil {
+ t.Fatalf("%s: T not found (invalid test case)", body)
+ }
+ T, ok := under(obj.Type()).(*Interface)
+ if !ok {
+ t.Fatalf("%s: %v is not an interface (invalid test case)", body, obj)
+ }
+
+ // verify test case
+ got := T.typeSet().String()
+ if got != want {
+ t.Errorf("%s: got %s; want %s", body, got, want)
+ }
+ }
+}
+
+// TODO(gri) add more tests
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index 40016697b7c90f99c6319f0dae6c9079f3db0767..4d03eba65757c0ceaac89f2f358754102d88f67c 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -9,6 +9,9 @@ package types2
import (
"bytes"
"fmt"
+ "sort"
+ "strconv"
+ "strings"
"unicode/utf8"
)
@@ -39,33 +42,18 @@ func RelativeTo(pkg *Package) Qualifier {
}
}
-// If gcCompatibilityMode is set, printing of types is modified
-// to match the representation of some types in the gc compiler:
-//
-// - byte and rune lose their alias name and simply stand for
-// uint8 and int32 respectively
-// - embedded interfaces get flattened (the embedding info is lost,
-// and certain recursive interface types cannot be printed anymore)
-//
-// This makes it easier to compare packages computed with the type-
-// checker vs packages imported from gc export data.
-//
-// Caution: This flag affects all uses of WriteType, globally.
-// It is only provided for testing in conjunction with
-// gc-generated data.
-//
-// This flag is exported in the x/tools/go/types package. We don't
-// need it at the moment in the std repo and so we don't export it
-// anymore. We should eventually try to remove it altogether.
-// TODO(gri) remove this
-var gcCompatibilityMode bool
-
// TypeString returns the string representation of typ.
// 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()
}
@@ -73,172 +61,190 @@ func TypeString(typ Type, qf Qualifier) string {
// The Qualifier controls the printing of
// package-level objects, and may be nil.
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
- writeType(buf, typ, qf, make([]Type, 0, 8))
+ newTypeWriter(buf, qf).typ(typ)
+}
+
+// WriteSignature writes the representation of the signature sig to buf,
+// without a leading "func" keyword.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
+ newTypeWriter(buf, qf).signature(sig)
+}
+
+type typeWriter struct {
+ buf *bytes.Buffer
+ seen map[Type]bool
+ qf Qualifier
+ ctxt *Context // if non-nil, we are type hashing
+ tparams *TypeParamList // local type parameters
+ debug bool // if true, write debug annotations
+}
+
+func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
+ return &typeWriter{buf, make(map[Type]bool), qf, nil, nil, false}
+}
+
+func newTypeHasher(buf *bytes.Buffer, ctxt *Context) *typeWriter {
+ assert(ctxt != nil)
+ return &typeWriter{buf, make(map[Type]bool), nil, ctxt, nil, false}
}
-// instanceMarker is the prefix for an instantiated type
-// in "non-evaluated" instance form.
-const instanceMarker = '#'
-
-func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
- // Theoretically, this is a quadratic lookup algorithm, but in
- // practice deeply nested composite types with unnamed component
- // types are uncommon. This code is likely more efficient than
- // using a map.
- for _, t := range visited {
- if t == typ {
- fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
- return
+func (w *typeWriter) byte(b byte) {
+ if w.ctxt != nil {
+ if b == ' ' {
+ b = '#'
}
+ w.buf.WriteByte(b)
+ return
+ }
+ w.buf.WriteByte(b)
+ if b == ',' || b == ';' {
+ w.buf.WriteByte(' ')
+ }
+}
+
+func (w *typeWriter) string(s string) {
+ w.buf.WriteString(s)
+}
+
+func (w *typeWriter) error(msg string) {
+ if w.ctxt != nil {
+ panic(msg)
+ }
+ w.buf.WriteString("<" + msg + ">")
+}
+
+func (w *typeWriter) typ(typ Type) {
+ if w.seen[typ] {
+ w.error("cycle to " + goTypeName(typ))
+ return
}
- visited = append(visited, typ)
+ w.seen[typ] = true
+ defer delete(w.seen, typ)
switch t := typ.(type) {
case nil:
- buf.WriteString("")
+ w.error("nil")
case *Basic:
// exported basic types go into package unsafe
// (currently this is just unsafe.Pointer)
if isExported(t.name) {
if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
- writeTypeName(buf, obj, qf)
+ w.typeName(obj)
break
}
}
-
- if gcCompatibilityMode {
- // forget the alias names
- switch t.kind {
- case Byte:
- t = Typ[Uint8]
- case Rune:
- t = Typ[Int32]
- }
- }
- buf.WriteString(t.name)
+ w.string(t.name)
case *Array:
- fmt.Fprintf(buf, "[%d]", t.len)
- writeType(buf, t.elem, qf, visited)
+ w.byte('[')
+ w.string(strconv.FormatInt(t.len, 10))
+ w.byte(']')
+ w.typ(t.elem)
case *Slice:
- buf.WriteString("[]")
- writeType(buf, t.elem, qf, visited)
+ w.string("[]")
+ w.typ(t.elem)
case *Struct:
- buf.WriteString("struct{")
+ w.string("struct{")
for i, f := range t.fields {
if i > 0 {
- buf.WriteString("; ")
+ w.byte(';')
}
// This doesn't do the right thing for embedded type
// aliases where we should print the alias name, not
// the aliased type (see issue #44410).
if !f.embedded {
- buf.WriteString(f.name)
- buf.WriteByte(' ')
+ w.string(f.name)
+ w.byte(' ')
}
- writeType(buf, f.typ, qf, visited)
+ w.typ(f.typ)
if tag := t.Tag(i); tag != "" {
- fmt.Fprintf(buf, " %q", tag)
+ w.byte(' ')
+ // TODO(gri) If tag contains blanks, replacing them with '#'
+ // in Context.TypeHash may produce another tag
+ // accidentally.
+ w.string(strconv.Quote(tag))
}
}
- buf.WriteByte('}')
+ w.byte('}')
case *Pointer:
- buf.WriteByte('*')
- writeType(buf, t.base, qf, visited)
+ w.byte('*')
+ w.typ(t.base)
case *Tuple:
- writeTuple(buf, t, false, qf, visited)
+ w.tuple(t, false)
case *Signature:
- buf.WriteString("func")
- writeSignature(buf, t, qf, visited)
-
- case *Sum:
- for i, t := range t.types {
+ w.string("func")
+ w.signature(t)
+
+ case *Union:
+ // Unions only appear as (syntactic) embedded elements
+ // in interfaces and syntactically cannot be empty.
+ if t.Len() == 0 {
+ w.error("empty union")
+ break
+ }
+ for i, t := range t.terms {
if i > 0 {
- buf.WriteString(", ")
+ w.byte('|')
+ }
+ if t.tilde {
+ w.byte('~')
}
- writeType(buf, t, qf, visited)
+ w.typ(t.typ)
}
case *Interface:
- // We write the source-level methods and embedded types rather
- // than the actual method set since resolved method signatures
- // may have non-printable cycles if parameters have embedded
- // interface types that (directly or indirectly) embed the
- // current interface. For instance, consider the result type
- // of m:
- //
- // type T interface{
- // m() interface{ T }
- // }
- //
- buf.WriteString("interface{")
- empty := true
- if gcCompatibilityMode {
- // print flattened interface
- // (useful to compare against gc-generated interfaces)
- for i, m := range t.allMethods {
- if i > 0 {
- buf.WriteString("; ")
- }
- buf.WriteString(m.name)
- writeSignature(buf, m.typ.(*Signature), qf, visited)
- empty = false
- }
- if !empty && t.allTypes != nil {
- buf.WriteString("; ")
- }
- if t.allTypes != nil {
- buf.WriteString("type ")
- writeType(buf, t.allTypes, qf, visited)
+ if t == universeAny.Type() && w.ctxt == nil {
+ // When not hashing, we can try to improve type strings by writing "any"
+ // for a type that is pointer-identical to universeAny. This logic should
+ // be deprecated by more robust handling for aliases.
+ w.string("any")
+ break
+ }
+ if t.implicit {
+ if len(t.methods) == 0 && len(t.embeddeds) == 1 {
+ w.typ(t.embeddeds[0])
+ break
}
+ // Something's wrong with the implicit interface.
+ // Print it as such and continue.
+ w.string("/* implicit */ ")
+ }
+ w.string("interface{")
+ first := true
+ if w.ctxt != nil {
+ w.typeSet(t.typeSet())
} else {
- // print explicit interface methods and embedded types
- for i, m := range t.methods {
- if i > 0 {
- buf.WriteString("; ")
+ for _, m := range t.methods {
+ if !first {
+ w.byte(';')
}
- buf.WriteString(m.name)
- writeSignature(buf, m.typ.(*Signature), qf, visited)
- empty = false
+ first = false
+ w.string(m.name)
+ w.signature(m.typ.(*Signature))
}
- if !empty && t.types != nil {
- buf.WriteString("; ")
- }
- if t.types != nil {
- buf.WriteString("type ")
- writeType(buf, t.types, qf, visited)
- empty = false
- }
- if !empty && len(t.embeddeds) > 0 {
- buf.WriteString("; ")
- }
- for i, typ := range t.embeddeds {
- if i > 0 {
- buf.WriteString("; ")
+ for _, typ := range t.embeddeds {
+ if !first {
+ w.byte(';')
}
- writeType(buf, typ, qf, visited)
- empty = false
- }
- }
- if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
- if !empty {
- buf.WriteByte(' ')
+ first = false
+ w.typ(typ)
}
- buf.WriteString("/* incomplete */")
}
- buf.WriteByte('}')
+ w.byte('}')
case *Map:
- buf.WriteString("map[")
- writeType(buf, t.key, qf, visited)
- buf.WriteByte(']')
- writeType(buf, t.elem, qf, visited)
+ w.string("map[")
+ w.typ(t.key)
+ w.byte(']')
+ w.typ(t.elem)
case *Chan:
var s string
@@ -255,157 +261,187 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
case RecvOnly:
s = "<-chan "
default:
- panic("unreachable")
+ w.error("unknown channel direction")
}
- buf.WriteString(s)
+ w.string(s)
if parens {
- buf.WriteByte('(')
+ w.byte('(')
}
- writeType(buf, t.elem, qf, visited)
+ w.typ(t.elem)
if parens {
- buf.WriteByte(')')
+ w.byte(')')
}
case *Named:
- writeTypeName(buf, t.obj, qf)
+ // If hashing, write a unique prefix for t to represent its identity, since
+ // named type identity is pointer identity.
+ if w.ctxt != nil {
+ w.string(strconv.Itoa(w.ctxt.getID(t)))
+ }
+ w.typeName(t.obj) // when hashing written for readability of the hash only
if t.targs != nil {
// instantiated type
- buf.WriteByte('[')
- writeTypeList(buf, t.targs, qf, visited)
- buf.WriteByte(']')
- } else if t.tparams != nil {
+ w.typeList(t.targs.list())
+ } else if w.ctxt == nil && t.TypeParams().Len() != 0 { // For type hashing, don't need to format the TypeParams
// parameterized type
- writeTParamList(buf, t.tparams, qf, visited)
+ w.tParamList(t.TypeParams().list())
}
case *TypeParam:
- s := "?"
- if t.obj != nil {
- s = t.obj.name
+ if t.obj == nil {
+ w.error("unnamed type parameter")
+ break
+ }
+ if i := tparamIndex(w.tparams.list(), t); i >= 0 {
+ // The names of type parameters that are declared by the type being
+ // hashed are not part of the type identity. Replace them with a
+ // placeholder indicating their index.
+ w.string(fmt.Sprintf("$%d", i))
+ } else {
+ w.string(t.obj.name)
+ if w.debug || w.ctxt != nil {
+ w.string(subscript(t.id))
+ }
}
- buf.WriteString(s + subscript(t.id))
-
- case *instance:
- buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
- writeTypeName(buf, t.base.obj, qf)
- buf.WriteByte('[')
- writeTypeList(buf, t.targs, qf, visited)
- buf.WriteByte(']')
-
- case *bottom:
- buf.WriteString("⊥")
-
- case *top:
- buf.WriteString("⊤")
default:
// For externally defined implementations of Type.
- buf.WriteString(t.String())
+ // Note: In this case cycles won't be caught.
+ w.string(t.String())
}
}
-func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
+// typeSet writes a canonical hash for an interface type set.
+func (w *typeWriter) typeSet(s *_TypeSet) {
+ assert(w.ctxt != nil)
+ first := true
+ for _, m := range s.methods {
+ if !first {
+ w.byte(';')
+ }
+ first = false
+ w.string(m.name)
+ w.signature(m.typ.(*Signature))
+ }
+ switch {
+ case s.terms.isAll():
+ // nothing to do
+ case s.terms.isEmpty():
+ w.string(s.terms.String())
+ default:
+ var termHashes []string
+ for _, term := range s.terms {
+ // terms are not canonically sorted, so we sort their hashes instead.
+ var buf bytes.Buffer
+ if term.tilde {
+ buf.WriteByte('~')
+ }
+ newTypeHasher(&buf, w.ctxt).typ(term.typ)
+ termHashes = append(termHashes, buf.String())
+ }
+ sort.Strings(termHashes)
+ if !first {
+ w.byte(';')
+ }
+ w.string(strings.Join(termHashes, "|"))
+ }
+}
+
+func (w *typeWriter) typeList(list []Type) {
+ w.byte('[')
for i, typ := range list {
if i > 0 {
- buf.WriteString(", ")
+ w.byte(',')
}
- writeType(buf, typ, qf, visited)
+ w.typ(typ)
}
+ w.byte(']')
}
-func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
- buf.WriteString("[")
+func (w *typeWriter) tParamList(list []*TypeParam) {
+ w.byte('[')
var prev Type
- for i, p := range list {
- // TODO(gri) support 'any' sugar here.
- var b Type = &emptyInterface
- if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
- b = t.bound
+ for i, tpar := range list {
+ // Determine the type parameter and its constraint.
+ // list is expected to hold type parameter names,
+ // but don't crash if that's not the case.
+ if tpar == nil {
+ w.error("nil type parameter")
+ continue
}
if i > 0 {
- if b != prev {
- // type bound changed - write previous one before advancing
- buf.WriteByte(' ')
- writeType(buf, prev, qf, visited)
+ if tpar.bound != prev {
+ // bound changed - write previous one before advancing
+ w.byte(' ')
+ w.typ(prev)
}
- buf.WriteString(", ")
- }
- prev = b
-
- if t, _ := p.typ.(*TypeParam); t != nil {
- writeType(buf, t, qf, visited)
- } else {
- buf.WriteString(p.name)
+ w.byte(',')
}
+ prev = tpar.bound
+ w.typ(tpar)
}
if prev != nil {
- buf.WriteByte(' ')
- writeType(buf, prev, qf, visited)
+ w.byte(' ')
+ w.typ(prev)
}
- buf.WriteByte(']')
+ w.byte(']')
}
-func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
- s := ""
- if obj != nil {
- if obj.pkg != nil {
- writePackage(buf, obj.pkg, qf)
- }
- // TODO(gri): function-local named types should be displayed
- // differently from named types at package level to avoid
- // ambiguity.
- s = obj.name
+func (w *typeWriter) typeName(obj *TypeName) {
+ if obj.pkg != nil {
+ writePackage(w.buf, obj.pkg, w.qf)
}
- buf.WriteString(s)
+ w.string(obj.name)
}
-func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
- buf.WriteByte('(')
+func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
+ w.byte('(')
if tup != nil {
for i, v := range tup.vars {
if i > 0 {
- buf.WriteString(", ")
+ w.byte(',')
}
- if v.name != "" {
- buf.WriteString(v.name)
- buf.WriteByte(' ')
+ // parameter names are ignored for type identity and thus type hashes
+ if w.ctxt == nil && v.name != "" {
+ w.string(v.name)
+ w.byte(' ')
}
typ := v.typ
if variadic && i == len(tup.vars)-1 {
if s, ok := typ.(*Slice); ok {
- buf.WriteString("...")
+ w.string("...")
typ = s.elem
} else {
// special case:
// append(s, "foo"...) leads to signature func([]byte, string...)
- if t := asBasic(typ); t == nil || t.kind != String {
- panic("internal error: string type expected")
+ if t, _ := under(typ).(*Basic); t == nil || t.kind != String {
+ w.error("expected string type")
+ continue
}
- writeType(buf, typ, qf, visited)
- buf.WriteString("...")
+ w.typ(typ)
+ w.string("...")
continue
}
}
- writeType(buf, typ, qf, visited)
+ w.typ(typ)
}
}
- buf.WriteByte(')')
+ w.byte(')')
}
-// WriteSignature writes the representation of the signature sig to buf,
-// without a leading "func" keyword.
-// The Qualifier controls the printing of
-// package-level objects, and may be nil.
-func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
- writeSignature(buf, sig, qf, make([]Type, 0, 8))
-}
-
-func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
- if sig.tparams != nil {
- writeTParamList(buf, sig.tparams, qf, visited)
+func (w *typeWriter) signature(sig *Signature) {
+ if sig.TypeParams().Len() != 0 {
+ if w.ctxt != nil {
+ assert(w.tparams == nil)
+ w.tparams = sig.TypeParams()
+ defer func() {
+ w.tparams = nil
+ }()
+ }
+ w.tParamList(sig.TypeParams().list())
}
- writeTuple(buf, sig.params, sig.variadic, qf, visited)
+ w.tuple(sig.params, sig.variadic)
n := sig.results.Len()
if n == 0 {
@@ -413,15 +449,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T
return
}
- buf.WriteByte(' ')
- if n == 1 && sig.results.vars[0].name == "" {
- // single unnamed result
- writeType(buf, sig.results.vars[0].typ, qf, visited)
+ w.byte(' ')
+ if n == 1 && (w.ctxt != nil || sig.results.vars[0].name == "") {
+ // single unnamed result (if type hashing, name must be ignored)
+ w.typ(sig.results.vars[0].typ)
return
}
// multiple or named result(s)
- writeTuple(buf, sig.results, false, qf, visited)
+ w.tuple(sig.results, false)
}
// subscript returns the decimal (utf8) representation of x using subscript digits.
diff --git a/src/cmd/compile/internal/types2/typestring_test.go b/src/cmd/compile/internal/types2/typestring_test.go
index d98e9a5ade6e9bbe0a3c60b03d9dd472425c35a9..eda68355884ceeaa5ed4d61c51b3badca56d3419 100644
--- a/src/cmd/compile/internal/types2/typestring_test.go
+++ b/src/cmd/compile/internal/types2/typestring_test.go
@@ -91,7 +91,8 @@ var independentTestTypes = []testEntry{
dup("interface{}"),
dup("interface{m()}"),
dup(`interface{String() string; m(int) float32}`),
- dup(`interface{type int, float32, complex128}`),
+ dup("interface{int|float32|complex128}"),
+ dup("interface{int|~float32|~complex128}"),
// maps
dup("map[string]int"),
@@ -128,67 +129,18 @@ func TestTypeString(t *testing.T) {
t.Errorf("%s: %s", src, err)
continue
}
- typ := pkg.Scope().Lookup("T").Type().Underlying()
+ obj := pkg.Scope().Lookup("T")
+ if obj == nil {
+ t.Errorf("%s: T not found", test.src)
+ continue
+ }
+ typ := obj.Type().Underlying()
if got := typ.String(); got != test.str {
t.Errorf("%s: got %s, want %s", test.src, got, test.str)
}
}
}
-var nopos syntax.Pos
-
-func TestIncompleteInterfaces(t *testing.T) {
- sig := NewSignature(nil, nil, nil, false)
- m := NewFunc(nopos, nil, "m", sig)
- for _, test := range []struct {
- typ *Interface
- want string
- }{
- {new(Interface), "interface{/* incomplete */}"},
- {new(Interface).Complete(), "interface{}"},
-
- {NewInterface(nil, nil), "interface{}"},
- {NewInterface(nil, nil).Complete(), "interface{}"},
- {NewInterface([]*Func{}, nil), "interface{}"},
- {NewInterface([]*Func{}, nil).Complete(), "interface{}"},
- {NewInterface(nil, []*Named{}), "interface{}"},
- {NewInterface(nil, []*Named{}).Complete(), "interface{}"},
- {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"},
- {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"},
- {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"},
- {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"},
-
- {NewInterfaceType(nil, nil), "interface{}"},
- {NewInterfaceType(nil, nil).Complete(), "interface{}"},
- {NewInterfaceType([]*Func{}, nil), "interface{}"},
- {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"},
- {NewInterfaceType(nil, []Type{}), "interface{}"},
- {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"},
- {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"},
- {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"},
- {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"},
- {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"},
- } {
- got := test.typ.String()
- if got != test.want {
- t.Errorf("got: %s, want: %s", got, test.want)
- }
- }
-}
-
-// newDefined creates a new defined type named T with the given underlying type.
-// Helper function for use with TestIncompleteInterfaces only.
-func newDefined(underlying Type) *Named {
- tname := NewTypeName(nopos, nil, "T", nil)
- return NewNamed(tname, underlying, nil)
-}
-
func TestQualifiedTypeString(t *testing.T) {
p, _ := pkgFor("p.go", "package p; type T int", nil)
q, _ := pkgFor("q.go", "package q", nil)
diff --git a/src/cmd/compile/internal/types2/typeterm.go b/src/cmd/compile/internal/types2/typeterm.go
new file mode 100644
index 0000000000000000000000000000000000000000..1d7223f13c42c013e06c8d8f1fb9cf4b423a7fb2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typeterm.go
@@ -0,0 +1,166 @@
+// 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
+
+// A term describes elementary type sets:
+//
+// ∅: (*term)(nil) == ∅ // set of no types (empty set)
+// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
+// T: &term{false, T} == {T} // set of type T
+// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
+//
+type term struct {
+ tilde bool // valid if typ != nil
+ typ Type
+}
+
+func (x *term) String() string {
+ switch {
+ case x == nil:
+ return "∅"
+ case x.typ == nil:
+ return "𝓤"
+ case x.tilde:
+ return "~" + x.typ.String()
+ default:
+ return x.typ.String()
+ }
+}
+
+// equal reports whether x and y represent the same type set.
+func (x *term) equal(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return x == y
+ case x.typ == nil || y.typ == nil:
+ return x.typ == y.typ
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ return x.tilde == y.tilde && Identical(x.typ, y.typ)
+}
+
+// union returns the union x ∪ y: zero, one, or two non-nil terms.
+func (x *term) union(y *term) (_, _ *term) {
+ // easy cases
+ switch {
+ case x == nil && y == nil:
+ return nil, nil // ∅ ∪ ∅ == ∅
+ case x == nil:
+ return y, nil // ∅ ∪ y == y
+ case y == nil:
+ return x, nil // x ∪ ∅ == x
+ case x.typ == nil:
+ return x, nil // 𝓤 ∪ y == 𝓤
+ case y.typ == nil:
+ return y, nil // x ∪ 𝓤 == 𝓤
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return x, y // x ∪ y == (x, y) if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ∪ ~t == ~t
+ // ~t ∪ T == ~t
+ // T ∪ ~t == ~t
+ // T ∪ T == T
+ if x.tilde || !y.tilde {
+ return x, nil
+ }
+ return y, nil
+}
+
+// intersect returns the intersection x ∩ y.
+func (x *term) intersect(y *term) *term {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
+ case x.typ == nil:
+ return y // 𝓤 ∩ y == y
+ case y.typ == nil:
+ return x // x ∩ 𝓤 == x
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return nil // x ∩ y == ∅ if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ∩ ~t == ~t
+ // ~t ∩ T == T
+ // T ∩ ~t == T
+ // T ∩ T == T
+ if !x.tilde || y.tilde {
+ return x
+ }
+ return y
+}
+
+// includes reports whether t ∈ x.
+func (x *term) includes(t Type) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return false // t ∈ ∅ == false
+ case x.typ == nil:
+ return true // t ∈ 𝓤 == true
+ }
+ // ∅ ⊂ x ⊂ 𝓤
+
+ u := t
+ if x.tilde {
+ u = under(u)
+ }
+ return Identical(x.typ, u)
+}
+
+// subsetOf reports whether x ⊆ y.
+func (x *term) subsetOf(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return true // ∅ ⊆ y == true
+ case y == nil:
+ return false // x ⊆ ∅ == false since x != ∅
+ case y.typ == nil:
+ return true // x ⊆ 𝓤 == true
+ case x.typ == nil:
+ return false // 𝓤 ⊆ y == false since y != 𝓤
+ }
+ // ∅ ⊂ x, y ⊂ 𝓤
+
+ if x.disjoint(y) {
+ return false // x ⊆ y == false if x ∩ y == ∅
+ }
+ // x.typ == y.typ
+
+ // ~t ⊆ ~t == true
+ // ~t ⊆ T == false
+ // T ⊆ ~t == true
+ // T ⊆ T == true
+ return !x.tilde || y.tilde
+}
+
+// disjoint reports whether x ∩ y == ∅.
+// x.typ and y.typ must not be nil.
+func (x *term) disjoint(y *term) bool {
+ if debug && (x.typ == nil || y.typ == nil) {
+ panic("invalid argument(s)")
+ }
+ ux := x.typ
+ if y.tilde {
+ ux = under(ux)
+ }
+ uy := y.typ
+ if x.tilde {
+ uy = under(uy)
+ }
+ return !Identical(ux, uy)
+}
diff --git a/src/cmd/compile/internal/types2/typeterm_test.go b/src/cmd/compile/internal/types2/typeterm_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5a5c1fa4477d9e0da4a5a4d72a4505bce65856e1
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typeterm_test.go
@@ -0,0 +1,239 @@
+// 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 (
+ "strings"
+ "testing"
+)
+
+var myInt = func() Type {
+ tname := NewTypeName(nopos, nil, "myInt", nil)
+ return NewNamed(tname, Typ[Int], nil)
+}()
+
+var testTerms = map[string]*term{
+ "∅": nil,
+ "𝓤": {},
+ "int": {false, Typ[Int]},
+ "~int": {true, Typ[Int]},
+ "string": {false, Typ[String]},
+ "~string": {true, Typ[String]},
+ "myInt": {false, myInt},
+}
+
+func TestTermString(t *testing.T) {
+ for want, x := range testTerms {
+ if got := x.String(); got != want {
+ t.Errorf("%v.String() == %v; want %v", x, got, want)
+ }
+ }
+}
+
+func split(s string, n int) []string {
+ r := strings.Split(s, " ")
+ if len(r) != n {
+ panic("invalid test case: " + s)
+ }
+ return r
+}
+
+func testTerm(name string) *term {
+ r, ok := testTerms[name]
+ if !ok {
+ panic("invalid test argument: " + name)
+ }
+ return r
+}
+
+func TestTermEqual(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ T",
+ "𝓤 𝓤 T",
+ "int int T",
+ "~int ~int T",
+ "myInt myInt T",
+ "∅ 𝓤 F",
+ "∅ int F",
+ "∅ ~int F",
+ "𝓤 int F",
+ "𝓤 ~int F",
+ "𝓤 myInt F",
+ "int ~int F",
+ "int myInt F",
+ "~int myInt F",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.equal(y); got != want {
+ t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want)
+ }
+ // equal is symmetric
+ x, y = y, x
+ if got := x.equal(y); got != want {
+ t.Errorf("%v.equal(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermUnion(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ ∅ ∅",
+ "∅ 𝓤 𝓤 ∅",
+ "∅ int int ∅",
+ "∅ ~int ~int ∅",
+ "∅ myInt myInt ∅",
+ "𝓤 𝓤 𝓤 ∅",
+ "𝓤 int 𝓤 ∅",
+ "𝓤 ~int 𝓤 ∅",
+ "𝓤 myInt 𝓤 ∅",
+ "int int int ∅",
+ "int ~int ~int ∅",
+ "int string int string",
+ "int ~string int ~string",
+ "int myInt int myInt",
+ "~int ~string ~int ~string",
+ "~int myInt ~int ∅",
+
+ // union is symmetric, but the result order isn't - repeat symmetric cases explictly
+ "𝓤 ∅ 𝓤 ∅",
+ "int ∅ int ∅",
+ "~int ∅ ~int ∅",
+ "myInt ∅ myInt ∅",
+ "int 𝓤 𝓤 ∅",
+ "~int 𝓤 𝓤 ∅",
+ "myInt 𝓤 𝓤 ∅",
+ "~int int ~int ∅",
+ "string int string int",
+ "~string int ~string int",
+ "myInt int myInt int",
+ "~string ~int ~string ~int",
+ "myInt ~int ~int ∅",
+ } {
+ args := split(test, 4)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want1 := testTerm(args[2])
+ want2 := testTerm(args[3])
+ if got1, got2 := x.union(y); !got1.equal(want1) || !got2.equal(want2) {
+ t.Errorf("%v.union(%v) = %v, %v; want %v, %v", x, y, got1, got2, want1, want2)
+ }
+ }
+}
+
+func TestTermIntersection(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ ∅",
+ "∅ 𝓤 ∅",
+ "∅ int ∅",
+ "∅ ~int ∅",
+ "∅ myInt ∅",
+ "𝓤 𝓤 𝓤",
+ "𝓤 int int",
+ "𝓤 ~int ~int",
+ "𝓤 myInt myInt",
+ "int int int",
+ "int ~int int",
+ "int string ∅",
+ "int ~string ∅",
+ "int string ∅",
+ "~int ~string ∅",
+ "~int myInt myInt",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := testTerm(args[2])
+ if got := x.intersect(y); !got.equal(want) {
+ t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want)
+ }
+ // intersect is symmetric
+ x, y = y, x
+ if got := x.intersect(y); !got.equal(want) {
+ t.Errorf("%v.intersect(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermIncludes(t *testing.T) {
+ for _, test := range []string{
+ "∅ int F",
+ "𝓤 int T",
+ "int int T",
+ "~int int T",
+ "~int myInt T",
+ "string int F",
+ "~string int F",
+ "myInt int F",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1]).typ
+ want := args[2] == "T"
+ if got := x.includes(y); got != want {
+ t.Errorf("%v.includes(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermSubsetOf(t *testing.T) {
+ for _, test := range []string{
+ "∅ ∅ T",
+ "𝓤 𝓤 T",
+ "int int T",
+ "~int ~int T",
+ "myInt myInt T",
+ "∅ 𝓤 T",
+ "∅ int T",
+ "∅ ~int T",
+ "∅ myInt T",
+ "𝓤 int F",
+ "𝓤 ~int F",
+ "𝓤 myInt F",
+ "int ~int T",
+ "int myInt F",
+ "~int myInt F",
+ "myInt int F",
+ "myInt ~int T",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.subsetOf(y); got != want {
+ t.Errorf("%v.subsetOf(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
+
+func TestTermDisjoint(t *testing.T) {
+ for _, test := range []string{
+ "int int F",
+ "~int ~int F",
+ "int ~int F",
+ "int string T",
+ "int ~string T",
+ "int myInt T",
+ "~int ~string T",
+ "~int myInt F",
+ "string myInt T",
+ "~string myInt T",
+ } {
+ args := split(test, 3)
+ x := testTerm(args[0])
+ y := testTerm(args[1])
+ want := args[2] == "T"
+ if got := x.disjoint(y); got != want {
+ t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want)
+ }
+ // disjoint is symmetric
+ x, y = y, x
+ if got := x.disjoint(y); got != want {
+ t.Errorf("%v.disjoint(%v) = %v; want %v", x, y, got, want)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index e64d804c30b8ccc59a9c5532d714a624109c0cca..9121c2c1f64414bd0d45b205d39d3fa4bf44868a 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -10,14 +10,9 @@ import (
"cmd/compile/internal/syntax"
"fmt"
"go/constant"
- "sort"
- "strconv"
"strings"
)
-// Disabled by default, but enabled when running tests (via types_test.go).
-var acceptMethodTypeParams bool
-
// ident type-checks identifier e and initializes x with the value or type of e.
// If an error occurred, x.mode is set to invalid.
// For the meaning of def, see Checker.definedType, below.
@@ -30,9 +25,18 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
// Note that we cannot use check.lookup here because the returned scope
// may be different from obj.Parent(). See also Scope.LookupParent doc.
scope, obj := check.scope.LookupParent(e.Value, check.pos)
- if obj == nil {
+ switch obj {
+ case nil:
if e.Value == "_" {
- check.error(e, "cannot use _ as value or type")
+ // Blank identifiers are never declared, but the current identifier may
+ // be a placeholder for a receiver type parameter. In this case we can
+ // resolve its type and object from Checker.recvTParamMap.
+ if tpar := check.recvTParamMap[e]; tpar != nil {
+ x.mode = typexpr
+ x.typ = tpar
+ } else {
+ check.error(e, "cannot use _ as value or type")
+ }
} else {
if check.conf.CompilerErrorMessages {
check.errorf(e, "undefined: %s", e.Value)
@@ -41,6 +45,11 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
}
}
return
+ case universeAny, universeComparable:
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.errorf(e, "undeclared name: %s (requires version go1.18 or later)", e.Value)
+ return // avoid follow-on errors
+ }
}
check.recordUse(e, obj)
@@ -63,7 +72,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
// If so, mark the respective package as used.
// (This code is only needed for dot-imports. Without them,
// we only have to mark variables, see *Var case below).
- if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil {
+ if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil {
pkgName.used = true
}
@@ -130,40 +139,33 @@ func (check *Checker) typ(e syntax.Expr) Type {
}
// varType type-checks the type expression e and returns its type, or Typ[Invalid].
-// The type must not be an (uninstantiated) generic type and it must be ordinary
-// (see ordinaryType).
+// The type must not be an (uninstantiated) generic type and it must not be a
+// constraint interface.
func (check *Checker) varType(e syntax.Expr) Type {
typ := check.definedType(e, nil)
- check.ordinaryType(syntax.StartPos(e), typ)
- return typ
-}
-// ordinaryType reports an error if typ is an interface type containing
-// type lists or is (or embeds) the predeclared type comparable.
-func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
- // We don't want to call under() (via Interface) 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.
+ // If we have a type parameter there's nothing to do.
+ if isTypeParam(typ) {
+ return typ
+ }
+
+ // We don't want to call under() 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() {
- if t := asInterface(typ); t != nil {
- check.completeInterface(pos, t) // TODO(gri) is this the correct position?
- if t.allTypes != nil {
- check.softErrorf(pos, "interface contains type constraints (%s)", t.allTypes)
- return
- }
- if t.IsComparable() {
- check.softErrorf(pos, "interface is (or embeds) comparable")
+ if t, _ := under(typ).(*Interface); t != nil {
+ pos := syntax.StartPos(e)
+ tset := computeInterfaceTypeSet(check, pos, t) // TODO(gri) is this the correct position?
+ if !tset.IsMethodSet() {
+ if tset.comparable {
+ check.softErrorf(pos, "interface is (or embeds) comparable")
+ } else {
+ check.softErrorf(pos, "interface contains type constraints")
+ }
}
}
})
-}
-// anyType type-checks the type expression e and returns its type, or Typ[Invalid].
-// The type may be generic or instantiated.
-func (check *Checker) anyType(e syntax.Expr) Type {
- typ := check.typInternal(e, nil)
- assert(isTyped(typ))
- check.recordTypeAndValue(e, typexpr, typ, nil)
return typ
}
@@ -198,238 +200,6 @@ func (check *Checker) genericType(e syntax.Expr, reportErr bool) Type {
return typ
}
-// isubst returns an x with identifiers substituted per the substitution map smap.
-// isubst only handles the case of (valid) method receiver type expressions correctly.
-func isubst(x syntax.Expr, smap map[*syntax.Name]*syntax.Name) syntax.Expr {
- switch n := x.(type) {
- case *syntax.Name:
- if alt := smap[n]; alt != nil {
- return alt
- }
- // case *syntax.StarExpr:
- // X := isubst(n.X, smap)
- // if X != n.X {
- // new := *n
- // new.X = X
- // return &new
- // }
- case *syntax.Operation:
- if n.Op == syntax.Mul && n.Y == nil {
- X := isubst(n.X, smap)
- if X != n.X {
- new := *n
- new.X = X
- return &new
- }
- }
- case *syntax.IndexExpr:
- Index := isubst(n.Index, smap)
- if Index != n.Index {
- new := *n
- new.Index = Index
- return &new
- }
- case *syntax.ListExpr:
- var elems []syntax.Expr
- for i, elem := range n.ElemList {
- new := isubst(elem, smap)
- if new != elem {
- if elems == nil {
- elems = make([]syntax.Expr, len(n.ElemList))
- copy(elems, n.ElemList)
- }
- elems[i] = new
- }
- }
- if elems != nil {
- new := *n
- new.ElemList = elems
- return &new
- }
- case *syntax.ParenExpr:
- return isubst(n.X, smap) // no need to keep parentheses
- default:
- // Other receiver type expressions are invalid.
- // It's fine to ignore those here as they will
- // be checked elsewhere.
- }
- return x
-}
-
-// funcType type-checks a function or method type.
-func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []*syntax.Field, ftyp *syntax.FuncType) {
- check.openScope(ftyp, "function")
- check.scope.isFunc = true
- check.recordScope(ftyp, check.scope)
- sig.scope = check.scope
- defer check.closeScope()
-
- var recvTyp syntax.Expr // rewritten receiver type; valid if != nil
- if recvPar != nil {
- // collect generic receiver type parameters, if any
- // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
- // - the receiver specification acts as local declaration for its type parameters, which may be blank
- _, rname, rparams := check.unpackRecv(recvPar.Type, true)
- if len(rparams) > 0 {
- // Blank identifiers don't get declared and regular type-checking of the instantiated
- // parameterized receiver type expression fails in Checker.collectParams of receiver.
- // Identify blank type parameters and substitute each with a unique new identifier named
- // "n_" (where n is the parameter index) and which cannot conflict with any user-defined
- // name.
- var smap map[*syntax.Name]*syntax.Name // substitution map from "_" to "!n" identifiers
- for i, p := range rparams {
- if p.Value == "_" {
- new := *p
- new.Value = fmt.Sprintf("%d_", i)
- rparams[i] = &new // use n_ identifier instead of _ so it can be looked up
- if smap == nil {
- smap = make(map[*syntax.Name]*syntax.Name)
- }
- smap[p] = &new
- }
- }
- if smap != nil {
- // blank identifiers were found => use rewritten receiver type
- recvTyp = isubst(recvPar.Type, smap)
- }
- // TODO(gri) rework declareTypeParams
- sig.rparams = nil
- for _, rparam := range rparams {
- sig.rparams = check.declareTypeParam(sig.rparams, rparam)
- }
- // determine receiver type to get its type parameters
- // and the respective type parameter bounds
- var recvTParams []*TypeName
- if rname != nil {
- // recv should be a Named type (otherwise an error is reported elsewhere)
- // Also: Don't report an error via genericType since it will be reported
- // again when we type-check the signature.
- // TODO(gri) maybe the receiver should be marked as invalid instead?
- if recv := asNamed(check.genericType(rname, false)); recv != nil {
- recvTParams = recv.tparams
- }
- }
- // provide type parameter bounds
- // - only do this if we have the right number (otherwise an error is reported elsewhere)
- if len(sig.rparams) == len(recvTParams) {
- // We have a list of *TypeNames but we need a list of Types.
- list := make([]Type, len(sig.rparams))
- for i, t := range sig.rparams {
- list[i] = t.typ
- }
- smap := makeSubstMap(recvTParams, list)
- for i, tname := range sig.rparams {
- bound := recvTParams[i].typ.(*TypeParam).bound
- // bound is (possibly) parameterized in the context of the
- // receiver type declaration. Substitute parameters for the
- // current context.
- // TODO(gri) should we assume now that bounds always exist?
- // (no bound == empty interface)
- if bound != nil {
- bound = check.subst(tname.pos, bound, smap)
- tname.typ.(*TypeParam).bound = bound
- }
- }
- }
- }
- }
-
- if tparams != nil {
- sig.tparams = check.collectTypeParams(tparams)
- // Always type-check method type parameters but complain if they are not enabled.
- // (A separate check is needed when type-checking interface method signatures because
- // they don't have a receiver specification.)
- if recvPar != nil && !acceptMethodTypeParams {
- check.error(ftyp, "methods cannot have type parameters")
- }
- }
-
- // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
- // declarations and then squash that scope into the parent scope (and report any redeclarations at
- // that time).
- scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
- var recvList []*Var // TODO(gri) remove the need for making a list here
- if recvPar != nil {
- recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, recvTyp, false) // use rewritten receiver type, if any
- }
- params, variadic := check.collectParams(scope, ftyp.ParamList, nil, true)
- results, _ := check.collectParams(scope, ftyp.ResultList, nil, false)
- scope.Squash(func(obj, alt Object) {
- var err error_
- err.errorf(obj, "%s redeclared in this block", obj.Name())
- err.recordAltDecl(alt)
- check.report(&err)
- })
-
- if recvPar != nil {
- // recv parameter list present (may be empty)
- // spec: "The receiver is specified via an extra parameter section preceding the
- // method name. That parameter section must declare a single parameter, the receiver."
- var recv *Var
- switch len(recvList) {
- case 0:
- // error reported by resolver
- recv = NewParam(nopos, nil, "", Typ[Invalid]) // ignore recv below
- default:
- // more than one receiver
- check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
- fallthrough // continue with first receiver
- case 1:
- recv = recvList[0]
- }
-
- // TODO(gri) We should delay rtyp expansion to when we actually need the
- // receiver; thus all checks here should be delayed to later.
- rtyp, _ := deref(recv.typ)
- rtyp = expand(rtyp)
-
- // spec: "The receiver type must be of the form T or *T where T is a type name."
- // (ignore invalid types - error was reported before)
- if t := rtyp; t != Typ[Invalid] {
- var err string
- if T := asNamed(t); T != nil {
- // spec: "The type denoted by T is called the receiver base type; it must not
- // be a pointer or interface type and it must be declared in the same package
- // as the method."
- if T.obj.pkg != check.pkg {
- err = "type not defined in this package"
- if check.conf.CompilerErrorMessages {
- check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
- err = ""
- }
- } else {
- switch u := optype(T).(type) {
- case *Basic:
- // unsafe.Pointer is treated like a regular pointer
- if u.kind == UnsafePointer {
- err = "unsafe.Pointer"
- }
- case *Pointer, *Interface:
- err = "pointer or interface type"
- }
- }
- } else if T := asBasic(t); T != nil {
- err = "basic or unnamed type"
- if check.conf.CompilerErrorMessages {
- check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
- err = ""
- }
- } else {
- check.errorf(recv.pos, "invalid receiver type %s", recv.typ)
- }
- if err != "" {
- check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err)
- // ok to continue
- }
- }
- sig.recv = recv
- }
-
- sig.params = NewTuple(params...)
- sig.results = NewTuple(results...)
- sig.variadic = variadic
-}
-
// goTypeName returns the Go type name for typ and
// removes any occurrences of "types2." from that name.
func goTypeName(typ Type) string {
@@ -449,9 +219,7 @@ 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 = T.Underlying()
+ under = safeUnderlying(T)
}
if T == under {
check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T))
@@ -500,6 +268,9 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
}
case *syntax.IndexExpr:
+ if !check.allowVersion(check.pkg, 1, 18) {
+ check.versionErrorf(e.Pos(), "go1.18", "type instantiation")
+ }
return check.instantiatedType(e.X, unpackExpr(e.Index), def)
case *syntax.ParenExpr:
@@ -544,8 +315,16 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
case *syntax.Operation:
if e.Op == syntax.Mul && e.Y == nil {
typ := new(Pointer)
+ typ.base = Typ[Invalid] // avoid nil base in invalid recursive type declaration
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
}
@@ -583,7 +362,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
check.later(func() {
if !Comparable(typ.key) {
var why string
- if asTypeParam(typ.key) != nil {
+ if isTypeParam(typ.key) {
why = " (missing comparable constraint)"
}
check.errorf(e.Key, "invalid map key type %s%s", typ.key, why)
@@ -623,77 +402,109 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
return typ
}
-// typeOrNil type-checks the type expression (or nil value) e
-// and returns the type of e, or nil. If e is a type, it must
-// not be an (uninstantiated) generic type.
-// If e is neither a type nor nil, typeOrNil returns Typ[Invalid].
-// TODO(gri) should we also disallow non-var types?
-func (check *Checker) typOrNil(e syntax.Expr) Type {
- var x operand
- check.rawExpr(&x, e, nil)
- switch x.mode {
- case invalid:
- // ignore - error reported before
- case novalue:
- check.errorf(&x, "%s used as type", &x)
- case typexpr:
- check.instantiatedOperand(&x)
- return x.typ
- case nilvalue:
- return nil
- default:
- check.errorf(&x, "%s is not a type", &x)
+func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *Named) (res Type) {
+ if check.conf.Trace {
+ check.trace(x.Pos(), "-- instantiating %s with %s", x, xlist)
+ check.indent++
+ defer func() {
+ check.indent--
+ // Don't format the underlying here. It will always be nil.
+ check.trace(x.Pos(), "=> %s", res)
+ }()
}
- return Typ[Invalid]
-}
-func (check *Checker) instantiatedType(x syntax.Expr, targs []syntax.Expr, def *Named) Type {
- b := check.genericType(x, true) // TODO(gri) what about cycles?
- if b == Typ[Invalid] {
- return b // error already reported
- }
- base := asNamed(b)
- if base == nil {
- unreachable() // should have been caught by genericType
+ gtyp := check.genericType(x, true)
+ if gtyp == Typ[Invalid] {
+ return gtyp // error already reported
}
- // create a new type instance rather than instantiate the type
- // TODO(gri) should do argument number check here rather than
- // when instantiating the type?
- typ := new(instance)
- def.setUnderlying(typ)
-
- typ.check = check
- typ.pos = x.Pos()
- typ.base = base
+ orig, _ := gtyp.(*Named)
+ if orig == nil {
+ panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp))
+ }
- // evaluate arguments (always)
- typ.targs = check.typeList(targs)
- if typ.targs == nil {
+ // evaluate arguments
+ targs := check.typeList(xlist)
+ if targs == nil {
def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
return Typ[Invalid]
}
- // determine argument positions (for error reporting)
- typ.poslist = make([]syntax.Pos, len(targs))
- for i, arg := range targs {
- typ.poslist[i] = syntax.StartPos(arg)
+ // create the instance
+ ctxt := check.bestContext(nil)
+ h := ctxt.instanceHash(orig, targs)
+ // targs may be incomplete, and require inference. In any case we should de-duplicate.
+ inst, _ := ctxt.lookup(h, orig, targs).(*Named)
+ // 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(), orig.obj.pkg, orig.obj.name, nil)
+ inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved
+ inst.targs = newTypeList(targs)
+ inst = ctxt.update(h, orig, targs, inst).(*Named)
+ }
+ def.setUnderlying(inst)
+
+ inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, []*Func) {
+ tparams := orig.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())
}
- // make sure we check instantiation works at least once
- // and that the resulting type is valid
+ // orig.tparams may not be set up, so we need to do expansion later.
check.later(func() {
- t := typ.expand()
- check.validType(t, 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(ctxt)
+ // 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(xlist) {
+ pos = syntax.StartPos(xlist[i])
+ }
+ check.softErrorf(pos, "%s", err)
+ } else {
+ check.mono.recordInstance(check.pkg, x.Pos(), inst.tparams.list(), inst.targs.list(), xlist)
+ }
+ }
+
+ check.validType(inst, nil)
})
- return typ
+ return inst
}
// arrayLength type-checks the array length expression e
// and returns the constant length >= 0, or a value < 0
// to indicate an error (and thus an unknown length).
func (check *Checker) arrayLength(e syntax.Expr) int64 {
+ // If e is an undeclared identifier, the array declaration might be an
+ // attempt at a parameterized type declaration with missing constraint.
+ // Provide a better error message than just "undeclared name: X".
+ if name, _ := e.(*syntax.Name); name != nil && check.lookup(name.Value) == nil {
+ check.errorf(name, "undeclared name %s for array length", name.Value)
+ return -1
+ }
+
var x operand
check.expr(&x, e)
if x.mode != constant_ {
@@ -702,6 +513,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 {
}
return -1
}
+
if isUntyped(x.typ) || isInteger(x.typ) {
if val := constant.ToInt(x.val); val.Kind() == constant.Int {
if representableConst(val, check, Typ[Int], nil) {
@@ -713,6 +525,7 @@ func (check *Checker) arrayLength(e syntax.Expr) int64 {
}
}
}
+
check.errorf(&x, "array length %s must be integer", &x)
return -1
}
@@ -732,537 +545,3 @@ func (check *Checker) typeList(list []syntax.Expr) []Type {
}
return res
}
-
-// collectParams declares the parameters of list in scope and returns the corresponding
-// variable list. If type0 != nil, it is used instead of the first type in list.
-func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 syntax.Expr, variadicOk bool) (params []*Var, variadic bool) {
- if list == nil {
- return
- }
-
- var named, anonymous bool
-
- var typ Type
- var prev syntax.Expr
- for i, field := range list {
- ftype := field.Type
- // type-check type of grouped fields only once
- if ftype != prev {
- prev = ftype
- if i == 0 && type0 != nil {
- ftype = type0
- }
- if t, _ := ftype.(*syntax.DotsType); t != nil {
- ftype = t.Elem
- if variadicOk && i == len(list)-1 {
- variadic = true
- } else {
- check.softErrorf(t, "can only use ... with final parameter in list")
- // ignore ... and continue
- }
- }
- typ = check.varType(ftype)
- }
- // The parser ensures that f.Tag is nil and we don't
- // care if a constructed AST contains a non-nil tag.
- if field.Name != nil {
- // named parameter
- name := field.Name.Value
- if name == "" {
- check.error(field.Name, invalidAST+"anonymous parameter")
- // ok to continue
- }
- par := NewParam(field.Name.Pos(), check.pkg, name, typ)
- check.declare(scope, field.Name, par, scope.pos)
- params = append(params, par)
- named = true
- } else {
- // anonymous parameter
- par := NewParam(ftype.Pos(), check.pkg, "", typ)
- check.recordImplicit(field, par)
- params = append(params, par)
- anonymous = true
- }
- }
-
- if named && anonymous {
- check.error(list[0], invalidAST+"list contains both named and anonymous parameters")
- // ok to continue
- }
-
- // For a variadic function, change the last parameter's type from T to []T.
- // Since we type-checked T rather than ...T, we also need to retro-actively
- // record the type for ...T.
- if variadic {
- last := params[len(params)-1]
- last.typ = &Slice{elem: last.typ}
- check.recordTypeAndValue(list[len(list)-1].Type, typexpr, last.typ, nil)
- }
-
- return
-}
-
-func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
- if alt := oset.insert(obj); alt != nil {
- var err error_
- err.errorf(pos, "%s redeclared", obj.Name())
- err.recordAltDecl(alt)
- check.report(&err)
- return false
- }
- return true
-}
-
-func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) {
- var tname *syntax.Name // most recent "type" name
- var types []syntax.Expr
- for _, f := range iface.MethodList {
- if f.Name != nil {
- // We have a method with name f.Name, or a type
- // of a type list (f.Name.Value == "type").
- name := f.Name.Value
- if name == "_" {
- if check.conf.CompilerErrorMessages {
- check.error(f.Name, "methods must have a unique non-blank name")
- } else {
- check.error(f.Name, "invalid method name _")
- }
- continue // ignore
- }
-
- if name == "type" {
- // Always collect all type list entries, even from
- // different type lists, under the assumption that
- // the author intended to include all types.
- types = append(types, f.Type)
- if tname != nil && tname != f.Name {
- check.error(f.Name, "cannot have multiple type lists in an interface")
- }
- tname = f.Name
- continue
- }
-
- typ := check.typ(f.Type)
- sig, _ := typ.(*Signature)
- if sig == nil {
- if typ != Typ[Invalid] {
- check.errorf(f.Type, invalidAST+"%s is not a method signature", typ)
- }
- continue // ignore
- }
-
- // Always type-check method type parameters but complain if they are not enabled.
- // (This extra check is needed here because interface method signatures don't have
- // a receiver specification.)
- if sig.tparams != nil && !acceptMethodTypeParams {
- check.error(f.Type, "methods cannot have type parameters")
- }
-
- // use named receiver type if available (for better error messages)
- var recvTyp Type = ityp
- if def != nil {
- recvTyp = def
- }
- sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
-
- m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
- check.recordDef(f.Name, m)
- ityp.methods = append(ityp.methods, m)
- } else {
- // We have an embedded type. completeInterface will
- // eventually verify that we have an interface.
- ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
- check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
- }
- }
-
- // type constraints
- ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types))
-
- if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
- // empty interface
- ityp.allMethods = markComplete
- return
- }
-
- // sort for API stability
- sortMethods(ityp.methods)
- sortTypes(ityp.embeddeds)
-
- check.later(func() { check.completeInterface(iface.Pos(), ityp) })
-}
-
-func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) {
- if ityp.allMethods != nil {
- return
- }
-
- // completeInterface may be called via the LookupFieldOrMethod,
- // MissingMethod, Identical, or IdenticalIgnoreTags external API
- // in which case check will be nil. In this case, type-checking
- // must be finished and all interfaces should have been completed.
- if check == nil {
- panic("internal error: incomplete interface")
- }
-
- if check.conf.Trace {
- // Types don't generally have position information.
- // If we don't have a valid pos provided, try to use
- // one close enough.
- if !pos.IsKnown() && len(ityp.methods) > 0 {
- pos = ityp.methods[0].pos
- }
-
- check.trace(pos, "complete %s", ityp)
- check.indent++
- defer func() {
- check.indent--
- check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
- }()
- }
-
- // An infinitely expanding interface (due to a cycle) is detected
- // elsewhere (Checker.validType), so here we simply assume we only
- // have valid interfaces. Mark the interface as complete to avoid
- // infinite recursion if the validType check occurs later for some
- // reason.
- ityp.allMethods = markComplete
-
- // Methods of embedded interfaces are collected unchanged; i.e., the identity
- // of a method I.m's Func Object of an interface I is the same as that of
- // the method m in an interface that embeds interface I. On the other hand,
- // if a method is embedded via multiple overlapping embedded interfaces, we
- // don't provide a guarantee which "original m" got chosen for the embedding
- // interface. See also issue #34421.
- //
- // If we don't care to provide this identity guarantee anymore, instead of
- // reusing the original method in embeddings, we can clone the method's Func
- // Object and give it the position of a corresponding embedded interface. Then
- // we can get rid of the mpos map below and simply use the cloned method's
- // position.
-
- var seen objset
- var methods []*Func
- mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
- addMethod := func(pos syntax.Pos, m *Func, explicit bool) {
- switch other := seen.insert(m); {
- case other == nil:
- methods = append(methods, m)
- mpos[m] = pos
- case explicit:
- var err error_
- err.errorf(pos, "duplicate method %s", m.name)
- err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
- check.report(&err)
- default:
- // We have a duplicate method name in an embedded (not explicitly declared) method.
- // Check method signatures after all types are computed (issue #33656).
- // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
- // error here as well (even though we could do it eagerly) because it's the same
- // error message.
- check.later(func() {
- if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
- var err error_
- err.errorf(pos, "duplicate method %s", m.name)
- err.errorf(mpos[other.(*Func)], "other declaration of %s", m.name)
- check.report(&err)
- }
- })
- }
- }
-
- for _, m := range ityp.methods {
- addMethod(m.pos, m, true)
- }
-
- // collect types
- allTypes := ityp.types
-
- posList := check.posMap[ityp]
- for i, typ := range ityp.embeddeds {
- pos := posList[i] // embedding position
- utyp := under(typ)
- etyp := asInterface(utyp)
- if etyp == nil {
- if utyp != Typ[Invalid] {
- var format string
- if _, ok := utyp.(*TypeParam); ok {
- format = "%s is a type parameter, not an interface"
- } else {
- format = "%s is not an interface"
- }
- check.errorf(pos, format, typ)
- }
- continue
- }
- check.completeInterface(pos, etyp)
- for _, m := range etyp.allMethods {
- addMethod(pos, m, false) // use embedding position pos rather than m.pos
- }
- allTypes = intersect(allTypes, etyp.allTypes)
- }
-
- if methods != nil {
- sortMethods(methods)
- ityp.allMethods = methods
- }
- ityp.allTypes = allTypes
-}
-
-// intersect computes the intersection of the types x and y.
-// Note: A incomming nil type stands for the top type. A top
-// type result is returned as nil.
-func intersect(x, y Type) (r Type) {
- defer func() {
- if r == theTop {
- r = nil
- }
- }()
-
- switch {
- case x == theBottom || y == theBottom:
- return theBottom
- case x == nil || x == theTop:
- return y
- case y == nil || x == theTop:
- return x
- }
-
- xtypes := unpack(x)
- ytypes := unpack(y)
- // Compute the list rtypes which includes only
- // types that are in both xtypes and ytypes.
- // Quadratic algorithm, but good enough for now.
- // TODO(gri) fix this
- var rtypes []Type
- for _, x := range xtypes {
- if includes(ytypes, x) {
- rtypes = append(rtypes, x)
- }
- }
-
- if rtypes == nil {
- return theBottom
- }
- return NewSum(rtypes)
-}
-
-func sortTypes(list []Type) {
- sort.Stable(byUniqueTypeName(list))
-}
-
-// byUniqueTypeName named type lists can be sorted by their unique type names.
-type byUniqueTypeName []Type
-
-func (a byUniqueTypeName) Len() int { return len(a) }
-func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
-func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func sortName(t Type) string {
- if named := asNamed(t); named != nil {
- return named.obj.Id()
- }
- return ""
-}
-
-func sortMethods(list []*Func) {
- sort.Sort(byUniqueMethodName(list))
-}
-
-func assertSortedMethods(list []*Func) {
- if !debug {
- panic("internal error: assertSortedMethods called outside debug mode")
- }
- if !sort.IsSorted(byUniqueMethodName(list)) {
- panic("internal error: methods not sorted")
- }
-}
-
-// byUniqueMethodName method lists can be sorted by their unique method names.
-type byUniqueMethodName []*Func
-
-func (a byUniqueMethodName) Len() int { return len(a) }
-func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(a[j]) }
-func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-func (check *Checker) tag(t *syntax.BasicLit) string {
- // If t.Bad, an error was reported during parsing.
- if t != nil && !t.Bad {
- if t.Kind == syntax.StringLit {
- if val, err := strconv.Unquote(t.Value); err == nil {
- return val
- }
- }
- check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
- }
- return ""
-}
-
-func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
- if e.FieldList == nil {
- return
- }
-
- // struct fields and tags
- var fields []*Var
- var tags []string
-
- // for double-declaration checks
- var fset objset
-
- // current field typ and tag
- var typ Type
- var tag string
- add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
- if tag != "" && tags == nil {
- tags = make([]string, len(fields))
- }
- if tags != nil {
- tags = append(tags, tag)
- }
-
- name := ident.Value
- fld := NewField(pos, check.pkg, name, typ, embedded)
- // spec: "Within a struct, non-blank field names must be unique."
- if name == "_" || check.declareInSet(&fset, pos, fld) {
- fields = append(fields, fld)
- check.recordDef(ident, fld)
- }
- }
-
- // addInvalid adds an embedded field of invalid type to the struct for
- // fields with errors; this keeps the number of struct fields in sync
- // with the source as long as the fields are _ or have different names
- // (issue #25627).
- addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
- typ = Typ[Invalid]
- tag = ""
- add(ident, true, pos)
- }
-
- var prev syntax.Expr
- for i, f := range e.FieldList {
- // Fields declared syntactically with the same type (e.g.: a, b, c T)
- // share the same type expression. Only check type if it's a new type.
- if i == 0 || f.Type != prev {
- typ = check.varType(f.Type)
- prev = f.Type
- }
- tag = ""
- if i < len(e.TagList) {
- tag = check.tag(e.TagList[i])
- }
- if f.Name != nil {
- // named field
- add(f.Name, false, f.Name.Pos())
- } else {
- // embedded field
- // spec: "An embedded type must be specified as a type name T or as a
- // pointer to a non-interface type name *T, and T itself may not be a
- // pointer type."
- pos := syntax.StartPos(f.Type)
- name := embeddedFieldIdent(f.Type)
- if name == nil {
- check.errorf(pos, "invalid embedded field type %s", f.Type)
- name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
- addInvalid(name, pos)
- continue
- }
- add(name, true, pos)
-
- // Because we have a name, typ must be of the form T or *T, where T is the name
- // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
- // We must delay this check to the end because we don't want to instantiate
- // (via under(t)) a possibly incomplete type.
- embeddedTyp := typ // for closure below
- embeddedPos := pos
- check.later(func() {
- t, isPtr := deref(embeddedTyp)
- switch t := optype(t).(type) {
- case *Basic:
- if t == Typ[Invalid] {
- // error was reported before
- return
- }
- // unsafe.Pointer is treated like a regular pointer
- if t.kind == UnsafePointer {
- check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
- }
- case *Pointer:
- check.error(embeddedPos, "embedded field type cannot be a pointer")
- case *Interface:
- if isPtr {
- check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
- }
- }
- })
- }
- }
-
- styp.fields = fields
- styp.tags = tags
-}
-
-func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
- switch e := e.(type) {
- case *syntax.Name:
- return e
- case *syntax.Operation:
- if base := ptrBase(e); base != nil {
- // *T is valid, but **T is not
- if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
- return embeddedFieldIdent(e.X)
- }
- }
- case *syntax.SelectorExpr:
- return e.Sel
- case *syntax.IndexExpr:
- return embeddedFieldIdent(e.X)
- }
- return nil // invalid embedded field
-}
-
-func (check *Checker) collectTypeConstraints(pos syntax.Pos, types []syntax.Expr) []Type {
- list := make([]Type, 0, len(types)) // assume all types are correct
- for _, texpr := range types {
- if texpr == nil {
- check.error(pos, invalidAST+"missing type constraint")
- continue
- }
- list = append(list, check.varType(texpr))
- }
-
- // Ensure that each type is only present once in the type list. Types may be
- // interfaces, which may not be complete yet. It's ok to do this check at the
- // end because it's not a requirement for correctness of the code.
- // Note: This is a quadratic algorithm, but type lists tend to be short.
- check.later(func() {
- for i, t := range list {
- if t := asInterface(t); t != nil {
- check.completeInterface(types[i].Pos(), t)
- }
- if includes(list[:i], t) {
- check.softErrorf(types[i], "duplicate type %s in type list", t)
- }
- }
- })
-
- return list
-}
-
-// includes reports whether typ is in list
-func includes(list []Type, typ Type) bool {
- for _, e := range list {
- if Identical(typ, e) {
- return true
- }
- }
- return false
-}
-
-func ptrBase(x *syntax.Operation) syntax.Expr {
- if x.Op == syntax.Mul && x.Y == nil {
- return x.X
- }
- return nil
-}
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index e1832bbb2a36232cd00a04e2e195ae51711938fe..f663beec38bbcca373a5ef51d52715ba6e9a737a 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -6,7 +6,10 @@
package types2
-import "bytes"
+import (
+ "bytes"
+ "fmt"
+)
// The unifier maintains two separate sets of type parameters x and y
// which are used to resolve type parameters in the x and y arguments
@@ -34,7 +37,6 @@ import "bytes"
// and the respective types inferred for each type parameter.
// A unifier is created by calling newUnifier.
type unifier struct {
- check *Checker
exact bool
x, y tparamsList // x and y must initialized via tparamsList.init
types []Type // inferred types, shared by x and y
@@ -45,8 +47,8 @@ type unifier struct {
// exactly. If exact is not set, a named type's underlying type
// is considered if unification would fail otherwise, and the
// direction of channels is ignored.
-func newUnifier(check *Checker, exact bool) *unifier {
- u := &unifier{check: check, exact: exact}
+func newUnifier(exact bool) *unifier {
+ u := &unifier{exact: exact}
u.x.unifier = u
u.y.unifier = u
return u
@@ -60,7 +62,7 @@ func (u *unifier) unify(x, y Type) bool {
// A tparamsList describes a list of type parameters and the types inferred for them.
type tparamsList struct {
unifier *unifier
- tparams []*TypeName
+ tparams []*TypeParam
// For each tparams element, there is a corresponding type slot index in indices.
// index < 0: unifier.types[-index-1] == nil
// index == 0: no type slot allocated yet
@@ -74,29 +76,30 @@ type tparamsList struct {
// String returns a string representation for a tparamsList. For debugging.
func (d *tparamsList) String() string {
var buf bytes.Buffer
- buf.WriteByte('[')
- for i, tname := range d.tparams {
+ w := newTypeWriter(&buf, nil)
+ w.byte('[')
+ for i, tpar := range d.tparams {
if i > 0 {
- buf.WriteString(", ")
+ w.string(", ")
}
- writeType(&buf, tname.typ, nil, nil)
- buf.WriteString(": ")
- writeType(&buf, d.at(i), nil, nil)
+ w.typ(tpar)
+ w.string(": ")
+ w.typ(d.at(i))
}
- buf.WriteByte(']')
+ w.byte(']')
return buf.String()
}
// init initializes d with the given type parameters.
// The type parameters must be in the order in which they appear in their declaration
// (this ensures that the tparams indices match the respective type parameter index).
-func (d *tparamsList) init(tparams []*TypeName) {
+func (d *tparamsList) init(tparams []*TypeParam) {
if len(tparams) == 0 {
return
}
if debug {
for i, tpar := range tparams {
- assert(i == tpar.typ.(*TypeParam).index)
+ assert(i == tpar.index)
}
}
d.tparams = tparams
@@ -105,7 +108,7 @@ func (d *tparamsList) init(tparams []*TypeName) {
// join unifies the i'th type parameter of x with the j'th type parameter of y.
// If both type parameters already have a type associated with them and they are
-// not joined, join fails and return false.
+// not joined, join fails and returns false.
func (u *unifier) join(i, j int) bool {
ti := u.x.indices[i]
tj := u.y.indices[j]
@@ -129,6 +132,7 @@ func (u *unifier) join(i, j int) bool {
break
case ti > 0 && tj > 0:
// Both type parameters have (possibly different) inferred types. Cannot join.
+ // TODO(gri) Should we check if types are identical? Investigate.
return false
case ti > 0:
// Only the type parameter for x has an inferred type. Use x slot for y.
@@ -148,10 +152,23 @@ func (u *unifier) join(i, j int) bool {
// If typ is a type parameter of d, index returns the type parameter index.
// Otherwise, the result is < 0.
func (d *tparamsList) index(typ Type) int {
- if t, ok := typ.(*TypeParam); ok {
- if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
- return i
- }
+ if tpar, ok := typ.(*TypeParam); ok {
+ return tparamIndex(d.tparams, tpar)
+ }
+ return -1
+}
+
+// If tpar is a type parameter in list, tparamIndex returns the type parameter index.
+// Otherwise, the result is < 0. tpar must not be nil.
+func tparamIndex(list []*TypeParam, tpar *TypeParam) int {
+ // Once a type parameter is bound its index is >= 0. However, there are some
+ // code paths (namely tracing and type hashing) by which it is possible to
+ // arrive here with a type parameter that has not been bound, hence the check
+ // for 0 <= i below.
+ // TODO(rfindley): investigate a better approach for guarding against using
+ // unbound type parameters.
+ if i := tpar.index; 0 <= i && i < len(list) && list[i] == tpar {
+ return i
}
return -1
}
@@ -216,26 +233,20 @@ func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool {
}
// nify implements the core unification algorithm which is an
-// adapted version of Checker.identical0. For changes to that
+// adapted version of Checker.identical. For changes to that
// code the corresponding changes should be made here.
// Must not be called directly from outside the unifier.
func (u *unifier) nify(x, y Type, p *ifacePair) bool {
- // types must be expanded for comparison
- x = expand(x)
- y = expand(y)
-
if !u.exact {
// 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.
- // basic types and type parameters. We use asNamed because we only
- // want *Named types.)
- switch {
- case !isNamed(x) && y != nil && asNamed(y) != nil:
- return u.nify(x, under(y), p)
- case x != nil && asNamed(x) != nil && !isNamed(y):
- return u.nify(under(x), y, p)
+ // (We use !hasName to exclude any type with a name, including
+ // basic types and type parameters; the rest are unamed types.)
+ if nx, _ := x.(*Named); nx != nil && !hasName(y) {
+ return u.nify(nx.under(), y, p)
+ } else if ny, _ := y.(*Named); ny != nil && !hasName(x) {
+ return u.nify(x, ny.under(), p)
}
}
@@ -352,25 +363,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
u.nify(x.results, y.results, p)
}
- case *Sum:
- // This should not happen with the current internal use of sum types.
- panic("type inference across sum types not implemented")
-
case *Interface:
// Two interface types are identical if they have the same set of methods with
// the same names and identical function types. Lower-case method names from
// different packages are always different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
- // If identical0 is called (indirectly) via an external API entry point
- // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
- // that case, interfaces are expected to be complete and lazy completion
- // here is not needed.
- if u.check != nil {
- u.check.completeInterface(nopos, x)
- u.check.completeInterface(nopos, y)
+ xset := x.typeSet()
+ yset := y.typeSet()
+ if !xset.terms.equal(yset.terms) {
+ return false
}
- a := x.allMethods
- b := y.allMethods
+ a := xset.methods
+ b := yset.methods
if len(a) == len(b) {
// Interface types are the only types where cycles can occur
// that are not "terminated" via named types; and such cycles
@@ -428,19 +432,18 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
}
case *Named:
- // Two named types are identical if their type names originate
- // in the same type declaration.
- // if y, ok := y.(*Named); ok {
- // return x.obj == y.obj
- // }
+ // TODO(gri) This code differs now from the parallel code in Checker.identical. Investigate.
if y, ok := y.(*Named); ok {
+ xargs := x.targs.list()
+ yargs := y.targs.list()
+
// TODO(gri) This is not always correct: two types may have the same names
// in the same package if one of them is nested in a function.
// Extremely unlikely but we need an always correct solution.
if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name {
- assert(len(x.targs) == len(y.targs))
- for i, x := range x.targs {
- if !u.nify(x, y.targs[i], p) {
+ assert(len(xargs) == len(yargs))
+ for i, x := range xargs {
+ if !u.nify(x, yargs[i], p) {
return false
}
}
@@ -454,15 +457,11 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
// are identical if they originate in the same declaration.
return x == y
- // case *instance:
- // unreachable since types are expanded
-
case nil:
// avoid a crash in case of nil type
default:
- u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)
- unreachable()
+ panic(fmt.Sprintf("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams))
}
return false
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
new file mode 100644
index 0000000000000000000000000000000000000000..97581fe863083ef21449b08db081bd6ac1eb3705
--- /dev/null
+++ b/src/cmd/compile/internal/types2/union.go
@@ -0,0 +1,157 @@
+// 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"
+
+// ----------------------------------------------------------------------------
+// API
+
+// A Union represents a union of terms embedded in an interface.
+type Union struct {
+ terms []*Term // list of syntactical terms (not a canonicalized termlist)
+ tset *_TypeSet // type set described by this union, computed lazily
+}
+
+// NewUnion returns a new Union type with the given terms.
+// It is an error to create an empty union; they are syntactically not possible.
+func NewUnion(terms []*Term) *Union {
+ if len(terms) == 0 {
+ panic("empty union")
+ }
+ return &Union{terms, nil}
+}
+
+func (u *Union) Len() int { return len(u.terms) }
+func (u *Union) Term(i int) *Term { return u.terms[i] }
+
+func (u *Union) Underlying() Type { return u }
+func (u *Union) String() string { return TypeString(u, nil) }
+
+// A Term represents a term in a Union.
+type Term term
+
+// NewTerm returns a new union term.
+func NewTerm(tilde bool, typ Type) *Term { return &Term{tilde, typ} }
+
+func (t *Term) Tilde() bool { return t.tilde }
+func (t *Term) Type() Type { return t.typ }
+func (t *Term) String() string { return (*term)(t).String() }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+// Avoid excessive type-checking times due to quadratic termlist operations.
+const maxTermCount = 100
+
+// parseUnion parses uexpr as a union of expressions.
+// The result is a Union type, or Typ[Invalid] for some errors.
+func parseUnion(check *Checker, uexpr syntax.Expr) Type {
+ tlist := flattenUnion(nil, uexpr)
+
+ var terms []*Term
+ for _, x := range tlist {
+ tilde, typ := parseTilde(check, x)
+ if len(tlist) == 1 && !tilde {
+ // Single type. Ok to return early because all relevant
+ // checks have been performed in parseTilde (no need to
+ // run through term validity check below).
+ return typ // typ already recorded through check.typ in parseTilde
+ }
+ if len(terms) >= maxTermCount {
+ check.errorf(x, "cannot handle more than %d union terms (implementation limitation)", maxTermCount)
+ check.recordTypeAndValue(uexpr, typexpr, Typ[Invalid], nil)
+ return Typ[Invalid]
+ }
+ terms = append(terms, NewTerm(tilde, typ))
+ }
+
+ // Check validity of terms.
+ // Do this check later because it requires types to be set up.
+ // Note: This is a quadratic algorithm, but unions tend to be short.
+ check.later(func() {
+ for i, t := range terms {
+ if t.typ == Typ[Invalid] {
+ continue
+ }
+
+ u := under(t.typ)
+ f, _ := u.(*Interface)
+ if t.tilde {
+ if f != nil {
+ 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(tlist[i], "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
+ continue // don't report another error for t
+ }
+ }
+
+ // Stand-alone embedded interfaces are ok and are handled by the single-type case
+ // 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(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(tlist[i], "overlapping terms %s and %s", t, terms[j])
+ }
+ }
+ })
+
+ u := &Union{terms, nil}
+ check.recordTypeAndValue(uexpr, typexpr, u, nil)
+ return u
+}
+
+func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
+ if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde {
+ x = op.X
+ tilde = true
+ }
+ typ = check.typ(x)
+ // Embedding stand-alone type parameters is not permitted (issue #47127).
+ // We don't need this restriction anymore if we make the underlying type of a type
+ // parameter its constraint interface: if we embed a lone type parameter, we will
+ // simply use its underlying type (like we do for other named, embedded interfaces),
+ // and since the underlying type is an interface the embedding is well defined.
+ if isTypeParam(typ) {
+ check.error(x, "cannot embed a type parameter")
+ typ = Typ[Invalid]
+ }
+ return
+}
+
+// overlappingTerm reports the index of the term x in terms which is
+// overlapping (not disjoint) from y. The result is < 0 if there is no
+// such term.
+func overlappingTerm(terms []*Term, y *Term) int {
+ for i, x := range terms {
+ // disjoint requires non-nil, non-top arguments
+ if debug {
+ if x == nil || x.typ == nil || y == nil || y.typ == nil {
+ panic("empty or top union term")
+ }
+ }
+ if !(*term)(x).disjoint((*term)(y)) {
+ return i
+ }
+ }
+ return -1
+}
+
+func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
+ if o, _ := x.(*syntax.Operation); o != nil && o.Op == syntax.Or {
+ list = flattenUnion(list, o.X)
+ x = o.Y
+ }
+ return append(list, x)
+}
diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go
index 76d4e55e84b59127dd4d3b5d9335a37c8369d0a8..c16ae3f63eafd0cc90ec46b6e3a6b94b1ff52cec 100644
--- a/src/cmd/compile/internal/types2/universe.go
+++ b/src/cmd/compile/internal/types2/universe.go
@@ -20,11 +20,12 @@ var Universe *Scope
var Unsafe *Package
var (
- universeIota *Const
- universeByte *Basic // uint8 alias, but has name "byte"
- universeRune *Basic // int32 alias, but has name "rune"
- universeAny *Interface
- universeError *Named
+ universeIota Object
+ universeByte Type // uint8 alias, but has name "byte"
+ universeRune Type // int32 alias, but has name "rune"
+ universeAny Object
+ universeError Type
+ universeComparable Object
)
// Typ contains the predeclared *Basic types indexed by their
@@ -77,20 +78,33 @@ func defPredeclaredTypes() {
def(NewTypeName(nopos, nil, t.name, t))
}
- // any
- // (Predeclared and entered into universe scope so we do all the
- // usual checks; but removed again from scope later since it's
- // only visible as constraint in a type parameter list.)
- def(NewTypeName(nopos, nil, "any", &emptyInterface))
+ // type any = interface{}
+ // Note: don't use &emptyInterface for the type of any. Using a unique
+ // pointer allows us to detect any and format it as "any" rather than
+ // interface{}, which clarifies user-facing error messages significantly.
+ def(NewTypeName(nopos, nil, "any", &Interface{complete: true, tset: &topTypeSet}))
- // Error has a nil package in its qualified name since it is in no package
+ // type error interface{ Error() string }
{
+ obj := NewTypeName(nopos, nil, "error", nil)
+ obj.setColor(black)
res := NewVar(nopos, nil, "", Typ[String])
- sig := &Signature{results: NewTuple(res)}
+ sig := NewSignatureType(nil, nil, nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)
- typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()}
+ ityp := &Interface{nil, obj, []*Func{err}, nil, nil, false, true, nil}
+ computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset
+ typ := NewNamed(obj, ityp, nil)
sig.recv = NewVar(nopos, nil, "", typ)
- def(NewTypeName(nopos, nil, "error", typ))
+ def(obj)
+ }
+
+ // type comparable interface{ /* type set marked comparable */ }
+ {
+ obj := NewTypeName(nopos, nil, "comparable", nil)
+ obj.setColor(black)
+ ityp := &Interface{nil, obj, nil, nil, nil, false, true, &_TypeSet{true, nil, allTermlist}}
+ NewNamed(obj, ityp, nil)
+ def(obj)
}
}
@@ -200,33 +214,6 @@ func DefPredeclaredTestFuncs() {
def(newBuiltin(_Trace))
}
-func defPredeclaredComparable() {
- // The "comparable" interface can be imagined as defined like
- //
- // type comparable interface {
- // == () untyped bool
- // != () untyped bool
- // }
- //
- // == and != cannot be user-declared but we can declare
- // a magic method == and check for its presence when needed.
-
- // Define interface { == () }. We don't care about the signature
- // for == so leave it empty except for the receiver, which is
- // set up later to match the usual interface method assumptions.
- sig := new(Signature)
- eql := NewFunc(nopos, nil, "==", sig)
- iface := NewInterfaceType([]*Func{eql}, nil).Complete()
-
- // set up the defined type for the interface
- obj := NewTypeName(nopos, nil, "comparable", nil)
- named := NewNamed(obj, iface, nil)
- obj.color_ = black
- sig.recv = NewVar(nopos, nil, "", named) // complete == signature
-
- def(obj)
-}
-
func init() {
Universe = NewScope(nil, nopos, nopos, "universe")
Unsafe = NewPackage("unsafe", "unsafe")
@@ -236,16 +223,13 @@ func init() {
defPredeclaredConsts()
defPredeclaredNil()
defPredeclaredFuncs()
- defPredeclaredComparable()
-
- universeIota = Universe.Lookup("iota").(*Const)
- universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
- universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
- universeAny = Universe.Lookup("any").(*TypeName).typ.(*Interface)
- universeError = Universe.Lookup("error").(*TypeName).typ.(*Named)
- // "any" is only visible as constraint in a type parameter list
- delete(Universe.elems, "any")
+ universeIota = Universe.Lookup("iota")
+ universeByte = Universe.Lookup("byte").Type()
+ universeRune = Universe.Lookup("rune").Type()
+ universeAny = Universe.Lookup("any")
+ universeError = Universe.Lookup("error").Type()
+ universeComparable = Universe.Lookup("comparable")
}
// Objects with names containing blanks are internal and not entered into
@@ -259,7 +243,7 @@ func def(obj Object) {
return // nothing to do
}
// fix Obj link for named types
- if typ := asNamed(obj.Type()); typ != nil {
+ if typ, _ := obj.Type().(*Named); typ != nil {
typ.obj = obj.(*TypeName)
}
// exported identifiers go into package unsafe
@@ -277,6 +261,6 @@ func def(obj Object) {
}
}
if scope.Insert(obj) != nil {
- panic("internal error: double declaration")
+ panic("double declaration of predeclared identifier")
}
}
diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go
index d9d18b6f7a7f487e88320324fd7f457b5704b023..b649f09c3aa95ad333e1dcad2e4988ea0d549ff1 100644
--- a/src/cmd/compile/internal/types2/version.go
+++ b/src/cmd/compile/internal/types2/version.go
@@ -21,7 +21,7 @@ func (check *Checker) langCompat(lit *syntax.BasicLit) {
}
// len(s) > 2
if strings.Contains(s, "_") {
- check.error(lit, "underscores in numeric literals requires go1.13 or later")
+ check.versionErrorf(lit, "go1.13", "underscores in numeric literals")
return
}
if s[0] != '0' {
@@ -29,15 +29,15 @@ func (check *Checker) langCompat(lit *syntax.BasicLit) {
}
radix := s[1]
if radix == 'b' || radix == 'B' {
- check.error(lit, "binary literals requires go1.13 or later")
+ check.versionErrorf(lit, "go1.13", "binary literals")
return
}
if radix == 'o' || radix == 'O' {
- check.error(lit, "0o/0O-style octal literals requires go1.13 or later")
+ check.versionErrorf(lit, "go1.13", "0o/0O-style octal literals")
return
}
if lit.Kind != syntax.IntLit && (radix == 'x' || radix == 'X') {
- check.error(lit, "hexadecimal floating-point literals requires go1.13 or later")
+ check.versionErrorf(lit, "go1.13", "hexadecimal floating-point literals")
}
}
diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go
index 6d697a53ae3aaa14442a21f9f4e15880bbfe75c2..9350c389c162d6f3bbccee60d84014c9e4442b83 100644
--- a/src/cmd/compile/internal/walk/assign.go
+++ b/src/cmd/compile/internal/walk/assign.go
@@ -167,7 +167,7 @@ func walkAssignMapRead(init *ir.Nodes, n *ir.AssignListStmt) ir.Node {
a := n.Lhs[0]
var call *ir.CallExpr
- if w := t.Elem().Width; w <= zeroValSize {
+ if w := t.Elem().Size(); w <= zeroValSize {
fn := mapfn(mapaccess2[fast], t, false)
call = mkcall1(fn, fn.Type().Results(), init, reflectdata.TypePtr(t), r.X, key)
} else {
@@ -429,6 +429,7 @@ func readsMemory(n ir.Node) bool {
ir.OBITNOT,
ir.OCONV,
ir.OCONVIFACE,
+ ir.OCONVIDATA,
ir.OCONVNOP,
ir.ODIV,
ir.ODOT,
@@ -532,7 +533,7 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
fn := typecheck.LookupRuntime("slicecopy")
fn = typecheck.SubstArgTypes(fn, ptr1.Type().Elem(), ptr2.Type().Elem())
- ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Width))
+ ncopy = mkcall1(fn, types.Types[types.TINT], &nodes, ptr1, len1, ptr2, len2, ir.NewInt(elemtype.Size()))
} else {
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
ix := ir.NewIndexExpr(base.Pos, s, ir.NewUnaryExpr(base.Pos, ir.OLEN, l1))
@@ -542,7 +543,7 @@ func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, l2)
nwid := cheapExpr(typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, l2), types.Types[types.TUINTPTR]), &nodes)
- nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Width))
+ nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(elemtype.Size()))
// instantiate func memmove(to *any, frm *any, length uintptr)
fn := typecheck.LookupRuntime("memmove")
@@ -689,7 +690,7 @@ func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
hp := typecheck.ConvNop(typecheck.NodAddr(ix), types.Types[types.TUNSAFEPTR])
// hn := l2 * sizeof(elem(s))
- hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Width)), types.Types[types.TUINTPTR])
+ hn := typecheck.Conv(ir.NewBinaryExpr(base.Pos, ir.OMUL, l2, ir.NewInt(elemtype.Size())), types.Types[types.TUINTPTR])
clrname := "memclrNoHeapPointers"
hasPointers := elemtype.HasPointers()
diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go
index 14efc05e32753727502e42f4e805ebe134522125..d0aaee03d59d07b4bd39ef861f1ea62ccc5fae30 100644
--- a/src/cmd/compile/internal/walk/builtin.go
+++ b/src/cmd/compile/internal/walk/builtin.go
@@ -158,7 +158,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node {
fn := typecheck.LookupRuntime("slicecopy")
fn = typecheck.SubstArgTypes(fn, ptrL.Type().Elem(), ptrR.Type().Elem())
- return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Width))
+ return mkcall1(fn, n.Type(), init, ptrL, lenL, ptrR, lenR, ir.NewInt(n.X.Type().Elem().Size()))
}
n.X = walkExpr(n.X, init)
@@ -194,7 +194,7 @@ func walkCopy(n *ir.BinaryExpr, init *ir.Nodes, runtimecall bool) ir.Node {
nwid := ir.Node(typecheck.Temp(types.Types[types.TUINTPTR]))
setwid := ir.NewAssignStmt(base.Pos, nwid, typecheck.Conv(nlen, types.Types[types.TUINTPTR]))
ne.Body.Append(setwid)
- nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Width))
+ nwid = ir.NewBinaryExpr(base.Pos, ir.OMUL, nwid, ir.NewInt(nl.Type().Elem().Size()))
call := mkcall1(fn, nil, init, nto, nfrm, nwid)
ne.Body.Append(call)
@@ -452,7 +452,7 @@ func walkMakeSliceCopy(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
// We do not check for overflow of len(to)*elem.Width here
// since len(from) is an existing checked slice capacity
// with same elem.Width for the from slice.
- size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Width), types.Types[types.TUINTPTR]))
+ size := ir.NewBinaryExpr(base.Pos, ir.OMUL, typecheck.Conv(length, types.Types[types.TUINTPTR]), typecheck.Conv(ir.NewInt(t.Elem().Size()), types.Types[types.TUINTPTR]))
// instantiate mallocgc(size uintptr, typ *byte, needszero bool) unsafe.Pointer
fn := typecheck.LookupRuntime("mallocgc")
@@ -622,10 +622,7 @@ func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
r := ir.NewCallExpr(base.Pos, ir.OCALL, on, nil)
if params := on.Type().Params().FieldSlice(); len(params) > 0 {
t := params[0].Type
- if !types.Identical(t, n.Type()) {
- n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
- n.SetType(t)
- }
+ n = typecheck.Conv(n, t)
r.Args.Append(n)
}
calls = append(calls, r)
@@ -641,16 +638,9 @@ func walkPrint(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
return walkStmt(typecheck.Stmt(r))
}
-// walkRecover walks an ORECOVER node.
-func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
- // Call gorecover with the FP of this frame.
- // FP is equal to caller's SP plus FixedFrameSize().
- var fp ir.Node = mkcall("getcallersp", types.Types[types.TUINTPTR], init)
- if off := base.Ctxt.FixedFrameSize(); off != 0 {
- fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off))
- }
- fp = ir.NewConvExpr(fp.Pos(), ir.OCONVNOP, types.NewPtr(types.Types[types.TINT32]), fp)
- return mkcall("gorecover", nn.Type(), init, fp)
+// walkRecover walks an ORECOVERFP node.
+func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
+ return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init))
}
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
diff --git a/src/cmd/compile/internal/walk/closure.go b/src/cmd/compile/internal/walk/closure.go
index 2194e1c5b0c7a3fd9f5ed01a57bb1e8aeb613238..4d1c5621fec857fe3a9487bf9da3c422367e08d5 100644
--- a/src/cmd/compile/internal/walk/closure.go
+++ b/src/cmd/compile/internal/walk/closure.go
@@ -37,14 +37,6 @@ func directClosureCall(n *ir.CallExpr) {
return // leave for walkClosure to handle
}
- // If wrapGoDefer() in the order phase has flagged this call,
- // avoid eliminating the closure even if there is a direct call to
- // (the closure is needed to simplify the register ABI). See
- // wrapGoDefer for more details.
- if n.PreserveClosure {
- return
- }
-
// We are going to insert captured variables before input args.
var params []*types.Field
var decls []*ir.Name
@@ -115,13 +107,25 @@ 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)
clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
clos.SetEsc(clo.Esc())
clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...)
+ for i, value := range clos.List {
+ clos.List[i] = ir.NewStructKeyExpr(base.Pos, typ.Field(i), value)
+ }
addr := typecheck.NodAddr(clos)
addr.SetEsc(clo.Esc())
@@ -161,7 +165,7 @@ func closureArgs(clo *ir.ClosureExpr) []ir.Node {
return args
}
-func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
+func walkMethodValue(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
// Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like:
//
@@ -175,18 +179,16 @@ func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
n.X = cheapExpr(n.X, init)
n.X = walkExpr(n.X, nil)
- tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
-
- c := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
- c.SetTypecheck(1)
- init.Append(c)
+ tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X)
+ check := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
+ init.Append(typecheck.Stmt(check))
}
- typ := typecheck.PartialCallType(n)
+ typ := typecheck.MethodValueType(n)
clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ), nil)
clos.SetEsc(n.Esc())
- clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, typecheck.MethodValueWrapper(n).Nname), n.X}
+ clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, methodValueWrapper(n)), n.X}
addr := typecheck.NodAddr(clos)
addr.SetEsc(n.Esc())
@@ -205,3 +207,78 @@ func walkCallPart(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
return walkExpr(cfn, init)
}
+
+// methodValueWrapper returns the ONAME node representing the
+// wrapper function (*-fm) needed for the given method value. If the
+// wrapper function hasn't already been created yet, it's created and
+// added to typecheck.Target.Decls.
+func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
+ if dot.Op() != ir.OMETHVALUE {
+ base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op())
+ }
+
+ t0 := dot.Type()
+ meth := dot.Sel
+ rcvrtype := dot.X.Type()
+ sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
+
+ if sym.Uniq() {
+ return sym.Def.(*ir.Name)
+ }
+ sym.SetUniq(true)
+
+ if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
+ base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
+ }
+
+ savecurfn := ir.CurFunc
+ saveLineNo := base.Pos
+ ir.CurFunc = nil
+
+ // Set line number equal to the line number where the method is declared.
+ if pos := dot.Selection.Pos; pos.IsKnown() {
+ base.Pos = pos
+ }
+ // Note: !dot.Selection.Pos.IsKnown() happens for method expressions where
+ // the method is implicitly declared. The Error method of the
+ // built-in error type is one such method. We leave the line
+ // number at the use of the method expression in this
+ // case. See issue 29389.
+
+ tfn := ir.NewFuncType(base.Pos, nil,
+ typecheck.NewFuncParams(t0.Params(), true),
+ typecheck.NewFuncParams(t0.Results(), false))
+
+ fn := typecheck.DeclFunc(sym, tfn)
+ fn.SetDupok(true)
+ fn.SetWrapper(true)
+
+ // Declare and initialize variable holding receiver.
+ ptr := ir.NewHiddenParam(base.Pos, fn, typecheck.Lookup(".this"), rcvrtype)
+
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ call.IsDDD = tfn.Type().IsVariadic()
+
+ var body ir.Node = call
+ if t0.NumResults() != 0 {
+ ret := ir.NewReturnStmt(base.Pos, nil)
+ ret.Results = []ir.Node{call}
+ body = ret
+ }
+
+ fn.Body = []ir.Node{body}
+ typecheck.FinishFuncBody()
+
+ typecheck.Func(fn)
+ // Need to typecheck the body of the just-generated wrapper.
+ // typecheckslice() requires that Curfn is set when processing an ORETURN.
+ ir.CurFunc = fn
+ typecheck.Stmts(fn.Body)
+ sym.Def = fn.Nname
+ typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
+ ir.CurFunc = savecurfn
+ base.Pos = saveLineNo
+
+ return fn.Nname
+}
diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go
index b18615f61a3e3b4d0ca988193c1114d3b6d1fb86..625e2160503754b6c2beb0452e0ecddf0b2b280d 100644
--- a/src/cmd/compile/internal/walk/compare.go
+++ b/src/cmd/compile/internal/walk/compare.go
@@ -5,7 +5,6 @@
package walk
import (
- "encoding/binary"
"go/constant"
"cmd/compile/internal/base"
@@ -14,7 +13,6 @@ import (
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
- "cmd/internal/sys"
)
// The result of walkCompare MUST be assigned back to n, e.g.
@@ -81,7 +79,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
var inline bool
maxcmpsize := int64(4)
- unalignedLoad := canMergeLoads()
+ unalignedLoad := ssagen.Arch.LinkArch.CanMergeLoads
if unalignedLoad {
// Keep this low enough to generate less code than a function call.
maxcmpsize = 2 * int64(ssagen.Arch.LinkArch.RegSize)
@@ -138,7 +136,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
return n
case types.TARRAY:
// We can compare several elements at once with 2/4/8 byte integer compares
- inline = t.NumElem() <= 1 || (types.IsSimple[t.Elem().Kind()] && (t.NumElem() <= 4 || t.Elem().Width*t.NumElem() <= maxcmpsize))
+ inline = t.NumElem() <= 1 || (types.IsSimple[t.Elem().Kind()] && (t.NumElem() <= 4 || t.Elem().Size()*t.NumElem() <= maxcmpsize))
case types.TSTRUCT:
inline = t.NumComponents(types.IgnoreBlankFields) <= 4
}
@@ -164,7 +162,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
call.Args.Append(typecheck.NodAddr(cmpl))
call.Args.Append(typecheck.NodAddr(cmpr))
if needsize {
- call.Args.Append(ir.NewInt(t.Width))
+ call.Args.Append(ir.NewInt(t.Size()))
}
res := ir.Node(call)
if n.Op() != ir.OEQ {
@@ -202,22 +200,22 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
}
} else {
step := int64(1)
- remains := t.NumElem() * t.Elem().Width
- combine64bit := unalignedLoad && types.RegSize == 8 && t.Elem().Width <= 4 && t.Elem().IsInteger()
- combine32bit := unalignedLoad && t.Elem().Width <= 2 && t.Elem().IsInteger()
- combine16bit := unalignedLoad && t.Elem().Width == 1 && t.Elem().IsInteger()
+ remains := t.NumElem() * t.Elem().Size()
+ combine64bit := unalignedLoad && types.RegSize == 8 && t.Elem().Size() <= 4 && t.Elem().IsInteger()
+ combine32bit := unalignedLoad && t.Elem().Size() <= 2 && t.Elem().IsInteger()
+ combine16bit := unalignedLoad && t.Elem().Size() == 1 && t.Elem().IsInteger()
for i := int64(0); remains > 0; {
var convType *types.Type
switch {
case remains >= 8 && combine64bit:
convType = types.Types[types.TINT64]
- step = 8 / t.Elem().Width
+ step = 8 / t.Elem().Size()
case remains >= 4 && combine32bit:
convType = types.Types[types.TUINT32]
- step = 4 / t.Elem().Width
+ step = 4 / t.Elem().Size()
case remains >= 2 && combine16bit:
convType = types.Types[types.TUINT16]
- step = 2 / t.Elem().Width
+ step = 2 / t.Elem().Size()
default:
step = 1
}
@@ -227,7 +225,7 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i)),
)
i++
- remains -= t.Elem().Width
+ remains -= t.Elem().Size()
} else {
elemType := t.Elem().ToUnsigned()
cmplw := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i)))
@@ -242,17 +240,17 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
lb := ir.Node(ir.NewIndexExpr(base.Pos, cmpl, ir.NewInt(i+offset)))
lb = typecheck.Conv(lb, elemType)
lb = typecheck.Conv(lb, convType)
- lb = ir.NewBinaryExpr(base.Pos, ir.OLSH, lb, ir.NewInt(8*t.Elem().Width*offset))
+ lb = ir.NewBinaryExpr(base.Pos, ir.OLSH, lb, ir.NewInt(8*t.Elem().Size()*offset))
cmplw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmplw, lb)
rb := ir.Node(ir.NewIndexExpr(base.Pos, cmpr, ir.NewInt(i+offset)))
rb = typecheck.Conv(rb, elemType)
rb = typecheck.Conv(rb, convType)
- rb = ir.NewBinaryExpr(base.Pos, ir.OLSH, rb, ir.NewInt(8*t.Elem().Width*offset))
+ rb = ir.NewBinaryExpr(base.Pos, ir.OLSH, rb, ir.NewInt(8*t.Elem().Size()*offset))
cmprw = ir.NewBinaryExpr(base.Pos, ir.OOR, cmprw, rb)
}
compare(cmplw, cmprw)
i += step
- remains -= step * t.Elem().Width
+ remains -= step * t.Elem().Size()
}
}
}
@@ -311,7 +309,7 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
maxRewriteLen := 6
// Some architectures can load unaligned byte sequence as 1 word.
// So we can cover longer strings with the same amount of code.
- canCombineLoads := canMergeLoads()
+ canCombineLoads := ssagen.Arch.LinkArch.CanMergeLoads
combine64bit := false
if canCombineLoads {
// Keep this low enough to generate less code than a function call.
@@ -491,18 +489,3 @@ func tracecmpArg(n ir.Node, t *types.Type, init *ir.Nodes) ir.Node {
return typecheck.Conv(n, t)
}
-
-// canMergeLoads reports whether the backend optimization passes for
-// the current architecture can combine adjacent loads into a single
-// larger, possibly unaligned, load. Note that currently the
-// optimizations must be able to handle little endian byte order.
-func canMergeLoads() bool {
- switch ssagen.Arch.LinkArch.Family {
- case sys.ARM64, sys.AMD64, sys.I386, sys.S390X:
- return true
- case sys.PPC64:
- // Load combining only supported on ppc64le.
- return ssagen.Arch.LinkArch.ByteOrder == binary.LittleEndian
- }
- return false
-}
diff --git a/src/cmd/compile/internal/walk/complit.go b/src/cmd/compile/internal/walk/complit.go
index abd920d64612ff720dbfc27e6b37d6ce32f64e7f..b985b4caeb8597ec7387cb7ca24eb4cde01e5c0d 100644
--- a/src/cmd/compile/internal/walk/complit.go
+++ b/src/cmd/compile/internal/walk/complit.go
@@ -218,11 +218,11 @@ func fixedlit(ctxt initContext, kind initKind, n *ir.CompLitExpr, var_ ir.Node,
case ir.OSTRUCTLIT:
splitnode = func(rn ir.Node) (ir.Node, ir.Node) {
r := rn.(*ir.StructKeyExpr)
- if r.Field.IsBlank() || isBlank {
+ if r.Sym().IsBlank() || isBlank {
return ir.BlankNode, r.Value
}
ir.SetPos(r)
- return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Field), r.Value
+ return ir.NewSelectorExpr(base.Pos, ir.ODOT, var_, r.Sym()), r.Value
}
default:
base.Fatalf("fixedlit bad op: %v", n.Op())
@@ -277,7 +277,7 @@ func isSmallSliceLit(n *ir.CompLitExpr) bool {
return false
}
- return n.Type().Elem().Width == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Width
+ return n.Type().Elem().Size() == 0 || n.Len <= ir.MaxSmallArraySize/n.Type().Elem().Size()
}
func slicelit(ctxt initContext, n *ir.CompLitExpr, var_ ir.Node, init *ir.Nodes) {
@@ -440,8 +440,8 @@ func maplit(n *ir.CompLitExpr, m ir.Node, init *ir.Nodes) {
tk := types.NewArray(n.Type().Key(), int64(len(entries)))
te := types.NewArray(n.Type().Elem(), int64(len(entries)))
- tk.SetNoalg(true)
- te.SetNoalg(true)
+ // TODO(#47904): mark tk and te NoAlg here once the
+ // compiler/linker can handle NoAlg types correctly.
types.CalcSize(tk)
types.CalcSize(te)
@@ -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
@@ -650,7 +650,7 @@ func genAsStatic(as *ir.AssignStmt) {
switch r := as.Y; r.Op() {
case ir.OLITERAL:
- staticdata.InitConst(name, offset, r, int(r.Type().Width))
+ staticdata.InitConst(name, offset, r, int(r.Type().Size()))
return
case ir.OMETHEXPR:
r := r.(*ir.SelectorExpr)
diff --git a/src/cmd/compile/internal/walk/convert.go b/src/cmd/compile/internal/walk/convert.go
index 26e17a126f22151a542fecf9026002846baa82ca..ffc5fd19e801dcfbc2bdd046bc43eb3fda9104c7 100644
--- a/src/cmd/compile/internal/walk/convert.go
+++ b/src/cmd/compile/internal/walk/convert.go
@@ -14,6 +14,7 @@ import (
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/internal/src"
"cmd/internal/sys"
)
@@ -24,9 +25,6 @@ func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
return n.X
}
if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
- if n.Type().IsPtr() && n.X.Type().IsUnsafePtr() { // unsafe.Pointer to *T
- return walkCheckPtrAlignment(n, init, nil)
- }
if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer
return walkCheckPtrArithmetic(n, init)
}
@@ -41,46 +39,98 @@ func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
// walkConvInterface walks an OCONVIFACE node.
func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+
n.X = walkExpr(n.X, init)
fromType := n.X.Type()
toType := n.Type()
-
- if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) { // skip unnamed functions (func _())
+ if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
+ // skip unnamed functions (func _())
reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
}
- // typeword generates the type word of the interface value.
- typeword := func() ir.Node {
+ if !fromType.IsInterface() {
+ var typeWord ir.Node
if toType.IsEmptyInterface() {
- return reflectdata.TypePtr(fromType)
+ typeWord = reflectdata.TypePtr(fromType)
+ } else {
+ typeWord = reflectdata.ITabAddr(fromType, toType)
}
- return reflectdata.ITabAddr(fromType, toType)
- }
-
- // Optimize convT2E or convT2I as a two-word copy when T is pointer-shaped.
- if types.IsDirectIface(fromType) {
- l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), n.X)
+ l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone))
l.SetType(toType)
l.SetTypecheck(n.Typecheck())
return l
}
+ if fromType.IsEmptyInterface() {
+ base.Fatalf("OCONVIFACE can't operate on an empty interface")
+ }
+
+ // Evaluate the input interface.
+ c := typecheck.Temp(fromType)
+ init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
+
+ // Grab its parts.
+ itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c)
+ itab.SetType(types.Types[types.TUINTPTR].PtrTo())
+ itab.SetTypecheck(1)
+ data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c)
+ data.SetType(types.Types[types.TUINT8].PtrTo()) // Type is generic pointer - we're just passing it through.
+ data.SetTypecheck(1)
+
+ var typeWord ir.Node
+ if toType.IsEmptyInterface() {
+ // Implement interface to empty interface conversion.
+ // res = itab
+ // if res != nil {
+ // res = res.type
+ // }
+ typeWord = typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
+ init.Append(ir.NewAssignStmt(base.Pos, typeWord, itab))
+ nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil)
+ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))}
+ init.Append(nif)
+ } else {
+ // Must be converting I2I (more specific to less specific interface).
+ // res = convI2I(toType, itab)
+ fn := typecheck.LookupRuntime("convI2I")
+ types.CalcSize(fn.Type())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args = []ir.Node{reflectdata.TypePtr(toType), itab}
+ typeWord = walkExpr(typecheck.Expr(call), init)
+ }
+
+ // Build the result.
+ // e = iface{typeWord, data}
+ e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeWord, data)
+ e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE.
+ e.SetTypecheck(1)
+ return e
+}
+
+// Returns the data word (the second word) used to represent n in an interface.
+// n must not be of interface type.
+// esc describes whether the result escapes.
+func dataWord(pos src.XPos, n ir.Node, init *ir.Nodes, escapes bool) ir.Node {
+ fromType := n.Type()
+
+ // If it's a pointer, it is its own representation.
+ if types.IsDirectIface(fromType) {
+ return n
+ }
- // Optimize convT2{E,I} for many cases in which T is not pointer-shaped,
- // by using an existing addressable value identical to n.Left
- // or creating one on the stack.
+ // Try a bunch of cases to avoid an allocation.
var value ir.Node
switch {
case fromType.Size() == 0:
- // n.Left is zero-sized. Use zerobase.
- cheapExpr(n.X, init) // Evaluate n.Left for side-effects. See issue 19246.
+ // n is zero-sized. Use zerobase.
+ cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
case fromType.IsBoolean() || (fromType.Size() == 1 && fromType.IsInteger()):
- // n.Left is a bool/byte. Use staticuint64s[n.Left * 8] on little-endian
- // and staticuint64s[n.Left * 8 + 7] on big-endian.
- n.X = cheapExpr(n.X, init)
- // byteindex widens n.Left so that the multiplication doesn't overflow.
- index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n.X), ir.NewInt(3))
+ // n is a bool/byte. Use staticuint64s[n * 8] on little-endian
+ // and staticuint64s[n * 8 + 7] on big-endian.
+ n = cheapExpr(n, init)
+ // byteindex widens n so that the multiplication doesn't overflow.
+ index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(3))
if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(7))
}
@@ -90,118 +140,71 @@ func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
xe.SetBounded(true)
value = xe
- case n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PEXTERN && n.X.(*ir.Name).Readonly():
- // n.Left is a readonly global; use it directly.
- value = n.X
- case !fromType.IsInterface() && n.Esc() == ir.EscNone && fromType.Width <= 1024:
- // n.Left does not escape. Use a stack temporary initialized to n.Left.
+ case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
+ // n is a readonly global; use it directly.
+ value = n
+ case !escapes && fromType.Size() <= 1024:
+ // n does not escape. Use a stack temporary initialized to n.
value = typecheck.Temp(fromType)
- init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n.X)))
+ init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
}
-
if value != nil {
- // Value is identical to n.Left.
- // Construct the interface directly: {type/itab, &value}.
- l := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), typecheck.Expr(typecheck.NodAddr(value)))
- l.SetType(toType)
- l.SetTypecheck(n.Typecheck())
- return l
- }
-
- // Implement interface to empty interface conversion.
- // tmp = i.itab
- // if tmp != nil {
- // tmp = tmp.type
- // }
- // e = iface{tmp, i.data}
- if toType.IsEmptyInterface() && fromType.IsInterface() && !fromType.IsEmptyInterface() {
- // Evaluate the input interface.
- c := typecheck.Temp(fromType)
- init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
-
- // Get the itab out of the interface.
- tmp := typecheck.Temp(types.NewPtr(types.Types[types.TUINT8]))
- init.Append(ir.NewAssignStmt(base.Pos, tmp, typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, c))))
-
- // Get the type out of the itab.
- nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, tmp, typecheck.NodNil())), nil, nil)
- nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, tmp, itabType(tmp))}
- init.Append(nif)
-
- // Build the result.
- e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, tmp, ifaceData(n.Pos(), c, types.NewPtr(types.Types[types.TUINT8])))
- e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE.
- e.SetTypecheck(1)
- return e
+ // The interface data word is &value.
+ return typecheck.Expr(typecheck.NodAddr(value))
}
- fnname, argType, needsaddr := convFuncName(fromType, toType)
-
- if !needsaddr && !fromType.IsInterface() {
- // Use a specialized conversion routine that only returns a data pointer.
- // ptr = convT2X(val)
- // e = iface{typ/tab, ptr}
- fn := typecheck.LookupRuntime(fnname)
- types.CalcSize(fromType)
+ // Time to do an allocation. We'll call into the runtime for that.
+ fnname, argType, needsaddr := dataWordFuncName(fromType)
+ fn := typecheck.LookupRuntime(fnname)
- arg := n.X
+ var args []ir.Node
+ if needsaddr {
+ // Types of large or unknown size are passed by reference.
+ // Orderexpr arranged for n to be a temporary for all
+ // the conversions it could see. Comparison of an interface
+ // with a non-interface, especially in a switch on interface value
+ // with non-interface cases, is not visible to order.stmt, so we
+ // have to fall back on allocating a temp here.
+ if !ir.IsAddressable(n) {
+ n = copyExpr(n, fromType, init)
+ }
+ fn = typecheck.SubstArgTypes(fn, fromType)
+ args = []ir.Node{reflectdata.TypePtr(fromType), typecheck.NodAddr(n)}
+ } else {
+ // Use a specialized conversion routine that takes the type being
+ // converted by value, not by pointer.
+ var arg ir.Node
switch {
case fromType == argType:
// already in the right type, nothing to do
+ arg = n
case fromType.Kind() == argType.Kind(),
fromType.IsPtrShaped() && argType.IsPtrShaped():
// can directly convert (e.g. named type to underlying type, or one pointer to another)
- arg = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, argType, arg)
+ // TODO: never happens because pointers are directIface?
+ arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
case fromType.IsInteger() && argType.IsInteger():
// can directly convert (e.g. int32 to uint32)
- arg = ir.NewConvExpr(n.Pos(), ir.OCONV, argType, arg)
+ arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
default:
// unsafe cast through memory
- arg = copyExpr(arg, arg.Type(), init)
+ arg = copyExpr(n, fromType, init)
var addr ir.Node = typecheck.NodAddr(arg)
- addr = ir.NewConvExpr(n.Pos(), ir.OCONVNOP, argType.PtrTo(), addr)
- arg = ir.NewStarExpr(n.Pos(), addr)
+ addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
+ arg = ir.NewStarExpr(pos, addr)
arg.SetType(argType)
}
-
- call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
- call.Args = []ir.Node{arg}
- e := ir.NewBinaryExpr(base.Pos, ir.OEFACE, typeword(), safeExpr(walkExpr(typecheck.Expr(call), init), init))
- e.SetType(toType)
- e.SetTypecheck(1)
- return e
- }
-
- var tab ir.Node
- if fromType.IsInterface() {
- // convI2I
- tab = reflectdata.TypePtr(toType)
- } else {
- // convT2x
- tab = typeword()
- }
-
- v := n.X
- if needsaddr {
- // Types of large or unknown size are passed by reference.
- // Orderexpr arranged for n.Left to be a temporary for all
- // the conversions it could see. Comparison of an interface
- // with a non-interface, especially in a switch on interface value
- // with non-interface cases, is not visible to order.stmt, so we
- // have to fall back on allocating a temp here.
- if !ir.IsAddressable(v) {
- v = copyExpr(v, v.Type(), init)
- }
- v = typecheck.NodAddr(v)
+ args = []ir.Node{arg}
}
-
- types.CalcSize(fromType)
- fn := typecheck.LookupRuntime(fnname)
- fn = typecheck.SubstArgTypes(fn, fromType, toType)
- types.CalcSize(fn.Type())
call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
- call.Args = []ir.Node{tab, v}
- return walkExpr(typecheck.Expr(call), init)
+ call.Args = args
+ return safeExpr(walkExpr(typecheck.Expr(call), init), init)
+}
+
+// walkConvIData walks an OCONVIDATA node.
+func walkConvIData(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ return dataWord(n.Pos(), n.X, init, n.Esc() != ir.EscNone)
}
// walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
@@ -312,50 +315,35 @@ func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
}
-// convFuncName builds the runtime function name for interface conversion.
-// It also returns the argument type that the runtime function takes, and
-// whether the function expects the data by address.
-// Not all names are possible. For example, we never generate convE2E or convE2I.
-func convFuncName(from, to *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
- tkind := to.Tie()
- switch from.Tie() {
- case 'I':
- if tkind == 'I' {
- return "convI2I", types.Types[types.TINTER], false
- }
- case 'T':
+// dataWordFuncName returns the name of the function used to convert a value of type "from"
+// to the data word of an interface.
+// argType is the type the argument needs to be coerced to.
+// needsaddr reports whether the value should be passed (needaddr==false) or its address (needsaddr==true).
+func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
+ if from.IsInterface() {
+ base.Fatalf("can only handle non-interfaces")
+ }
+ switch {
+ case from.Size() == 2 && uint8(from.Alignment()) == 2:
+ return "convT16", types.Types[types.TUINT16], false
+ case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
+ return "convT32", types.Types[types.TUINT32], false
+ case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
+ return "convT64", types.Types[types.TUINT64], false
+ }
+ if sc := from.SoleComponent(); sc != nil {
switch {
- case from.Size() == 2 && from.Align == 2:
- return "convT16", types.Types[types.TUINT16], false
- case from.Size() == 4 && from.Align == 4 && !from.HasPointers():
- return "convT32", types.Types[types.TUINT32], false
- case from.Size() == 8 && from.Align == types.Types[types.TUINT64].Align && !from.HasPointers():
- return "convT64", types.Types[types.TUINT64], false
- }
- if sc := from.SoleComponent(); sc != nil {
- switch {
- case sc.IsString():
- return "convTstring", types.Types[types.TSTRING], false
- case sc.IsSlice():
- return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter
- }
+ case sc.IsString():
+ return "convTstring", types.Types[types.TSTRING], false
+ case sc.IsSlice():
+ return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter
}
+ }
- switch tkind {
- case 'E':
- if !from.HasPointers() {
- return "convT2Enoptr", types.Types[types.TUNSAFEPTR], true
- }
- return "convT2E", types.Types[types.TUNSAFEPTR], true
- case 'I':
- if !from.HasPointers() {
- return "convT2Inoptr", types.Types[types.TUNSAFEPTR], true
- }
- return "convT2I", types.Types[types.TUNSAFEPTR], true
- }
+ if from.HasPointers() {
+ return "convT", types.Types[types.TUNSAFEPTR], true
}
- base.Fatalf("unknown conv func %c2%c", from.Tie(), to.Tie())
- panic("unreachable")
+ return "convTnoptr", types.Types[types.TUNSAFEPTR], true
}
// rtconvfn returns the parameter and result types that will be used by a
@@ -379,7 +367,7 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
if dst.IsFloat() {
switch src.Kind() {
case types.TINT64, types.TUINT64:
- return src.Kind(), types.TFLOAT64
+ return src.Kind(), dst.Kind()
}
}
@@ -395,7 +383,7 @@ func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
if dst.IsFloat() {
switch src.Kind() {
case types.TINT64, types.TUINT64:
- return src.Kind(), types.TFLOAT64
+ return src.Kind(), dst.Kind()
case types.TUINT32, types.TUINT, types.TUINTPTR:
return types.TUINT32, types.TFLOAT64
}
@@ -423,32 +411,6 @@ func byteindex(n ir.Node) ir.Node {
return n
}
-func walkCheckPtrAlignment(n *ir.ConvExpr, init *ir.Nodes, count ir.Node) ir.Node {
- if !n.Type().IsPtr() {
- base.Fatalf("expected pointer type: %v", n.Type())
- }
- elem := n.Type().Elem()
- if count != nil {
- if !elem.IsArray() {
- base.Fatalf("expected array type: %v", elem)
- }
- elem = elem.Elem()
- }
-
- size := elem.Size()
- if elem.Alignment() == 1 && (size == 0 || size == 1 && count == nil) {
- return n
- }
-
- if count == nil {
- count = ir.NewInt(1)
- }
-
- n.X = cheapExpr(n.X, init)
- init.Append(mkcall("checkptrAlignment", nil, init, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]), reflectdata.TypePtr(elem), typecheck.Conv(count, types.Types[types.TUINTPTR])))
- return n
-}
-
func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
// Calling cheapExpr(n, init) below leads to a recursive call to
// walkExpr, which leads us back here again. Use n.Checkptr to
@@ -462,7 +424,9 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
// TODO(mdempsky): Make stricter. We only need to exempt
// reflect.Value.Pointer and reflect.Value.UnsafeAddr.
switch n.X.Op() {
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
+ case ir.OCALLMETH:
+ base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+ case ir.OCALLFUNC, ir.OCALLINTER:
return n
}
@@ -499,7 +463,7 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
cheap := cheapExpr(n, init)
- slice := typecheck.MakeDotArgs(types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
+ slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
slice.SetEsc(ir.EscNone)
init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index 2fb907710bbbda09f6cdae70ba6a2fd734d84175..e5bf6cf0b5e3670a8df0c827f1385ff40251f54d 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -82,7 +82,7 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
base.Fatalf("walkExpr: switch 1 unknown op %+v", n.Op())
panic("unreachable")
- case ir.ONONAME, ir.OGETG:
+ case ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP:
return n
case ir.OTYPE, ir.ONAME, ir.OLITERAL, ir.ONIL, ir.OLINKSYMOFFSET:
@@ -136,6 +136,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.TypeAssertExpr)
return walkDotType(n, init)
+ case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
+ n := n.(*ir.DynamicTypeAssertExpr)
+ return walkDynamicDotType(n, init)
+
case ir.OLEN, ir.OCAP:
n := n.(*ir.UnaryExpr)
return walkLenCap(n, init)
@@ -161,13 +165,13 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.UnaryExpr)
return mkcall("gopanic", nil, init, n.X)
- case ir.ORECOVER:
- return walkRecover(n.(*ir.CallExpr), init)
+ case ir.ORECOVERFP:
+ return walkRecoverFP(n.(*ir.CallExpr), init)
case ir.OCFUNC:
return n
- case ir.OCALLINTER, ir.OCALLFUNC, ir.OCALLMETH:
+ case ir.OCALLINTER, ir.OCALLFUNC:
n := n.(*ir.CallExpr)
return walkCall(n, init)
@@ -206,6 +210,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.ConvExpr)
return walkConvInterface(n, init)
+ case ir.OCONVIDATA:
+ n := n.(*ir.ConvExpr)
+ return walkConvIData(n, init)
+
case ir.OCONV, ir.OCONVNOP:
n := n.(*ir.ConvExpr)
return walkConv(n, init)
@@ -308,8 +316,8 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
case ir.OCLOSURE:
return walkClosure(n.(*ir.ClosureExpr), init)
- case ir.OCALLPART:
- return walkCallPart(n.(*ir.SelectorExpr), init)
+ case ir.OMETHVALUE:
+ return walkMethodValue(n.(*ir.SelectorExpr), init)
}
// No return! Each case must return (or panic),
@@ -487,9 +495,12 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
return r1
}
-// walkCall walks an OCALLFUNC, OCALLINTER, or OCALLMETH node.
+// walkCall walks an OCALLFUNC or OCALLINTER node.
func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
- if n.Op() == ir.OCALLINTER || n.Op() == ir.OCALLMETH {
+ if n.Op() == ir.OCALLMETH {
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+ }
+ if n.Op() == ir.OCALLINTER || n.X.Op() == ir.OMETHEXPR {
// We expect both interface call reflect.Type.Method and concrete
// call reflect.(*rtype).Method.
usemethod(n)
@@ -549,20 +560,8 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
}
n.SetWalked(true)
- // If this is a method call t.M(...),
- // rewrite into a function call T.M(t, ...).
- // TODO(mdempsky): Do this right after type checking.
if n.Op() == ir.OCALLMETH {
- withRecv := make([]ir.Node, len(n.Args)+1)
- dot := n.X.(*ir.SelectorExpr)
- withRecv[0] = dot.X
- copy(withRecv[1:], n.Args)
- n.Args = withRecv
-
- dot = ir.NewSelectorExpr(dot.Pos(), ir.OXDOT, ir.TypeNode(dot.X.Type()), dot.Selection.Sym)
-
- n.SetOp(ir.OCALLFUNC)
- n.X = typecheck.Expr(dot)
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
}
args := n.Args
@@ -671,6 +670,13 @@ func walkDotType(n *ir.TypeAssertExpr, init *ir.Nodes) ir.Node {
return n
}
+// walkDynamicdotType walks an ODYNAMICDOTTYPE or ODYNAMICDOTTYPE2 node.
+func walkDynamicDotType(n *ir.DynamicTypeAssertExpr, init *ir.Nodes) ir.Node {
+ n.X = walkExpr(n.X, init)
+ n.T = walkExpr(n.T, init)
+ return n
+}
+
// walkIndex walks an OINDEX node.
func walkIndex(n *ir.IndexExpr, init *ir.Nodes) ir.Node {
n.X = walkExpr(n.X, init)
@@ -761,7 +767,7 @@ func walkIndexMap(n *ir.IndexExpr, init *ir.Nodes) ir.Node {
// m[k] is not the target of an assignment.
fast := mapfast(t)
key = mapKeyArg(fast, n, key)
- if w := t.Elem().Width; w <= zeroValSize {
+ if w := t.Elem().Size(); w <= zeroValSize {
call = mkcall1(mapfn(mapaccess1[fast], t, false), types.NewPtr(t.Elem()), init, reflectdata.TypePtr(t), map_, key)
} else {
z := reflectdata.ZeroAddr(w)
@@ -801,15 +807,7 @@ func walkSend(n *ir.SendStmt, init *ir.Nodes) ir.Node {
// walkSlice walks an OSLICE, OSLICEARR, OSLICESTR, OSLICE3, or OSLICE3ARR node.
func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node {
-
- checkSlice := ir.ShouldCheckPtr(ir.CurFunc, 1) && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr()
- if checkSlice {
- conv := n.X.(*ir.ConvExpr)
- conv.X = walkExpr(conv.X, init)
- } else {
- n.X = walkExpr(n.X, init)
- }
-
+ n.X = walkExpr(n.X, init)
n.Low = walkExpr(n.Low, init)
if n.Low != nil && ir.IsZero(n.Low) {
// Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
@@ -817,9 +815,6 @@ func walkSlice(n *ir.SliceExpr, init *ir.Nodes) ir.Node {
}
n.High = walkExpr(n.High, init)
n.Max = walkExpr(n.Max, init)
- if checkSlice {
- n.X = walkCheckPtrAlignment(n.X.(*ir.ConvExpr), init, n.Max)
- }
if n.Op().IsSlice3() {
if n.Max != nil && n.Max.Op() == ir.OCAP && ir.SameSafeExpr(n.X, n.Max.(*ir.UnaryExpr).X) {
@@ -867,7 +862,7 @@ func bounded(n ir.Node, max int64) bool {
}
sign := n.Type().IsSigned()
- bits := int32(8 * n.Type().Width)
+ bits := int32(8 * n.Type().Size())
if ir.IsSmallIntConst(n) {
v := ir.Int64Val(n)
@@ -931,56 +926,55 @@ func bounded(n ir.Node, max int64) bool {
return false
}
-// usemethod checks interface method calls for uses of reflect.Type.Method.
+// usemethod checks calls for uses of reflect.Type.{Method,MethodByName}.
func usemethod(n *ir.CallExpr) {
- t := n.X.Type()
+ // Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package.
+ // Those functions may be alive via the itab, which should not cause all methods
+ // alive. We only want to mark their callers.
+ if base.Ctxt.Pkgpath == "reflect" {
+ switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names?
+ case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName":
+ return
+ }
+ }
- // Looking for either of:
- // Method(int) reflect.Method
- // MethodByName(string) (reflect.Method, bool)
- //
- // TODO(crawshaw): improve precision of match by working out
- // how to check the method name.
- if n := t.NumParams(); n != 1 {
+ dot, ok := n.X.(*ir.SelectorExpr)
+ if !ok {
return
}
- if n := t.NumResults(); n != 1 && n != 2 {
+
+ // Looking for either direct method calls and interface method calls of:
+ // reflect.Type.Method - func(int) reflect.Method
+ // reflect.Type.MethodByName - func(string) (reflect.Method, bool)
+ var pKind types.Kind
+
+ switch dot.Sel.Name {
+ case "Method":
+ pKind = types.TINT
+ case "MethodByName":
+ pKind = types.TSTRING
+ default:
return
}
- p0 := t.Params().Field(0)
- res0 := t.Results().Field(0)
- var res1 *types.Field
- if t.NumResults() == 2 {
- res1 = t.Results().Field(1)
- }
- if res1 == nil {
- if p0.Type.Kind() != types.TINT {
- return
- }
- } else {
- if !p0.Type.IsString() {
- return
- }
- if !res1.Type.IsBoolean() {
- return
- }
+ t := dot.Selection.Type
+ if t.NumParams() != 1 || t.Params().Field(0).Type.Kind() != pKind {
+ return
}
-
- // Don't mark reflect.(*rtype).Method, etc. themselves in the reflect package.
- // Those functions may be alive via the itab, which should not cause all methods
- // alive. We only want to mark their callers.
- if base.Ctxt.Pkgpath == "reflect" {
- switch ir.CurFunc.Nname.Sym().Name { // TODO: is there a better way than hardcoding the names?
- case "(*rtype).Method", "(*rtype).MethodByName", "(*interfaceType).Method", "(*interfaceType).MethodByName":
+ switch t.NumResults() {
+ case 1:
+ // ok
+ case 2:
+ if t.Results().Field(1).Type.Kind() != types.TBOOL {
return
}
+ default:
+ return
}
- // Note: Don't rely on res0.Type.String() since its formatting depends on multiple factors
- // (including global variables such as numImports - was issue #19028).
- // Also need to check for reflect package itself (see Issue #38515).
- if s := res0.Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) {
+ // Check that first result type is "reflect.Method". Note that we have to check sym name and sym package
+ // separately, as we can't check for exact string "reflect.Method" reliably (e.g., see #19028 and #38515).
+ if s := t.Results().Field(0).Type.Sym(); s != nil && s.Name == "Method" && types.IsReflectPkg(s.Pkg) {
ir.CurFunc.SetReflectMethod(true)
// The LSym is initialized at this point. We need to set the attribute on the LSym.
ir.CurFunc.LSym.Set(obj.AttrReflectMethod, true)
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
index b733d3a29f67cc18a924462a0a4c8983a8e8bf7b..861c122456d7648f2fc319beecb77f23004e2ae1 100644
--- a/src/cmd/compile/internal/walk/order.go
+++ b/src/cmd/compile/internal/walk/order.go
@@ -7,15 +7,14 @@ package walk
import (
"fmt"
"go/constant"
- "internal/buildcfg"
"cmd/compile/internal/base"
- "cmd/compile/internal/escape"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/staticinit"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/internal/objabi"
"cmd/internal/src"
)
@@ -53,7 +52,7 @@ import (
type orderState struct {
out []ir.Node // list of generated statements
temp []*ir.Name // stack of temporary variables
- free map[string][]*ir.Name // free list of unused temporaries, by type.LongString().
+ free map[string][]*ir.Name // free list of unused temporaries, by type.LinkString().
edit func(ir.Node) ir.Node // cached closure of o.exprNoLHS
}
@@ -78,20 +77,14 @@ func (o *orderState) append(stmt ir.Node) {
// If clear is true, newTemp emits code to zero the temporary.
func (o *orderState) newTemp(t *types.Type, clear bool) *ir.Name {
var v *ir.Name
- // Note: LongString is close to the type equality we want,
- // but not exactly. We still need to double-check with types.Identical.
- key := t.LongString()
- a := o.free[key]
- for i, n := range a {
- if types.Identical(t, n.Type()) {
- v = a[i]
- a[i] = a[len(a)-1]
- a = a[:len(a)-1]
- o.free[key] = a
- break
+ key := t.LinkString()
+ if a := o.free[key]; len(a) > 0 {
+ v = a[len(a)-1]
+ if !types.Identical(t, v.Type()) {
+ base.Fatalf("expected %L to have type %v", v, t)
}
- }
- if v == nil {
+ o.free[key] = a[:len(a)-1]
+ } else {
v = typecheck.Temp(t)
}
if clear {
@@ -305,7 +298,7 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
// Unsafe cast through memory.
// We'll need to do a load with type kt. Create a temporary of type kt to
// ensure sufficient alignment. nt may be under-aligned.
- if kt.Align < nt.Align {
+ if uint8(kt.Alignment()) < uint8(nt.Alignment()) {
base.Fatalf("mapKeyTemp: key type is not sufficiently aligned, kt=%v nt=%v", kt, nt)
}
tmp := o.newTemp(kt, true)
@@ -372,7 +365,7 @@ func (o *orderState) markTemp() ordermarker {
// which must have been returned by markTemp.
func (o *orderState) popTemp(mark ordermarker) {
for _, n := range o.temp[mark:] {
- key := n.Type().LongString()
+ key := n.Type().LinkString()
o.free[key] = append(o.free[key], n)
}
o.temp = o.temp[:mark]
@@ -451,6 +444,13 @@ func (o *orderState) edge() {
// __libfuzzer_extra_counters.
counter := staticinit.StaticName(types.Types[types.TUINT8])
counter.SetLibfuzzerExtraCounter(true)
+ // As well as setting SetLibfuzzerExtraCounter, we preemptively set the
+ // symbol type to SLIBFUZZER_EXTRA_COUNTER so that the race detector
+ // instrumentation pass (which does not have access to the flags set by
+ // SetLibfuzzerExtraCounter) knows to ignore them. This information is
+ // lost by the time it reaches the compile step, so SetLibfuzzerExtraCounter
+ // is still necessary.
+ counter.Linksym().Type = objabi.SLIBFUZZER_EXTRA_COUNTER
// counter += 1
incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
@@ -514,15 +514,18 @@ func (o *orderState) init(n ir.Node) {
}
// call orders the call expression n.
-// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+// n.Op is OCALLFUNC/OCALLINTER or a builtin like OCOPY.
func (o *orderState) call(nn ir.Node) {
if len(nn.Init()) > 0 {
// Caller should have already called o.init(nn).
base.Fatalf("%v with unexpected ninit", nn.Op())
}
+ if nn.Op() == ir.OCALLMETH {
+ base.FatalfAt(nn.Pos(), "OCALLMETH missed by typecheck")
+ }
// Builtin functions.
- if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLMETH && nn.Op() != ir.OCALLINTER {
+ if nn.Op() != ir.OCALLFUNC && nn.Op() != ir.OCALLINTER {
switch n := nn.(type) {
default:
base.Fatalf("unexpected call: %+v", n)
@@ -554,39 +557,6 @@ func (o *orderState) call(nn ir.Node) {
n.X = o.expr(n.X, nil)
o.exprList(n.Args)
-
- if n.Op() == ir.OCALLINTER {
- return
- }
- keepAlive := func(arg ir.Node) {
- // If the argument is really a pointer being converted to uintptr,
- // arrange for the pointer to be kept alive until the call returns,
- // by copying it into a temp and marking that temp
- // still alive when we pop the temp stack.
- if arg.Op() == ir.OCONVNOP {
- arg := arg.(*ir.ConvExpr)
- if arg.X.Type().IsUnsafePtr() {
- x := o.copyExpr(arg.X)
- arg.X = x
- x.SetAddrtaken(true) // ensure SSA keeps the x variable
- n.KeepAlive = append(n.KeepAlive, x)
- }
- }
- }
-
- // Check for "unsafe-uintptr" tag provided by escape analysis.
- for i, param := range n.X.Type().Params().FieldSlice() {
- if param.Note == escape.UnsafeUintptrNote || param.Note == escape.UintptrEscapesNote {
- if arg := n.Args[i]; arg.Op() == ir.OSLICELIT {
- arg := arg.(*ir.CompLitExpr)
- for _, elt := range arg.List {
- keepAlive(elt)
- }
- } else {
- keepAlive(arg)
- }
- }
- }
}
// mapAssign appends n to o.out.
@@ -693,9 +663,20 @@ func (o *orderState) stmt(n ir.Node) {
n := n.(*ir.AssignListStmt)
t := o.markTemp()
o.exprList(n.Lhs)
- o.init(n.Rhs[0])
- o.call(n.Rhs[0])
- o.as2func(n)
+ call := n.Rhs[0]
+ o.init(call)
+ if ic, ok := call.(*ir.InlinedCallExpr); ok {
+ o.stmtList(ic.Body)
+
+ n.SetOp(ir.OAS2)
+ n.Rhs = ic.ReturnVars
+
+ o.exprList(n.Rhs)
+ o.out = append(o.out, n)
+ } else {
+ o.call(call)
+ o.as2func(n)
+ }
o.cleanTemp(t)
// Special: use temporary variables to hold result,
@@ -713,6 +694,10 @@ func (o *orderState) stmt(n ir.Node) {
case ir.ODOTTYPE2:
r := r.(*ir.TypeAssertExpr)
r.X = o.expr(r.X, nil)
+ case ir.ODYNAMICDOTTYPE2:
+ r := r.(*ir.DynamicTypeAssertExpr)
+ r.X = o.expr(r.X, nil)
+ r.T = o.expr(r.T, nil)
case ir.ORECV:
r := r.(*ir.UnaryExpr)
r.X = o.expr(r.X, nil)
@@ -748,14 +733,25 @@ func (o *orderState) stmt(n ir.Node) {
o.out = append(o.out, n)
// Special: handle call arguments.
- case ir.OCALLFUNC, ir.OCALLINTER, ir.OCALLMETH:
+ case ir.OCALLFUNC, ir.OCALLINTER:
n := n.(*ir.CallExpr)
t := o.markTemp()
o.call(n)
o.out = append(o.out, n)
o.cleanTemp(t)
- case ir.OCLOSE, ir.ORECV:
+ case ir.OINLCALL:
+ n := n.(*ir.InlinedCallExpr)
+ o.stmtList(n.Body)
+
+ // discard results; double-check for no side effects
+ for _, result := range n.ReturnVars {
+ if staticinit.AnySideEffects(result) {
+ base.FatalfAt(result.Pos(), "inlined call result has side effects: %v", result)
+ }
+ }
+
+ case ir.OCHECKNIL, ir.OCLOSE, ir.OPANIC, ir.ORECV:
n := n.(*ir.UnaryExpr)
t := o.markTemp()
n.X = o.expr(n.X, nil)
@@ -770,10 +766,10 @@ func (o *orderState) stmt(n ir.Node) {
o.out = append(o.out, n)
o.cleanTemp(t)
- case ir.OPRINT, ir.OPRINTN, ir.ORECOVER:
+ case ir.OPRINT, ir.OPRINTN, ir.ORECOVERFP:
n := n.(*ir.CallExpr)
t := o.markTemp()
- o.exprList(n.Args)
+ o.call(n)
o.out = append(o.out, n)
o.cleanTemp(t)
@@ -783,16 +779,6 @@ func (o *orderState) stmt(n ir.Node) {
t := o.markTemp()
o.init(n.Call)
o.call(n.Call)
- if n.Call.Op() == ir.ORECOVER {
- // Special handling of "defer recover()". We need to evaluate the FP
- // argument before wrapping.
- var init ir.Nodes
- n.Call = walkRecover(n.Call.(*ir.CallExpr), &init)
- o.stmtList(init)
- }
- if buildcfg.Experiment.RegabiDefer {
- o.wrapGoDefer(n)
- }
o.out = append(o.out, n)
o.cleanTemp(t)
@@ -830,16 +816,6 @@ func (o *orderState) stmt(n ir.Node) {
orderBlock(&n.Else, o.free)
o.out = append(o.out, n)
- case ir.OPANIC:
- n := n.(*ir.UnaryExpr)
- t := o.markTemp()
- n.X = o.expr(n.X, nil)
- if !n.X.Type().IsEmptyInterface() {
- base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
- }
- o.out = append(o.out, n)
- o.cleanTemp(t)
-
case ir.ORANGE:
// n.Right is the expression being ranged over.
// order it, and then make a copy if we need one.
@@ -973,6 +949,12 @@ func (o *orderState) stmt(n ir.Node) {
if colas {
if len(init) > 0 && init[0].Op() == ir.ODCL && init[0].(*ir.Decl).X == n {
init = init[1:]
+
+ // iimport may have added a default initialization assignment,
+ // due to how it handles ODCL statements.
+ if len(init) > 0 && init[0].Op() == ir.OAS && init[0].(*ir.AssignStmt).X == n {
+ init = init[1:]
+ }
}
dcl := typecheck.Stmt(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
ncas.PtrInit().Append(dcl)
@@ -1192,23 +1174,26 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
// concrete type (not interface) argument might need an addressable
// temporary to pass to the runtime conversion routine.
- case ir.OCONVIFACE:
+ case ir.OCONVIFACE, ir.OCONVIDATA:
n := n.(*ir.ConvExpr)
n.X = o.expr(n.X, nil)
if n.X.Type().IsInterface() {
return n
}
- if _, _, needsaddr := convFuncName(n.X.Type(), n.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
+ if _, _, needsaddr := dataWordFuncName(n.X.Type()); needsaddr || isStaticCompositeLiteral(n.X) {
// Need a temp if we need to pass the address to the conversion function.
// We also process static composite literal node here, making a named static global
- // whose address we can put directly in an interface (see OCONVIFACE case in walk).
+ // whose address we can put directly in an interface (see OCONVIFACE/OCONVIDATA case in walk).
n.X = o.addrTemp(n.X)
}
return n
case ir.OCONVNOP:
n := n.(*ir.ConvExpr)
- if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER || n.X.Op() == ir.OCALLMETH) {
+ if n.X.Op() == ir.OCALLMETH {
+ base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
+ }
+ if n.Type().IsKind(types.TUNSAFEPTR) && n.X.Type().IsKind(types.TUINTPTR) && (n.X.Op() == ir.OCALLFUNC || n.X.Op() == ir.OCALLINTER) {
call := n.X.(*ir.CallExpr)
// When reordering unsafe.Pointer(f()) into a separate
// statement, the conversion and function call must stay
@@ -1261,9 +1246,12 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
o.out = append(o.out, nif)
return r
+ case ir.OCALLMETH:
+ base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
+ panic("unreachable")
+
case ir.OCALLFUNC,
ir.OCALLINTER,
- ir.OCALLMETH,
ir.OCAP,
ir.OCOMPLEX,
ir.OCOPY,
@@ -1275,7 +1263,7 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
ir.OMAKESLICECOPY,
ir.ONEW,
ir.OREAL,
- ir.ORECOVER,
+ ir.ORECOVERFP,
ir.OSTR2BYTES,
ir.OSTR2BYTESTMP,
ir.OSTR2RUNES:
@@ -1293,6 +1281,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
}
return n
+ case ir.OINLCALL:
+ n := n.(*ir.InlinedCallExpr)
+ o.stmtList(n.Body)
+ return n.SingleResult()
+
case ir.OAPPEND:
// Check for append(x, make([]T, y)...) .
n := n.(*ir.CallExpr)
@@ -1327,11 +1320,11 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
}
return n
- case ir.OCALLPART:
+ case ir.OMETHVALUE:
n := n.(*ir.SelectorExpr)
n.X = o.expr(n.X, nil)
if n.Transient() {
- t := typecheck.PartialCallType(n)
+ t := typecheck.MethodValueType(n)
n.Prealloc = o.newTemp(t, false)
}
return n
@@ -1498,313 +1491,6 @@ func (o *orderState) as2ok(n *ir.AssignListStmt) {
o.stmt(typecheck.Stmt(as))
}
-var wrapGoDefer_prgen int
-
-// wrapGoDefer wraps the target of a "go" or "defer" statement with a
-// new "function with no arguments" closure. Specifically, it converts
-//
-// defer f(x, y)
-//
-// to
-//
-// x1, y1 := x, y
-// defer func() { f(x1, y1) }()
-//
-// This is primarily to enable a quicker bringup of defers under the
-// new register ABI; by doing this conversion, we can simplify the
-// code in the runtime that invokes defers on the panic path.
-func (o *orderState) wrapGoDefer(n *ir.GoDeferStmt) {
- call := n.Call
-
- var callX ir.Node // thing being called
- var callArgs []ir.Node // call arguments
- var keepAlive []*ir.Name // KeepAlive list from call, if present
-
- // A helper to recreate the call within the closure.
- var mkNewCall func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node
-
- // Defer calls come in many shapes and sizes; not all of them
- // are ir.CallExpr's. Examine the type to see what we're dealing with.
- switch x := call.(type) {
- case *ir.CallExpr:
- callX = x.X
- callArgs = x.Args
- keepAlive = x.KeepAlive
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- newcall := ir.NewCallExpr(pos, op, fun, args)
- newcall.IsDDD = x.IsDDD
- return ir.Node(newcall)
- }
- case *ir.UnaryExpr: // ex: OCLOSE
- callArgs = []ir.Node{x.X}
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- if len(args) != 1 {
- panic("internal error, expecting single arg")
- }
- return ir.Node(ir.NewUnaryExpr(pos, op, args[0]))
- }
- case *ir.BinaryExpr: // ex: OCOPY
- callArgs = []ir.Node{x.X, x.Y}
- mkNewCall = func(pos src.XPos, op ir.Op, fun ir.Node, args []ir.Node) ir.Node {
- if len(args) != 2 {
- panic("internal error, expecting two args")
- }
- return ir.Node(ir.NewBinaryExpr(pos, op, args[0], args[1]))
- }
- default:
- panic("unhandled op")
- }
-
- // No need to wrap if called func has no args, no receiver, and no results.
- // However in the case of "defer func() { ... }()" we need to
- // protect against the possibility of directClosureCall rewriting
- // things so that the call does have arguments.
- //
- // Do wrap method calls (OCALLMETH, OCALLINTER), because it has
- // a receiver.
- //
- // Also do wrap builtin functions, because they may be expanded to
- // calls with arguments (e.g. ORECOVER).
- //
- // TODO: maybe not wrap if the called function has no arguments and
- // only in-register results?
- if len(callArgs) == 0 && call.Op() == ir.OCALLFUNC && callX.Type().NumResults() == 0 {
- if c, ok := call.(*ir.CallExpr); ok && callX != nil && callX.Op() == ir.OCLOSURE {
- cloFunc := callX.(*ir.ClosureExpr).Func
- cloFunc.SetClosureCalled(false)
- c.PreserveClosure = true
- }
- return
- }
-
- if c, ok := call.(*ir.CallExpr); ok {
- // To simplify things, turn f(a, b, []T{c, d, e}...) back
- // into f(a, b, c, d, e) -- when the final call is run through the
- // type checker below, it will rebuild the proper slice literal.
- undoVariadic(c)
- callX = c.X
- callArgs = c.Args
- }
-
- // This is set to true if the closure we're generating escapes
- // (needs heap allocation).
- cloEscapes := func() bool {
- if n.Op() == ir.OGO {
- // For "go", assume that all closures escape.
- return true
- }
- // For defer, just use whatever result escape analysis
- // has determined for the defer.
- return n.Esc() != ir.EscNever
- }()
-
- // A helper for making a copy of an argument. Note that it is
- // not safe to use o.copyExpr(arg) if we're putting a
- // reference to the temp into the closure (as opposed to
- // copying it in by value), since in the by-reference case we
- // need a temporary whose lifetime extends to the end of the
- // function (as opposed to being local to the current block or
- // statement being ordered).
- mkArgCopy := func(arg ir.Node) *ir.Name {
- t := arg.Type()
- byval := t.Size() <= 128 || cloEscapes
- var argCopy *ir.Name
- if byval {
- argCopy = o.copyExpr(arg)
- } else {
- argCopy = typecheck.Temp(t)
- o.append(ir.NewAssignStmt(base.Pos, argCopy, arg))
- }
- // The value of 128 below is meant to be consistent with code
- // in escape analysis that picks byval/byaddr based on size.
- argCopy.SetByval(byval)
- return argCopy
- }
-
- // getUnsafeArg looks for an unsafe.Pointer arg that has been
- // previously captured into the call's keepalive list, returning
- // the name node for it if found.
- getUnsafeArg := func(arg ir.Node) *ir.Name {
- // Look for uintptr(unsafe.Pointer(name))
- if arg.Op() != ir.OCONVNOP {
- return nil
- }
- if !arg.Type().IsUintptr() {
- return nil
- }
- if !arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
- return nil
- }
- arg = arg.(*ir.ConvExpr).X
- argname, ok := arg.(*ir.Name)
- if !ok {
- return nil
- }
- for i := range keepAlive {
- if argname == keepAlive[i] {
- return argname
- }
- }
- return nil
- }
-
- // Copy the arguments to the function into temps.
- //
- // For calls with uintptr(unsafe.Pointer(...)) args that are being
- // kept alive (see code in (*orderState).call that does this), use
- // the existing arg copy instead of creating a new copy.
- unsafeArgs := make([]*ir.Name, len(callArgs))
- origArgs := callArgs
- var newNames []*ir.Name
- for i := range callArgs {
- arg := callArgs[i]
- var argname *ir.Name
- unsafeArgName := getUnsafeArg(arg)
- if unsafeArgName != nil {
- // arg has been copied already, use keepalive copy
- argname = unsafeArgName
- unsafeArgs[i] = unsafeArgName
- } else {
- argname = mkArgCopy(arg)
- }
- newNames = append(newNames, argname)
- }
-
- // Deal with cases where the function expression (what we're
- // calling) is not a simple function symbol.
- var fnExpr *ir.Name
- var methSelectorExpr *ir.SelectorExpr
- if callX != nil {
- switch {
- case callX.Op() == ir.ODOTMETH || callX.Op() == ir.ODOTINTER:
- // Handle defer of a method call, e.g. "defer v.MyMethod(x, y)"
- n := callX.(*ir.SelectorExpr)
- n.X = mkArgCopy(n.X)
- methSelectorExpr = n
- if callX.Op() == ir.ODOTINTER {
- // Currently for "defer i.M()" if i is nil it panics at the
- // point of defer statement, not when deferred function is called.
- // (I think there is an issue discussing what is the intended
- // behavior but I cannot find it.)
- // We need to do the nil check outside of the wrapper.
- tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
- c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, tab)
- c.SetTypecheck(1)
- o.append(c)
- }
- case !(callX.Op() == ir.ONAME && callX.(*ir.Name).Class == ir.PFUNC):
- // Deal with "defer returnsafunc()(x, y)" (for
- // example) by copying the callee expression.
- fnExpr = mkArgCopy(callX)
- if callX.Op() == ir.OCLOSURE {
- // For "defer func(...)", in addition to copying the
- // closure into a temp, mark it as no longer directly
- // called.
- callX.(*ir.ClosureExpr).Func.SetClosureCalled(false)
- }
- }
- }
-
- // Create a new no-argument function that we'll hand off to defer.
- var noFuncArgs []*ir.Field
- noargst := ir.NewFuncType(base.Pos, nil, noFuncArgs, nil)
- wrapGoDefer_prgen++
- outerfn := ir.CurFunc
- wrapname := fmt.Sprintf("%v·dwrap·%d", outerfn, wrapGoDefer_prgen)
- sym := types.LocalPkg.Lookup(wrapname)
- fn := typecheck.DeclFunc(sym, noargst)
- fn.SetIsHiddenClosure(true)
- fn.SetWrapper(true)
-
- // helper for capturing reference to a var declared in an outer scope.
- capName := func(pos src.XPos, fn *ir.Func, n *ir.Name) *ir.Name {
- t := n.Type()
- cv := ir.CaptureName(pos, fn, n)
- cv.SetType(t)
- return typecheck.Expr(cv).(*ir.Name)
- }
-
- // Call args (x1, y1) need to be captured as part of the newly
- // created closure.
- newCallArgs := []ir.Node{}
- for i := range newNames {
- var arg ir.Node
- arg = capName(callArgs[i].Pos(), fn, newNames[i])
- if unsafeArgs[i] != nil {
- arg = ir.NewConvExpr(arg.Pos(), origArgs[i].Op(), origArgs[i].Type(), arg)
- }
- newCallArgs = append(newCallArgs, arg)
- }
- // Also capture the function or method expression (if needed) into
- // the closure.
- if fnExpr != nil {
- callX = capName(callX.Pos(), fn, fnExpr)
- }
- if methSelectorExpr != nil {
- methSelectorExpr.X = capName(callX.Pos(), fn, methSelectorExpr.X.(*ir.Name))
- }
- ir.FinishCaptureNames(n.Pos(), outerfn, fn)
-
- // This flags a builtin as opposed to a regular call.
- irregular := (call.Op() != ir.OCALLFUNC &&
- call.Op() != ir.OCALLMETH &&
- call.Op() != ir.OCALLINTER)
-
- // Construct new function body: f(x1, y1)
- op := ir.OCALL
- if irregular {
- op = call.Op()
- }
- newcall := mkNewCall(call.Pos(), op, callX, newCallArgs)
-
- // Type-check the result.
- if !irregular {
- typecheck.Call(newcall.(*ir.CallExpr))
- } else {
- typecheck.Stmt(newcall)
- }
-
- // Finalize body, register function on the main decls list.
- fn.Body = []ir.Node{newcall}
- typecheck.FinishFuncBody()
- typecheck.Func(fn)
- typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
-
- // Create closure expr
- clo := ir.NewClosureExpr(n.Pos(), fn)
- fn.OClosure = clo
- clo.SetType(fn.Type())
-
- // Set escape properties for closure.
- if n.Op() == ir.OGO {
- // For "go", assume that the closure is going to escape
- // (with an exception for the runtime, which doesn't
- // permit heap-allocated closures).
- if base.Ctxt.Pkgpath != "runtime" {
- clo.SetEsc(ir.EscHeap)
- }
- } else {
- // For defer, just use whatever result escape analysis
- // has determined for the defer.
- if n.Esc() == ir.EscNever {
- clo.SetTransient(true)
- clo.SetEsc(ir.EscNone)
- }
- }
-
- // Create new top level call to closure over argless function.
- topcall := ir.NewCallExpr(n.Pos(), ir.OCALL, clo, []ir.Node{})
- typecheck.Call(topcall)
-
- // Tag the call to insure that directClosureCall doesn't undo our work.
- topcall.PreserveClosure = true
-
- fn.SetClosureCalled(false)
-
- // Finally, point the defer statement at the newly generated call.
- n.Call = topcall
-}
-
// isFuncPCIntrinsic returns whether n is a direct call of internal/abi.FuncPCABIxxx functions.
func isFuncPCIntrinsic(n *ir.CallExpr) bool {
if n.Op() != ir.OCALLFUNC || n.X.Op() != ir.ONAME {
diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go
index b1169fdae8a1aba973a7e3023d56b882634e721b..aa8c5489630829ec8781cd56cd2e694a259e37fe 100644
--- a/src/cmd/compile/internal/walk/range.go
+++ b/src/cmd/compile/internal/walk/range.go
@@ -112,7 +112,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node {
}
// for v1, v2 := range ha { body }
- if cheapComputableIndex(t.Elem().Width) {
+ if cheapComputableIndex(t.Elem().Size()) {
// v1, v2 = hv1, ha[hv1]
tmp := ir.NewIndexExpr(base.Pos, ha, hv1)
tmp.SetBounded(true)
@@ -154,7 +154,7 @@ func walkRange(nrange *ir.RangeStmt) ir.Node {
// This runs *after* the condition check, so we know
// advancing the pointer is safe and won't go past the
// end of the allocation.
- as := ir.NewAssignStmt(base.Pos, hp, addptr(hp, t.Elem().Width))
+ as := ir.NewAssignStmt(base.Pos, hp, addptr(hp, t.Elem().Size()))
nfor.Late = []ir.Node{typecheck.Stmt(as)}
case types.TMAP:
@@ -408,7 +408,7 @@ func arrayClear(loop *ir.RangeStmt, v1, v2, a ir.Node) ir.Node {
return nil
}
- elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Width
+ elemsize := typecheck.RangeExprType(loop.X.Type()).Elem().Size()
if elemsize <= 0 || !ir.IsZero(stmt.Y) {
return nil
}
diff --git a/src/cmd/compile/internal/walk/select.go b/src/cmd/compile/internal/walk/select.go
index d2b67ddf55a7abc8d5ccba5dc9d991aa7cd7fd2e..fde8f5089590fbc2984a6fb2c98d2d1e0e0412f0 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/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go
index 0bf76680c4626546f00fee0890a4418a157c77b5..f09e9165464bcf9e31979752111a881c5091c84c 100644
--- a/src/cmd/compile/internal/walk/stmt.go
+++ b/src/cmd/compile/internal/walk/stmt.go
@@ -7,7 +7,6 @@ package walk
import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
- "cmd/compile/internal/typecheck"
)
// The result of walkStmt MUST be assigned back to n, e.g.
@@ -41,7 +40,6 @@ func walkStmt(n ir.Node) ir.Node {
ir.OAS2MAPR,
ir.OCLOSE,
ir.OCOPY,
- ir.OCALLMETH,
ir.OCALLINTER,
ir.OCALL,
ir.OCALLFUNC,
@@ -50,7 +48,7 @@ func walkStmt(n ir.Node) ir.Node {
ir.OPRINT,
ir.OPRINTN,
ir.OPANIC,
- ir.ORECOVER,
+ ir.ORECOVERFP,
ir.OGETG:
if n.Typecheck() == 0 {
base.Fatalf("missing typecheck: %+v", n)
@@ -138,6 +136,14 @@ func walkStmt(n ir.Node) ir.Node {
case ir.OTAILCALL:
n := n.(*ir.TailCallStmt)
+
+ var init ir.Nodes
+ n.Call.X = walkExpr(n.Call.X, &init)
+
+ if len(init) > 0 {
+ init.Append(n)
+ return ir.NewBlockStmt(n.Pos(), init)
+ }
return n
case ir.OINLMARK:
@@ -187,33 +193,28 @@ func walkFor(n *ir.ForStmt) ir.Node {
return n
}
+// validGoDeferCall reports whether call is a valid call to appear in
+// a go or defer statement; that is, whether it's a regular function
+// call without arguments or results.
+func validGoDeferCall(call ir.Node) bool {
+ if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
+ sig := call.X.Type()
+ return sig.NumParams()+sig.NumResults() == 0
+ }
+ return false
+}
+
// walkGoDefer walks an OGO or ODEFER node.
func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
+ if !validGoDeferCall(n.Call) {
+ base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
+ }
+
var init ir.Nodes
- switch call := n.Call; call.Op() {
- case ir.OPRINT, ir.OPRINTN:
- call := call.(*ir.CallExpr)
- n.Call = wrapCall(call, &init)
-
- case ir.ODELETE:
- call := call.(*ir.CallExpr)
- n.Call = wrapCall(call, &init)
-
- case ir.OCOPY:
- call := call.(*ir.BinaryExpr)
- n.Call = walkCopy(call, &init, true)
-
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER:
- call := call.(*ir.CallExpr)
- if len(call.KeepAlive) > 0 {
- n.Call = wrapCall(call, &init)
- } else {
- n.Call = walkExpr(call, &init)
- }
- default:
- n.Call = walkExpr(call, &init)
- }
+ call := n.Call.(*ir.CallExpr)
+ call.X = walkExpr(call.X, &init)
+
if len(init) > 0 {
init.Append(n)
return ir.NewBlockStmt(n.Pos(), init)
@@ -228,110 +229,3 @@ func walkIf(n *ir.IfStmt) ir.Node {
walkStmtList(n.Else)
return n
}
-
-// Rewrite
-// go builtin(x, y, z)
-// into
-// go func(a1, a2, a3) {
-// builtin(a1, a2, a3)
-// }(x, y, z)
-// for print, println, and delete.
-//
-// Rewrite
-// go f(x, y, uintptr(unsafe.Pointer(z)))
-// into
-// go func(a1, a2, a3) {
-// f(a1, a2, uintptr(a3))
-// }(x, y, unsafe.Pointer(z))
-// for function contains unsafe-uintptr arguments.
-
-var wrapCall_prgen int
-
-// The result of wrapCall MUST be assigned back to n, e.g.
-// n.Left = wrapCall(n.Left, init)
-func wrapCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
- if len(n.Init()) != 0 {
- walkStmtList(n.Init())
- init.Append(ir.TakeInit(n)...)
- }
-
- isBuiltinCall := n.Op() != ir.OCALLFUNC && n.Op() != ir.OCALLMETH && n.Op() != ir.OCALLINTER
-
- // Turn f(a, b, []T{c, d, e}...) back into f(a, b, c, d, e).
- if !isBuiltinCall && n.IsDDD {
- undoVariadic(n)
- }
-
- wrapArgs := n.Args
- // If there's a receiver argument, it needs to be passed through the wrapper too.
- if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER {
- recv := n.X.(*ir.SelectorExpr).X
- wrapArgs = append([]ir.Node{recv}, wrapArgs...)
- }
-
- // origArgs keeps track of what argument is uintptr-unsafe/unsafe-uintptr conversion.
- origArgs := make([]ir.Node, len(wrapArgs))
- var funcArgs []*ir.Field
- for i, arg := range wrapArgs {
- s := typecheck.LookupNum("a", i)
- if !isBuiltinCall && arg.Op() == ir.OCONVNOP && arg.Type().IsUintptr() && arg.(*ir.ConvExpr).X.Type().IsUnsafePtr() {
- origArgs[i] = arg
- arg = arg.(*ir.ConvExpr).X
- wrapArgs[i] = arg
- }
- funcArgs = append(funcArgs, ir.NewField(base.Pos, s, nil, arg.Type()))
- }
- t := ir.NewFuncType(base.Pos, nil, funcArgs, nil)
-
- wrapCall_prgen++
- sym := typecheck.LookupNum("wrap·", wrapCall_prgen)
- fn := typecheck.DeclFunc(sym, t)
-
- args := ir.ParamNames(t.Type())
- for i, origArg := range origArgs {
- if origArg == nil {
- continue
- }
- args[i] = ir.NewConvExpr(base.Pos, origArg.Op(), origArg.Type(), args[i])
- }
- if n.Op() == ir.OCALLMETH || n.Op() == ir.OCALLINTER {
- // Move wrapped receiver argument back to its appropriate place.
- recv := typecheck.Expr(args[0])
- n.X.(*ir.SelectorExpr).X = recv
- args = args[1:]
- }
- call := ir.NewCallExpr(base.Pos, n.Op(), n.X, args)
- if !isBuiltinCall {
- call.SetOp(ir.OCALL)
- call.IsDDD = n.IsDDD
- }
- fn.Body = []ir.Node{call}
-
- typecheck.FinishFuncBody()
-
- typecheck.Func(fn)
- typecheck.Stmts(fn.Body)
- typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
-
- call = ir.NewCallExpr(base.Pos, ir.OCALL, fn.Nname, wrapArgs)
- return walkExpr(typecheck.Stmt(call), init)
-}
-
-// undoVariadic turns a call to a variadic function of the form
-//
-// f(a, b, []T{c, d, e}...)
-//
-// back into
-//
-// f(a, b, c, d, e)
-//
-func undoVariadic(call *ir.CallExpr) {
- if call.IsDDD {
- last := len(call.Args) - 1
- if va := call.Args[last]; va.Op() == ir.OSLICELIT {
- va := va.(*ir.CompLitExpr)
- call.Args = append(call.Args[:last], va.List...)
- call.IsDDD = false
- }
- }
-}
diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go
index 162de018f637ef5a329d8a31cab1f64cc7906b54..3705c5b19265d9a4f5a4c5cf9d36cfba3cf94ac5 100644
--- a/src/cmd/compile/internal/walk/switch.go
+++ b/src/cmd/compile/internal/walk/switch.go
@@ -360,10 +360,10 @@ func walkSwitchType(sw *ir.SwitchStmt) {
}
if singleType != nil && singleType.IsInterface() {
- s.Add(ncase.Pos(), n1.Type(), caseVar, jmp)
+ s.Add(ncase.Pos(), n1, caseVar, jmp)
caseVarInitialized = true
} else {
- s.Add(ncase.Pos(), n1.Type(), nil, jmp)
+ s.Add(ncase.Pos(), n1, nil, jmp)
}
}
@@ -377,6 +377,17 @@ func walkSwitchType(sw *ir.SwitchStmt) {
}
val = ifaceData(ncase.Pos(), s.facename, singleType)
}
+ if len(ncase.List) == 1 && ncase.List[0].Op() == ir.ODYNAMICTYPE {
+ dt := ncase.List[0].(*ir.DynamicType)
+ x := ir.NewDynamicTypeAssertExpr(ncase.Pos(), ir.ODYNAMICDOTTYPE, val, dt.X)
+ if dt.ITab != nil {
+ // TODO: make ITab a separate field in DynamicTypeAssertExpr?
+ x.T = dt.ITab
+ }
+ x.SetType(caseVar.Type())
+ x.SetTypecheck(1)
+ val = x
+ }
l := []ir.Node{
ir.NewDecl(ncase.Pos(), ir.ODCL, caseVar),
ir.NewAssignStmt(ncase.Pos(), caseVar, val),
@@ -446,7 +457,8 @@ type typeClause struct {
body ir.Nodes
}
-func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir.Node) {
+func (s *typeSwitch) Add(pos src.XPos, n1 ir.Node, caseVar *ir.Name, jmp ir.Node) {
+ typ := n1.Type()
var body ir.Nodes
if caseVar != nil {
l := []ir.Node{
@@ -462,9 +474,25 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir
// cv, ok = iface.(type)
as := ir.NewAssignListStmt(pos, ir.OAS2, nil, nil)
as.Lhs = []ir.Node{caseVar, s.okname} // cv, ok =
- dot := ir.NewTypeAssertExpr(pos, s.facename, nil)
- dot.SetType(typ) // iface.(type)
- as.Rhs = []ir.Node{dot}
+ switch n1.Op() {
+ case ir.OTYPE:
+ // Static type assertion (non-generic)
+ dot := ir.NewTypeAssertExpr(pos, s.facename, nil)
+ dot.SetType(typ) // iface.(type)
+ as.Rhs = []ir.Node{dot}
+ case ir.ODYNAMICTYPE:
+ // Dynamic type assertion (generic)
+ dt := n1.(*ir.DynamicType)
+ dot := ir.NewDynamicTypeAssertExpr(pos, ir.ODYNAMICDOTTYPE, s.facename, dt.X)
+ if dt.ITab != nil {
+ dot.T = dt.ITab
+ }
+ dot.SetType(typ)
+ dot.SetTypecheck(1)
+ as.Rhs = []ir.Node{dot}
+ default:
+ base.Fatalf("unhandled type case %s", n1.Op())
+ }
appendWalkStmt(&body, as)
// if ok { goto label }
@@ -473,9 +501,10 @@ func (s *typeSwitch) Add(pos src.XPos, typ *types.Type, caseVar *ir.Name, jmp ir
nif.Body = []ir.Node{jmp}
body.Append(nif)
- if !typ.IsInterface() {
+ if n1.Op() == ir.OTYPE && !typ.IsInterface() {
+ // Defer static, noninterface cases so they can be binary searched by hash.
s.clauses = append(s.clauses, typeClause{
- hash: types.TypeHash(typ),
+ hash: types.TypeHash(n1.Type()),
body: body,
})
return
diff --git a/src/cmd/compile/internal/walk/walk.go b/src/cmd/compile/internal/walk/walk.go
index 26da6e314574431ef88187128097d4c644f00c1d..78cc2e69da091b1acf00109c036980bd69dd665f 100644
--- a/src/cmd/compile/internal/walk/walk.go
+++ b/src/cmd/compile/internal/walk/walk.go
@@ -113,8 +113,7 @@ func vmkcall(fn ir.Node, t *types.Type, init *ir.Nodes, va []ir.Node) *ir.CallEx
base.Fatalf("vmkcall %v needs %v args got %v", fn, n, len(va))
}
- call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, va)
- typecheck.Call(call)
+ call := typecheck.Call(base.Pos, fn, va, false).(*ir.CallExpr)
call.SetType(t)
return walkExpr(call, init).(*ir.CallExpr)
}
@@ -206,7 +205,7 @@ var mapdelete = mkmapnames("mapdelete", "")
func mapfast(t *types.Type) int {
// Check runtime/map.go:maxElemSize before changing.
- if t.Elem().Width > 128 {
+ if t.Elem().Size() > 128 {
return mapslow
}
switch reflectdata.AlgType(t.Key()) {
@@ -308,12 +307,12 @@ func mayCall(n ir.Node) bool {
default:
base.FatalfAt(n.Pos(), "mayCall %+v", n)
- case ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER,
+ case ir.OCALLFUNC, ir.OCALLINTER,
ir.OUNSAFEADD, ir.OUNSAFESLICE:
return true
case ir.OINDEX, ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR, ir.OSLICESTR,
- ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR:
+ ir.ODEREF, ir.ODOTPTR, ir.ODOTTYPE, ir.ODYNAMICDOTTYPE, ir.ODIV, ir.OMOD, ir.OSLICE2ARRPTR:
// These ops might panic, make sure they are done
// before we start marshaling args for a call. See issue 16760.
return true
@@ -343,7 +342,7 @@ func mayCall(n ir.Node) bool {
ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL,
ir.OCONVNOP, ir.ODOT,
ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR,
- ir.OBYTES2STRTMP, ir.OGETG, ir.OSLICEHEADER:
+ ir.OBYTES2STRTMP, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OSLICEHEADER:
// ok: operations that don't require function calls.
// Expand as needed.
}
diff --git a/src/cmd/compile/internal/wasm/ssa.go b/src/cmd/compile/internal/wasm/ssa.go
index 31b09016eb98296a3838bc39125dbb3f4f73a101..765051c9445b293e84f372fcd890598c048f3ba3 100644
--- a/src/cmd/compile/internal/wasm/ssa.go
+++ b/src/cmd/compile/internal/wasm/ssa.go
@@ -24,7 +24,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.ZeroRange = zeroRange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue
@@ -89,13 +88,7 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.ARET)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
-
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockDefer:
p := s.Prog(wasm.AGet)
@@ -123,10 +116,14 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
func ssaGenValue(s *ssagen.State, v *ssa.Value) {
switch v.Op {
- case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall:
+ case ssa.OpWasmLoweredStaticCall, ssa.OpWasmLoweredClosureCall, ssa.OpWasmLoweredInterCall, ssa.OpWasmLoweredTailCall:
s.PrepareCall(v)
if call, ok := v.Aux.(*ssa.AuxCall); ok && call.Fn == ir.Syms.Deferreturn {
- // add a resume point before call to deferreturn so it can be called again via jmpdefer
+ // The runtime needs to inject jumps to
+ // deferreturn calls using the address in
+ // _func.deferreturn. Hence, the call to
+ // deferreturn must itself be a resumption
+ // point so it gets a target PC.
s.Prog(wasm.ARESUMEPOINT)
}
if v.Op == ssa.OpWasmLoweredClosureCall {
@@ -138,6 +135,9 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p := s.Prog(obj.ACALL)
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
p.Pos = v.Pos
+ if v.Op == ssa.OpWasmLoweredTailCall {
+ p.As = obj.ARET
+ }
} else {
getValue64(s, v.Args[0])
p := s.Prog(obj.ACALL)
diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go
index 00a20e429f1d6112cbf985e0b3c8706b187b0356..5565bd32c765b36debf7cde83eab4f3d2d8a71dd 100644
--- a/src/cmd/compile/internal/x86/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -34,7 +34,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.ZeroRange = zerorange
arch.Ginsnop = ginsnop
- arch.Ginsnopdefer = ginsnop
arch.SSAMarkMoves = ssaMarkMoves
}
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
index a06fdbcb71770ebce782dcbba69fdc673b236ac7..32e29f347b9c7f675414ee46c037aa62ea682b00 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -752,6 +752,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.Op386CALLstatic, ssa.Op386CALLclosure, ssa.Op386CALLinter:
s.Call(v)
+ case ssa.Op386CALLtail:
+ s.TailCall(v)
case ssa.Op386NEGL,
ssa.Op386BSWAPL,
ssa.Op386NOTL:
@@ -892,14 +894,9 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()})
}
- case ssa.BlockExit:
+ case ssa.BlockExit, ssa.BlockRetJmp:
case ssa.BlockRet:
s.Prog(obj.ARET)
- case ssa.BlockRetJmp:
- p := s.Prog(obj.AJMP)
- p.To.Type = obj.TYPE_MEM
- p.To.Name = obj.NAME_EXTERN
- p.To.Sym = b.Aux.(*obj.LSym)
case ssa.Block386EQF:
s.CombJump(b, next, &eqfJumps)
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index 7ee000861bcc2ad073e68a38c227a6a401343ea8..9c8529f7eb600ab5e0457595343a3464aae44004 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 b794962205d3da7f8cdf9c37c43fecdc0473e81e..0e1dbc61943112cda78141572962d9434d7f7618 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"
@@ -150,7 +151,7 @@ func testSwitch() {
}
func testTypeSwitch() {
- var x = []interface{}{1, 2.0, "hi"}
+ var x = []any{1, 2.0, "hi"}
for _, v := range x {
switch func() { check(LINE, 3) }(); v.(type) {
case int:
@@ -214,7 +215,7 @@ func testEmptySwitches() {
switch 3 {
}
check(LINE, 1)
- switch i := (interface{})(3).(int); i {
+ switch i := (any)(3).(int); i {
}
check(LINE, 1)
c := make(chan int)
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index bec17696f3040acd6f78750f3306c1d3bbeba102..d37c3f83efb4d474175a46062956222c450ad906 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -32,6 +32,7 @@ var (
goos string
goarm string
go386 string
+ goamd64 string
gomips string
gomips64 string
goppc64 string
@@ -48,8 +49,6 @@ var (
exe string
defaultcc map[string]string
defaultcxx map[string]string
- defaultcflags string
- defaultldflags string
defaultpkgconfig string
defaultldso string
@@ -147,6 +146,12 @@ func xinit() {
}
go386 = b
+ b = os.Getenv("GOAMD64")
+ if b == "" {
+ b = "v1"
+ }
+ goamd64 = b
+
b = os.Getenv("GOMIPS")
if b == "" {
b = "hardfloat"
@@ -209,9 +214,6 @@ func xinit() {
defaultcc = compilerEnv("CC", cc)
defaultcxx = compilerEnv("CXX", cxx)
- defaultcflags = os.Getenv("CFLAGS")
- defaultldflags = os.Getenv("LDFLAGS")
-
b = os.Getenv("PKG_CONFIG")
if b == "" {
b = "pkg-config"
@@ -222,6 +224,7 @@ func xinit() {
// For tools being invoked but also for os.ExpandEnv.
os.Setenv("GO386", go386)
+ os.Setenv("GOAMD64", goamd64)
os.Setenv("GOARCH", goarch)
os.Setenv("GOARM", goarm)
os.Setenv("GOHOSTARCH", gohostarch)
@@ -809,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)
}
}
@@ -819,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 {
@@ -967,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") ||
@@ -1029,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 == "" {
@@ -1049,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
@@ -1186,6 +1168,9 @@ func cmdenv() {
if goarch == "386" {
xprintf(format, "GO386", go386)
}
+ if goarch == "amd64" {
+ xprintf(format, "GOAMD64", goamd64)
+ }
if goarch == "mips" || goarch == "mipsle" {
xprintf(format, "GOMIPS", gomips)
}
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index 54e935ad3bec1d35c5bf37732ed1bb659965792c..fdc1d257744fcdba092d5db466acdcf4e321ac13 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -60,6 +60,7 @@ func mkbuildcfg(file string) {
fmt.Fprintf(&buf, "import \"runtime\"\n")
fmt.Fprintln(&buf)
fmt.Fprintf(&buf, "const defaultGO386 = `%s`\n", go386)
+ fmt.Fprintf(&buf, "const defaultGOAMD64 = `%s`\n", goamd64)
fmt.Fprintf(&buf, "const defaultGOARM = `%s`\n", goarm)
fmt.Fprintf(&buf, "const defaultGOMIPS = `%s`\n", gomips)
fmt.Fprintf(&buf, "const defaultGOMIPS64 = `%s`\n", gomips64)
diff --git a/src/cmd/dist/buildtag.go b/src/cmd/dist/buildtag.go
new file mode 100644
index 0000000000000000000000000000000000000000..24776a0aaf735f14fe79caf53510248f09112742
--- /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/buildtag_test.go b/src/cmd/dist/buildtag_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f64abfd1f18c252ed1b8a8a9cbbbea5a286abc03
--- /dev/null
+++ b/src/cmd/dist/buildtag_test.go
@@ -0,0 +1,43 @@
+// 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"
+ "reflect"
+ "testing"
+)
+
+var buildParserTests = []struct {
+ x string
+ matched bool
+ err error
+}{
+ {"gc", true, nil},
+ {"gccgo", false, nil},
+ {"!gc", false, nil},
+ {"gc && gccgo", false, nil},
+ {"gc || gccgo", true, nil},
+ {"gc || (gccgo && !gccgo)", true, nil},
+ {"gc && (gccgo || !gccgo)", true, nil},
+ {"!(gc && (gccgo || !gccgo))", false, nil},
+ {"gccgo || gc", true, nil},
+ {"!(!(!(gccgo || gc)))", false, nil},
+ {"compiler_bootstrap", false, nil},
+ {"cmd_go_bootstrap", true, nil},
+ {"syntax(error", false, fmt.Errorf("parsing //go:build line: unexpected (")},
+ {"(gc", false, fmt.Errorf("parsing //go:build line: missing )")},
+ {"gc gc", false, fmt.Errorf("parsing //go:build line: unexpected tag")},
+ {"(gc))", false, fmt.Errorf("parsing //go:build line: unexpected )")},
+}
+
+func TestBuildParser(t *testing.T) {
+ for _, tt := range buildParserTests {
+ matched, err := matchexpr(tt.x)
+ if matched != tt.matched || !reflect.DeepEqual(err, tt.err) {
+ t.Errorf("matchexpr(%q) = %v, %v; want %v, %v", tt.x, matched, err, tt.matched, tt.err)
+ }
+ }
+}
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 26b33e389fe9283aabd5c0c8f44fba88fa44ea8c..036f8c52fa3918f66d05b27611cf5c349c941ef2 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -15,6 +15,7 @@ import (
"fmt"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strings"
)
@@ -46,6 +47,7 @@ var bootstrapDirs = []string{
"cmd/internal/obj/...",
"cmd/internal/objabi",
"cmd/internal/pkgpath",
+ "cmd/internal/quoted",
"cmd/internal/src",
"cmd/internal/sys",
"cmd/link",
@@ -92,10 +94,21 @@ var ignoreSuffixes = []string{
"_test.go",
}
+var tryDirs = []string{
+ "sdk/go1.17",
+ "go1.17",
+}
+
func bootstrapBuildTools() {
goroot_bootstrap := os.Getenv("GOROOT_BOOTSTRAP")
if goroot_bootstrap == "" {
- goroot_bootstrap = pathf("%s/go1.4", os.Getenv("HOME"))
+ home := os.Getenv("HOME")
+ goroot_bootstrap = pathf("%s/go1.4", home)
+ for _, d := range tryDirs {
+ if p := pathf("%s/%s", home, d); isdir(p) {
+ goroot_bootstrap = p
+ }
+ }
}
xprintf("Building Go toolchain1 using %s.\n", goroot_bootstrap)
@@ -276,7 +289,11 @@ func rewriteBlock%s(b *Block) bool { panic("unused during bootstrap") }
}
func bootstrapFixImports(srcFile string) string {
- lines := strings.SplitAfter(readfile(srcFile), "\n")
+ text := readfile(srcFile)
+ if !strings.Contains(srcFile, "/cmd/") && !strings.Contains(srcFile, `\cmd\`) {
+ text = regexp.MustCompile(`\bany\b`).ReplaceAllString(text, "interface{}")
+ }
+ lines := strings.SplitAfter(text, "\n")
inBlock := false
for i, line := range lines {
if strings.HasPrefix(line, "import (") {
diff --git a/src/cmd/dist/exec.go b/src/cmd/dist/exec.go
new file mode 100644
index 0000000000000000000000000000000000000000..67305530ae83fcbc6a30e1956ad25094d1935d30
--- /dev/null
+++ b/src/cmd/dist/exec.go
@@ -0,0 +1,53 @@
+// 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 (
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// setDir sets cmd.Dir to dir, and also adds PWD=dir to cmd's environment.
+func setDir(cmd *exec.Cmd, dir string) {
+ cmd.Dir = dir
+ setEnv(cmd, "PWD", dir)
+}
+
+// setEnv sets cmd.Env so that key = value.
+//
+// It first removes any existing values for key, so it is safe to call
+// even from within cmdbootstrap.
+func setEnv(cmd *exec.Cmd, key, value string) {
+ kv := key + "=" + value
+ if cmd.Env == nil {
+ cmd.Env = os.Environ()
+ }
+
+ prefix := kv[:len(key)+1]
+ for i, entry := range cmd.Env {
+ if strings.HasPrefix(entry, prefix) {
+ cmd.Env[i] = kv
+ return
+ }
+ }
+
+ cmd.Env = append(cmd.Env, kv)
+}
+
+// unsetEnv sets cmd.Env so that key is not present in the environment.
+func unsetEnv(cmd *exec.Cmd, key string) {
+ if cmd.Env == nil {
+ cmd.Env = os.Environ()
+ }
+
+ prefix := key + "="
+ for i, entry := range cmd.Env {
+ if strings.HasPrefix(entry, prefix) {
+ cmd.Env = append(cmd.Env[:i], cmd.Env[i+1:]...)
+ return
+ }
+ }
+}
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index f40fa926dfd398b2c8adf239122358ef49e358cc..50a2e5936c639846d8d9f78f78756552fcf10390 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -139,27 +139,35 @@ func (t *tester) run() {
goInstall("go", append([]string{"-a", "-i"}, toolchain...)...)
}
- // Complete rebuild bootstrap, even with -no-rebuild.
- // If everything is up-to-date, this is a no-op.
- // If everything is not up-to-date, the first checkNotStale
- // during the test process will kill the tests, so we might
- // as well install the world.
- // Now that for example "go install cmd/compile" does not
- // also install runtime (you need "go install -i cmd/compile"
- // for that), it's easy for previous workflows like
- // "rebuild the compiler and then run run.bash"
- // to break if we don't automatically refresh things here.
- // Rebuilding is a shortened bootstrap.
- // See cmdbootstrap for a description of the overall process.
- //
- // But don't do this if we're running in the Go build system,
- // where cmd/dist is invoked many times. This just slows that
- // down (Issue 24300).
- if !t.listMode && os.Getenv("GO_BUILDER_NAME") == "" {
- goInstall("go", append([]string{"-i"}, toolchain...)...)
- goInstall("go", append([]string{"-i"}, toolchain...)...)
- goInstall("go", "std", "cmd")
- checkNotStale("go", "std", "cmd")
+ if !t.listMode {
+ if os.Getenv("GO_BUILDER_NAME") == "" {
+ // Complete rebuild bootstrap, even with -no-rebuild.
+ // If everything is up-to-date, this is a no-op.
+ // If everything is not up-to-date, the first checkNotStale
+ // during the test process will kill the tests, so we might
+ // as well install the world.
+ // Now that for example "go install cmd/compile" does not
+ // also install runtime (you need "go install -i cmd/compile"
+ // for that), it's easy for previous workflows like
+ // "rebuild the compiler and then run run.bash"
+ // to break if we don't automatically refresh things here.
+ // Rebuilding is a shortened bootstrap.
+ // See cmdbootstrap for a description of the overall process.
+ goInstall("go", append([]string{"-i"}, toolchain...)...)
+ goInstall("go", append([]string{"-i"}, toolchain...)...)
+ goInstall("go", "std", "cmd")
+ } else {
+ // The Go builder infrastructure should always begin running tests from a
+ // clean, non-stale state, so there is no need to rebuild the world.
+ // Instead, we can just check that it is not stale, which may be less
+ // expensive (and is also more likely to catch bugs in the builder
+ // implementation).
+ willTest := []string{"std"}
+ if t.shouldTestCmd() {
+ willTest = append(willTest, "cmd")
+ }
+ checkNotStale("go", willTest...)
+ }
}
t.timeoutScale = 1
@@ -491,30 +499,6 @@ func (t *tester) registerTests() {
})
}
- // Test go/... cmd/gofmt with type parameters enabled.
- if !t.compileOnly {
- t.tests = append(t.tests, distTest{
- name: "tyepparams",
- heading: "go/... and cmd/gofmt tests with tag typeparams",
- fn: func(dt *distTest) error {
- t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=typeparams", "go/...")
- t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=typeparams", "cmd/gofmt")
- return nil
- },
- })
- }
-
- if t.iOS() && !t.compileOnly {
- t.tests = append(t.tests, distTest{
- name: "x509omitbundledroots",
- heading: "crypto/x509 without bundled roots",
- fn: func(dt *distTest) error {
- t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-tags=x509omitbundledroots", "-run=OmitBundledRoots", "crypto/x509")
- return nil
- },
- })
- }
-
// Test ios/amd64 for the iOS simulator.
if goos == "darwin" && goarch == "amd64" && t.cgoEnabled {
t.tests = append(t.tests, distTest{
@@ -522,7 +506,8 @@ func (t *tester) registerTests() {
heading: "GOOS=ios on darwin/amd64",
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "-run=SystemRoots", "crypto/x509")
- cmd.Env = append(os.Environ(), "GOOS=ios", "CGO_ENABLED=1")
+ setEnv(cmd, "GOOS", "ios")
+ setEnv(cmd, "CGO_ENABLED", "1")
return nil
},
})
@@ -542,7 +527,7 @@ func (t *tester) registerTests() {
cmd := t.addCmd(dt, "src", t.goTest(), t.timeout(300), "runtime", "-cpu=1,2,4", "-quick")
// We set GOMAXPROCS=2 in addition to -cpu=1,2,4 in order to test runtime bootstrap code,
// creation of first goroutines and first garbage collections in the parallel setting.
- cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
+ setEnv(cmd, "GOMAXPROCS", "2")
return nil
},
})
@@ -563,7 +548,7 @@ func (t *tester) registerTests() {
return nil
}
cmd := exec.Command("go", "test")
- cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")
+ setDir(cmd, filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153"))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
@@ -600,16 +585,13 @@ func (t *tester) registerTests() {
return err
}
- // Run `go test fmt` in the moved GOROOT.
+ // Run `go test fmt` in the moved GOROOT, without explicitly setting
+ // GOROOT in the environment. The 'go' command should find itself.
cmd := exec.Command(filepath.Join(moved, "bin", "go"), "test", "fmt")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- // Don't set GOROOT in the environment.
- for _, e := range os.Environ() {
- if !strings.HasPrefix(e, "GOROOT=") && !strings.HasPrefix(e, "GOCACHE=") {
- cmd.Env = append(cmd.Env, e)
- }
- }
+ unsetEnv(cmd, "GOROOT")
+ unsetEnv(cmd, "GOCACHE") // TODO(bcmills): ...why‽
err := cmd.Run()
if rerr := os.Rename(moved, goroot); rerr != nil {
@@ -736,11 +718,9 @@ func (t *tester) registerTests() {
heading: "../misc/swig/callback",
fn: func(dt *distTest) error {
cmd := t.addCmd(dt, "misc/swig/callback", t.goTest())
- cmd.Env = append(os.Environ(),
- "CGO_CFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
- "CGO_CXXFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
- "CGO_LDFLAGS=-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option",
- )
+ setEnv(cmd, "CGO_CFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option")
+ setEnv(cmd, "CGO_CXXFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option")
+ setEnv(cmd, "CGO_LDFLAGS", "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option")
return nil
},
},
@@ -892,9 +872,9 @@ func (t *tester) registerSeqTest(name, dirBanner string, cmdline ...interface{})
func (t *tester) bgDirCmd(dir, bin string, args ...string) *exec.Cmd {
cmd := exec.Command(bin, args...)
if filepath.IsAbs(dir) {
- cmd.Dir = dir
+ setDir(cmd, dir)
} else {
- cmd.Dir = filepath.Join(goroot, dir)
+ setDir(cmd, filepath.Join(goroot, dir))
}
return cmd
}
@@ -997,11 +977,6 @@ func (t *tester) internalLink() bool {
// linkmode=internal fails on dragonfly since errno is a TLS relocation.
return false
}
- if gohostarch == "ppc64le" {
- // linkmode=internal fails on ppc64le because cmd/link doesn't
- // handle the TOC correctly (issue 15409).
- return false
- }
if goos == "android" {
return false
}
@@ -1027,7 +1002,7 @@ func (t *tester) internalLink() bool {
func (t *tester) internalLinkPIE() bool {
switch goos + "-" + goarch {
case "darwin-amd64", "darwin-arm64",
- "linux-amd64", "linux-arm64",
+ "linux-amd64", "linux-arm64", "linux-ppc64le",
"android-arm64",
"windows-amd64", "windows-386", "windows-arm":
return true
@@ -1045,7 +1020,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
@@ -1053,7 +1028,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",
@@ -1068,10 +1043,8 @@ func (t *tester) supportedBuildmode(mode string) bool {
}
return false
case "plugin":
- // linux-arm64 is missing because it causes the external linker
- // to crash, see https://golang.org/issue/17138
switch pair {
- case "linux-386", "linux-amd64", "linux-arm", "linux-s390x", "linux-ppc64le":
+ case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-s390x", "linux-ppc64le":
return true
case "darwin-amd64", "darwin-arm64":
return true
@@ -1132,7 +1105,8 @@ func (t *tester) runHostTest(dir, pkg string) error {
defer os.Remove(f.Name())
cmd := t.dirCmd(dir, t.goTest(), "-c", "-o", f.Name(), pkg)
- cmd.Env = append(os.Environ(), "GOARCH="+gohostarch, "GOOS="+gohostos)
+ setEnv(cmd, "GOARCH", gohostarch)
+ setEnv(cmd, "GOOS", gohostos)
if err := cmd.Run(); err != nil {
return err
}
@@ -1141,7 +1115,7 @@ func (t *tester) runHostTest(dir, pkg string) error {
func (t *tester) cgoTest(dt *distTest) error {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
- cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=auto")
+ setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=auto")
// Skip internal linking cases on linux/arm64 to support GCC-9.4 and above.
// See issue #39466.
@@ -1149,7 +1123,7 @@ func (t *tester) cgoTest(dt *distTest) error {
if t.internalLink() && !skipInternalLink {
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=internal")
- cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=internal")
+ setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=internal")
}
pair := gohostos + "-" + goarch
@@ -1161,9 +1135,9 @@ func (t *tester) cgoTest(dt *distTest) error {
break
}
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
- cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")
+ setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external")
- cmd = t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s")
+ t.addCmd(dt, "misc/cgo/test", t.goTest(), "-ldflags", "-linkmode=external -s")
if t.supportedBuildmode("pie") {
t.addCmd(dt, "misc/cgo/test", t.goTest(), "-buildmode=pie")
@@ -1181,10 +1155,10 @@ func (t *tester) cgoTest(dt *distTest) error {
"openbsd-386", "openbsd-amd64", "openbsd-arm", "openbsd-arm64", "openbsd-mips64":
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest())
- cmd.Env = append(os.Environ(), "GOFLAGS=-ldflags=-linkmode=external")
+ setEnv(cmd, "GOFLAGS", "-ldflags=-linkmode=external")
// cgo should be able to cope with both -g arguments and colored
// diagnostics.
- cmd.Env = append(cmd.Env, "CGO_CFLAGS=-g0 -fdiagnostics-color")
+ setEnv(cmd, "CGO_CFLAGS", "-g0 -fdiagnostics-color")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=auto")
t.addCmd(dt, "misc/cgo/testtls", t.goTest(), "-ldflags", "-linkmode=external")
@@ -1217,7 +1191,7 @@ func (t *tester) cgoTest(dt *distTest) error {
// than -static in -extldflags, so test both.
// See issue #16651.
cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-tags=static")
- cmd.Env = append(os.Environ(), "CGO_LDFLAGS=-static -pthread")
+ setEnv(cmd, "CGO_LDFLAGS", "-static -pthread")
}
}
@@ -1456,7 +1430,7 @@ func (t *tester) raceTest(dt *distTest) error {
// We shouldn't need to redo all of misc/cgo/test too.
// The race buildler will take care of this.
// cmd := t.addCmd(dt, "misc/cgo/test", t.goTest(), "-race")
- // cmd.Env = append(os.Environ(), "GOTRACEBACK=2")
+ // setEnv(cmd, "GOTRACEBACK", "2")
}
if t.extLink() {
// Test with external linking; see issue 9133.
@@ -1486,7 +1460,8 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error {
})
cmd := t.dirCmd("test", "go", "build", "-o", runtest.exe, "run.go")
- cmd.Env = append(os.Environ(), "GOOS="+gohostos, "GOARCH="+gohostarch)
+ setEnv(cmd, "GOOS", gohostos)
+ setEnv(cmd, "GOARCH", gohostarch)
runtest.err = cmd.Run()
})
if runtest.err != nil {
@@ -1650,7 +1625,7 @@ func (t *tester) runPrecompiledStdTest(timeout time.Duration) error {
bin := t.prebuiltGoPackageTestBinary()
fmt.Fprintf(os.Stderr, "# %s: using pre-built %s...\n", stdMatches[0], bin)
cmd := exec.Command(bin, "-test.short="+short(), "-test.timeout="+timeout.String())
- cmd.Dir = filepath.Dir(bin)
+ setDir(cmd, filepath.Dir(bin))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go
index df60145d1e21e8f2a9a4fc421e56c2f20d305c6b..8856f467d5ba8e66ed4ad851ed3ea4abc9efdf8a 100644
--- a/src/cmd/dist/util.go
+++ b/src/cmd/dist/util.go
@@ -72,7 +72,7 @@ func run(dir string, mode int, cmd ...string) string {
}
xcmd := exec.Command(cmd[0], cmd[1:]...)
- xcmd.Dir = dir
+ setDir(xcmd, dir)
var data []byte
var err error
@@ -172,6 +172,9 @@ func bgwait(wg *sync.WaitGroup) {
select {
case <-done:
case <-dying:
+ // Don't return to the caller, to avoid reporting additional errors
+ // to the user.
+ select {}
}
}
diff --git a/src/cmd/doc/dirs.go b/src/cmd/doc/dirs.go
index 661624cfe4c168c793d582203de163f3169634e6..f27af1d27befbe10f4fcf61fc21eed960f6e9867 100644
--- a/src/cmd/doc/dirs.go
+++ b/src/cmd/doc/dirs.go
@@ -221,11 +221,7 @@ func findCodeRoots() []Dir {
cmd.Stderr = os.Stderr
out, _ := cmd.Output()
for _, line := range strings.Split(string(out), "\n") {
- i := strings.Index(line, "\t")
- if i < 0 {
- continue
- }
- path, dir := line[:i], line[i+1:]
+ path, dir, _ := strings.Cut(line, "\t")
if dir != "" {
list = append(list, Dir{importPath: path, dir: dir, inModule: true})
}
diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go
index 0499c403695247035687054c0700fe28500a0b57..dee5d7bbcd7142d07c822387d2b42d6ce65fa522 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -110,6 +110,13 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
if buildPackage == nil {
return fmt.Errorf("no such package: %s", userPath)
}
+
+ // The builtin package needs special treatment: its symbols are lower
+ // case but we want to see them, always.
+ if buildPackage.ImportPath == "builtin" {
+ unexported = true
+ }
+
symbol, method = parseSymbol(sym)
pkg := parsePackage(writer, buildPackage, userPath)
paths = append(paths, pkg.prettyPath())
@@ -128,12 +135,6 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
panic(e)
}()
- // The builtin package needs special treatment: its symbols are lower
- // case but we want to see them, always.
- if pkg.build.ImportPath == "builtin" {
- unexported = true
- }
-
// We have a package.
if showAll && symbol == "" {
pkg.allDoc()
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 587f0bdc1468f2fd23a2f15d8a097a89137f4fef..f51efe08af5c1ab35055f92aff6b466484ba52c1 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -122,7 +122,7 @@ func trim(path, prefix string) (string, bool) {
// main do function, so it doesn't cause an exit. Allows testing to work
// without running a subprocess. The log prefix will be added when
// logged in main; it is not added here.
-func (pkg *Package) Fatalf(format string, args ...interface{}) {
+func (pkg *Package) Fatalf(format string, args ...any) {
panic(PackageError(fmt.Sprintf(format, args...)))
}
@@ -209,7 +209,7 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
return p
}
-func (pkg *Package) Printf(format string, args ...interface{}) {
+func (pkg *Package) Printf(format string, args ...any) {
fmt.Fprintf(&pkg.buf, format, args...)
}
@@ -235,7 +235,7 @@ func (pkg *Package) newlines(n int) {
// clears the stuff we don't want to print anyway. It's a bit of a magic trick.
func (pkg *Package) emit(comment string, node ast.Node) {
if node != nil {
- var arg interface{} = node
+ var arg any = node
if showSrc {
// Need an extra little dance to get internal comments to appear.
arg = &printer.CommentedNode{
@@ -315,9 +315,7 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
recv = "(" + recv + ") "
}
fnc := pkg.oneLineNodeDepth(n.Type, depth)
- if strings.Index(fnc, "func") == 0 {
- fnc = fnc[4:]
- }
+ fnc = strings.TrimPrefix(fnc, "func")
return fmt.Sprintf("func %s%s%s", recv, name, fnc)
case *ast.TypeSpec:
@@ -325,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
@@ -344,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 {
@@ -421,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/doc/testdata/nested/ignore.go b/src/cmd/doc/testdata/nested/ignore.go
index c497f1b5bc410017f84927737153148254e8447a..5fa811d0a859c192c3a34713e970e604b2f93da3 100644
--- a/src/cmd/doc/testdata/nested/ignore.go
+++ b/src/cmd/doc/testdata/nested/ignore.go
@@ -1,3 +1,4 @@
+//go:build ignore
// +build ignore
// Ignored package
diff --git a/src/cmd/fix/buildtag.go b/src/cmd/fix/buildtag.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f4fbfef16f15b99d78ff34d2f2c0a02334760db
--- /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 0000000000000000000000000000000000000000..1c6efbe9e0326232a59a9548e2cde10fb73e2bc0
--- /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 b47b06682add8040c98fa9480e73bf3788d6e062..27e4088aa9fa7f28bf98870758689eb2d42d9f32 100644
--- a/src/cmd/fix/cftype.go
+++ b/src/cmd/fix/cftype.go
@@ -45,8 +45,8 @@ func typefix(f *ast.File, badType func(string) bool) bool {
// step 1: Find all the nils with the offending types.
// Compute their replacement.
- badNils := map[interface{}]ast.Expr{}
- walk(f, func(n interface{}) {
+ badNils := map[any]ast.Expr{}
+ walk(f, func(n any) {
if i, ok := n.(*ast.Ident); ok && i.Name == "nil" && badType(typeof[n]) {
badNils[n] = &ast.BasicLit{ValuePos: i.NamePos, Kind: token.INT, Value: "0"}
}
@@ -58,12 +58,12 @@ func typefix(f *ast.File, badType func(string) bool) bool {
if len(badNils) > 0 {
exprType := reflect.TypeOf((*ast.Expr)(nil)).Elem()
exprSliceType := reflect.TypeOf(([]ast.Expr)(nil))
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
if n == nil {
return
}
v := reflect.ValueOf(n)
- if v.Type().Kind() != reflect.Ptr {
+ if v.Type().Kind() != reflect.Pointer {
return
}
if v.IsNil() {
@@ -99,7 +99,7 @@ func typefix(f *ast.File, badType func(string) bool) bool {
// Now we need unsafe.Pointer as an intermediate cast.
// (*unsafe.Pointer)(x) where x is type *bad -> (*unsafe.Pointer)(unsafe.Pointer(x))
// (*bad.type)(x) where x is type *unsafe.Pointer -> (*bad.type)(unsafe.Pointer(x))
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
if n == nil {
return
}
diff --git a/src/cmd/fix/fix.go b/src/cmd/fix/fix.go
index b49db375710c3c511ca26a3fa55fa406b19d19ff..7abdab28a8df21095aebc8ea009919406dd03128 100644
--- a/src/cmd/fix/fix.go
+++ b/src/cmd/fix/fix.go
@@ -43,15 +43,15 @@ func register(f fix) {
// walk traverses the AST x, calling visit(y) for each node y in the tree but
// also with a pointer to each ast.Expr, ast.Stmt, and *ast.BlockStmt,
// in a bottom-up traversal.
-func walk(x interface{}, visit func(interface{})) {
+func walk(x any, visit func(any)) {
walkBeforeAfter(x, nop, visit)
}
-func nop(interface{}) {}
+func nop(any) {}
// walkBeforeAfter is like walk but calls before(x) before traversing
// x's children and after(x) afterward.
-func walkBeforeAfter(x interface{}, before, after func(interface{})) {
+func walkBeforeAfter(x any, before, after func(any)) {
before(x)
switch n := x.(type) {
@@ -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:
@@ -381,7 +390,7 @@ func renameTop(f *ast.File, old, new string) bool {
// Rename top-level old to new, both unresolved names
// (probably defined in another file) and names that resolve
// to a declaration we renamed.
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
id, ok := n.(*ast.Ident)
if ok && isTopName(id, old) {
id.Name = new
diff --git a/src/cmd/fix/gotypes.go b/src/cmd/fix/gotypes.go
index 031f85c9cc5c18ebf3ea1779ebeddfcbcc86b789..6085816ada45c0d9b7f706574b07298aacad3155 100644
--- a/src/cmd/fix/gotypes.go
+++ b/src/cmd/fix/gotypes.go
@@ -36,7 +36,7 @@ func fixGoExact(f *ast.File) bool {
// This one is harder because the import name changes.
// First find the import spec.
var importSpec *ast.ImportSpec
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
if importSpec != nil {
return
}
diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go
index d055929aac0767bcd6b0be7ff6550b239c55af34..3229b71ec488ea3614afe6f1ee4134ff2c2eac49 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 != "" {
@@ -219,7 +245,7 @@ func processFile(filename string, useStdin bool) error {
return os.WriteFile(f.Name(), newSrc, 0)
}
-func gofmt(n interface{}) string {
+func gofmt(n any) string {
var gofmtBuf bytes.Buffer
if err := format.Node(&gofmtBuf, fset, n); err != nil {
return "<" + err.Error() + ">"
diff --git a/src/cmd/fix/main_test.go b/src/cmd/fix/main_test.go
index af16bcaa31ed94ca6e1f611c847cbae3e0f41786..1baa95c5456bcc85d7158902f9c2afa382363fdf 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/netipv6zone.go b/src/cmd/fix/netipv6zone.go
index 3e502bda07cb964ab77a7aaccf13b656a9b93a6b..199fcf5bf5976520857e7c194341e5443f60684a 100644
--- a/src/cmd/fix/netipv6zone.go
+++ b/src/cmd/fix/netipv6zone.go
@@ -26,7 +26,7 @@ func netipv6zone(f *ast.File) bool {
}
fixed := false
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
cl, ok := n.(*ast.CompositeLit)
if !ok {
return
diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go
index 6d9399687281134b22fd791ea54bfc4cc2588d40..bad6953196421408fbf905f169342dffc51f1313 100644
--- a/src/cmd/fix/printerconfig.go
+++ b/src/cmd/fix/printerconfig.go
@@ -23,7 +23,7 @@ func printerconfig(f *ast.File) bool {
}
fixed := false
- walk(f, func(n interface{}) {
+ walk(f, func(n any) {
cl, ok := n.(*ast.CompositeLit)
if !ok {
return
diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go
index 39a53785b724d4105ccfd6d7bdd320a7f3428005..8a18d61bf2748e5c080cb4811a2782b01de428c4 100644
--- a/src/cmd/fix/typecheck.go
+++ b/src/cmd/fix/typecheck.go
@@ -142,9 +142,9 @@ func (typ *Type) dot(cfg *TypeConfig, name string) string {
// typeof maps AST nodes to type information in gofmt string form.
// assign maps type strings to lists of expressions that were assigned
// to values of another type that were assigned to that type.
-func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, assign map[string][]interface{}) {
- typeof = make(map[interface{}]string)
- assign = make(map[string][]interface{})
+func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[any]string, assign map[string][]any) {
+ typeof = make(map[any]string)
+ assign = make(map[string][]any)
cfg1 := &TypeConfig{}
*cfg1 = *cfg // make copy so we can add locally
copied := false
@@ -296,7 +296,7 @@ func makeExprList(a []*ast.Ident) []ast.Expr {
// Typecheck1 is the recursive form of typecheck.
// It is like typecheck but adds to the information in typeof
// instead of allocating a new map.
-func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, assign map[string][]interface{}) {
+func typecheck1(cfg *TypeConfig, f any, typeof map[any]string, assign map[string][]any) {
// set sets the type of n to typ.
// If isDecl is true, n is being declared.
set := func(n ast.Expr, typ string, isDecl bool) {
@@ -368,7 +368,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
// the curfn stack.
var curfn []*ast.FuncType
- before := func(n interface{}) {
+ before := func(n any) {
// push function type on stack
switch n := n.(type) {
case *ast.FuncDecl:
@@ -379,11 +379,11 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
}
// After is the real type checker.
- after := func(n interface{}) {
+ after := func(n any) {
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())
@@ -544,8 +544,8 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") {
// Lazy: assume there are no nested [] in the array
// length or map key type.
- if i := strings.Index(t, "]"); i >= 0 {
- typeof[n] = t[i+1:]
+ if _, elem, ok := strings.Cut(t, "]"); ok {
+ typeof[n] = elem
}
}
@@ -575,8 +575,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
t := expand(typeof[n])
if strings.HasPrefix(t, "[") { // array or slice
// Lazy: assume there are no nested [] in the array length.
- if i := strings.Index(t, "]"); i >= 0 {
- et := t[i+1:]
+ if _, et, ok := strings.Cut(t, "]"); ok {
for _, e := range n.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
e = kv.Value
@@ -589,8 +588,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
}
if strings.HasPrefix(t, "map[") { // map
// Lazy: assume there are no nested [] in the map key type.
- if i := strings.Index(t, "]"); i >= 0 {
- kt, vt := t[4:i], t[i+1:]
+ if kt, vt, ok := strings.Cut(t[len("map["):], "]"); ok {
for _, e := range n.Elts {
if kv, ok := e.(*ast.KeyValueExpr); ok {
if typeof[kv.Key] == "" {
@@ -629,12 +627,10 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
key, value = "int", "rune"
} else if strings.HasPrefix(t, "[") {
key = "int"
- if i := strings.Index(t, "]"); i >= 0 {
- value = t[i+1:]
- }
+ _, value, _ = strings.Cut(t, "]")
} else if strings.HasPrefix(t, "map[") {
- if i := strings.Index(t, "]"); i >= 0 {
- key, value = t[4:i], t[i+1:]
+ if k, v, ok := strings.Cut(t[len("map["):], "]"); ok {
+ key, value = k, v
}
}
changed := false
diff --git a/src/cmd/go.mod b/src/cmd/go.mod
index cd03968eedcf618d0ecef8da89ce790646dba399..f46c770c199b4f865b4cf0431b1b442df150d7eb 100644
--- a/src/cmd/go.mod
+++ b/src/cmd/go.mod
@@ -1,15 +1,19 @@
module cmd
-go 1.17
+go 1.18
require (
- github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a
- github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 // indirect
- golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e
- golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e // indirect
- golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a
- golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 // indirect
- golang.org/x/term v0.0.0-20210503060354-a79de5458b56
- golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9
+ github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31
+ golang.org/x/arch v0.0.0-20210923205945-b76863e36670
+ golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020
+ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
+ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
+ golang.org/x/tools v0.1.9-0.20211207220608-fd2bfb79a16a
+)
+
+require (
+ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d // indirect
+ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
+ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
diff --git a/src/cmd/go.sum b/src/cmd/go.sum
index d728acaec9925e702dac5a61edeaebac48d878c6..941011fe09fc9e512030fe85b221adac28251d7e 100644
--- a/src/cmd/go.sum
+++ b/src/cmd/go.sum
@@ -1,45 +1,24 @@
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-20210506205249-923b5ab0fc1a h1:jmAp/2PZAScNd62lTD3Mcb0Ey9FvIIJtLohPhtxZJ+Q=
-github.com/google/pprof v0.0.0-20210506205249-923b5ab0fc1a/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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e h1:pv3V0NlNSh5Q6AX/StwGLBjcLS7UN4m4Gq+V+uSecqM=
-golang.org/x/arch v0.0.0-20210502124803-cbf565b21d1e/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI=
-golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a h1:e8qnjKz4EE6OjRki9wTadWSIogINvq10sMcuBRORxMY=
-golang.org/x/mod v0.4.3-0.20210608190319-0f08993efd8a/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+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-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
+golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020 h1:HjtpZuJcnSa+yHlL4Y5aypjDvbHkJne5FS8JRmKI2+I=
+golang.org/x/mod v0.6.0-dev.0.20211102181907-3a5865c02020/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744 h1:yhBbb4IRs2HS9PPlAg6DMC6mUOKexJBNsLf4Z+6En1Q=
-golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
-golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9 h1:2XlR/j4I4xz5GQZI7zBjqTfezYyRIE2jD5IMousB2rg=
-golang.org/x/tools v0.1.2-0.20210519160823-49064d2332f9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
+golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/tools v0.1.9-0.20211207220608-fd2bfb79a16a h1:G+TZ7v63o8mn+LBWOdnHaiypIhcgFZ6BDDnyX+RXDYg=
+golang.org/x/tools v0.1.9-0.20211207220608-fd2bfb79a16a/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
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=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index 7f88d3216cf080a9573026997231cc608a801589..6703792054d70d055bec469db2a35b0cf2509b21 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -25,6 +25,7 @@
// install compile and install packages and dependencies
// list list packages or modules
// mod module maintenance
+// work workspace maintenance
// run compile and run Go program
// test test packages
// tool run specified go tool
@@ -114,13 +115,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 +135,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 +159,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
@@ -167,6 +182,14 @@
// directory, but it is not accessed. When -modfile is specified, an
// alternate go.sum file is also used: its path is derived from the
// -modfile flag by trimming the ".mod" extension and appending ".sum".
+// -workfile file
+// in module aware mode, use the given go.work file as a workspace file.
+// By default or when -workfile is "auto", the go command searches for a
+// file named go.work in the current directory and then containing directories
+// until one is found. If a valid go.work file is found, the modules
+// specified will collectively be used as the main modules. If -workfile
+// is "off", or a go.work file is not found in "auto" mode, workspace
+// mode is disabled.
// -overlay file
// read a JSON config file that provides an overlay for build operations.
// The file is a JSON struct with a single field, named 'Replace', that
@@ -284,6 +307,13 @@
// download cache, including unpacked source code of versioned
// dependencies.
//
+// The -fuzzcache flag causes clean to remove files stored in the Go build
+// cache for fuzz testing. The fuzzing engine caches files that expand
+// code coverage, so removing them may make fuzzing less effective until
+// new inputs are found that provide the same coverage. These files are
+// distinct from those stored in testdata directory; clean does not remove
+// those files.
+//
// For more about build flags, see 'go help build'.
//
// For more about specifying packages, see 'go help packages'.
@@ -338,9 +368,8 @@
// path. The go tool's usual package mechanism does not apply: package path
// elements like . and ... are not implemented by go doc.
//
-// When run with two arguments, the first must be a full package path (not just a
-// suffix), and the second is a symbol, or symbol with method or struct field.
-// This is similar to the syntax accepted by godoc:
+// When run with two arguments, the first is a package path (full path or suffix),
+// and the second is a symbol, or symbol with method or struct field:
//
// go doc [.]
//
@@ -438,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.
//
@@ -483,7 +516,7 @@
// files. Those commands can run any process but the intent is to
// create or update Go source files.
//
-// Go generate is never run automatically by go build, go get, go test,
+// Go generate is never run automatically by go build, go test,
// and so on. It must be run explicitly.
//
// Go generate scans the file for directives, which are lines of
@@ -598,11 +631,11 @@
//
// Usage:
//
-// go get [-d] [-t] [-u] [-v] [build flags] [packages]
+// go get [-t] [-u] [-v] [build flags] [packages]
//
// Get resolves its command-line arguments to packages at specific module versions,
-// updates go.mod to require those versions, downloads source code into the
-// module cache, then builds and installs the named packages.
+// updates go.mod to require those versions, and downloads source code into the
+// module cache.
//
// To add a dependency for a package or upgrade it to its latest version:
//
@@ -618,17 +651,18 @@
//
// See https://golang.org/ref/mod#go-get for details.
//
-// The 'go install' command may be used to build and install packages. When a
-// version is specified, 'go install' runs in module-aware mode and ignores
-// the go.mod file in the current directory. For example:
+// In earlier versions of Go, 'go get' was used to build and install packages.
+// Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install'
+// may be used to build and install commands instead. When a version is specified,
+// 'go install' runs in module-aware mode and ignores the go.mod file in the
+// current directory. For example:
//
// go install example.com/pkg@v1.2.3
// go install example.com/pkg@latest
//
// See 'go help install' or https://golang.org/ref/mod#go-install for details.
//
-// In addition to build flags (listed in 'go help build') 'go get' accepts the
-// following flags.
+// 'go get' accepts the following flags.
//
// The -t flag instructs get to consider modules needed to build tests of
// packages specified on the command line.
@@ -643,15 +677,9 @@
// When the -t and -u flags are used together, get will update
// test dependencies as well.
//
-// The -d flag instructs get not to build or install packages. get will only
-// update go.mod and download source code needed to build packages.
-//
-// Building and installing packages with get is deprecated. In a future release,
-// the -d flag will be enabled by default, and 'go get' will be only be used to
-// adjust dependencies of the current module. To install a package using
-// dependencies from the current module, use 'go install'. To install a package
-// ignoring the current module, use 'go install' with an @version suffix like
-// "@latest" after each argument.
+// The -x flag prints commands as they are executed. This is useful for
+// debugging version control commands when a module is downloaded directly
+// from a repository.
//
// For more about modules, see https://golang.org/ref/mod.
//
@@ -694,14 +722,17 @@
//
// - All arguments must refer to packages in the same module at the same version.
//
+// - Package path arguments must refer to main packages. Pattern arguments
+// will only match main packages.
+//
// - No module is considered the "main" module. If the module containing
// packages named on the command line has a go.mod file, it must not contain
// directives (replace and exclude) that would cause it to be interpreted
// differently than if it were the main module. The module must not require
// a higher version of itself.
//
-// - Package path arguments must refer to main packages. Pattern arguments
-// will only match main packages.
+// - Vendor directories are not used in any module. (Vendor directories are not
+// included in the module zip files downloaded by 'go install'.)
//
// If the arguments don't have version suffixes, "go install" may run in
// module-aware mode or GOPATH mode, depending on the GO111MODULE environment
@@ -1041,8 +1072,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
@@ -1260,7 +1294,7 @@
//
// Usage:
//
-// go mod vendor [-e] [-v]
+// go mod vendor [-e] [-v] [-o outdir]
//
// Vendor resets the main module's vendor directory to include all packages
// needed to build and test all the main module's packages.
@@ -1272,6 +1306,11 @@
// The -e flag causes vendor to attempt to proceed despite errors
// encountered while loading packages.
//
+// The -o flag causes vendor to create the vendor directory at the given
+// path instead of "vendor". The go command can only use a vendor directory
+// named "vendor" within the module root directory, so this flag is
+// primarily useful for other tools.
+//
// See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'.
//
//
@@ -1329,6 +1368,148 @@
// See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'.
//
//
+// Workspace maintenance
+//
+// Go workspace provides access to operations on workspaces.
+//
+// Note that support for workspaces is built into many other commands,
+// not just 'go work'.
+//
+// See 'go help modules' for information about Go's module system of
+// which workspaces are a part.
+//
+// A workspace is specified by a go.work file that specifies a set of
+// module directories with the "use" directive. These modules are used
+// as root modules by the go command for builds and related operations.
+// A workspace that does not specify modules to be used cannot be used
+// to do builds from local modules.
+//
+// To determine whether the go command is operating in workspace mode,
+// use the "go env GOWORK" command. This will specify the workspace
+// file being used.
+//
+// Usage:
+//
+// go work [arguments]
+//
+// The commands are:
+//
+// edit edit go.work from tools or scripts
+// init initialize workspace file
+// sync sync workspace build list to modules
+// use add modules to workspace file
+//
+// Use "go help work " for more information about a command.
+//
+// Edit go.work from tools or scripts
+//
+// Usage:
+//
+// go work edit [editing flags] [go.work]
+//
+// Edit provides a command-line interface for editing go.work,
+// for use primarily by tools or scripts. It only reads go.work;
+// it does not look up information about the modules involved.
+// If no file is specified, Edit looks for a go.work file in the current
+// directory and its parent directories
+//
+// The editing flags specify a sequence of editing operations.
+//
+// The -fmt flag reformats the go.work file without making other changes.
+// This reformatting is also implied by any other modifications that use or
+// rewrite the go.mod file. The only time this flag is needed is if no other
+// flags are specified, as in 'go work edit -fmt'.
+//
+// The -use=path and -dropuse=path flags
+// add and drop a use directive from the go.work file's set of module directories.
+//
+// The -replace=old[@v]=new[@v] flag adds a replacement of the given
+// module path and version pair. If the @v in old@v is omitted, a
+// replacement without a version on the left side is added, which applies
+// to all versions of the old module path. If the @v in new@v is omitted,
+// the new path should be a local module root directory, not a module
+// path. Note that -replace overrides any redundant replacements for old[@v],
+// so omitting @v will drop existing replacements for specific versions.
+//
+// The -dropreplace=old[@v] flag drops a replacement of the given
+// module path and version pair. If the @v is omitted, a replacement without
+// a version on the left side is dropped.
+//
+// The -use, -dropuse, -replace, and -dropreplace,
+// editing flags may be repeated, and the changes are applied in the order given.
+//
+// The -go=version flag sets the expected Go language version.
+//
+// The -print flag prints the final go.work in its text format instead of
+// writing it back to go.mod.
+//
+// The -json flag prints the final go.work file in JSON format instead of
+// writing it back to go.mod. The JSON output corresponds to these Go types:
+//
+// type Module struct {
+// Path string
+// Version string
+// }
+//
+// type GoWork struct {
+// Go string
+// Directory []Directory
+// Replace []Replace
+// }
+//
+// type Use struct {
+// Path string
+// ModulePath string
+// }
+//
+// type Replace struct {
+// Old Module
+// New Module
+// }
+//
+// See the workspaces design proposal at
+// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
+// more information.
+//
+//
+// Initialize workspace file
+//
+// Usage:
+//
+// go work init [moddirs]
+//
+// Init initializes and writes a new go.work file in the current
+// directory, in effect creating a new workspace at the current directory.
+//
+// go work init optionally accepts paths to the workspace modules as arguments.
+// If the argument is omitted, an empty workspace with no modules will be created.
+//
+// See the workspaces design proposal at
+// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
+// more information.
+//
+//
+// Sync workspace build list to modules
+//
+// Usage:
+//
+// go work sync [moddirs]
+//
+// go work sync
+//
+//
+// Add modules to workspace file
+//
+// Usage:
+//
+// go work use [-r] [moddirs]
+//
+// Use provides a command-line interface for adding directories,
+// optionally recursively, to a go.work file.
+//
+// The -r flag searches recursively for modules in the argument directories.
+//
+//
// Compile and run Go program
//
// Usage:
@@ -1387,8 +1568,8 @@
//
// 'Go test' recompiles each package along with any files with names matching
// the file pattern "*_test.go".
-// These additional files can contain test functions, benchmark functions, and
-// example functions. See 'go help testfunc' for more.
+// These additional files can contain test functions, benchmark functions, fuzz
+// tests and example functions. See 'go help testfunc' for more.
// Each listed package causes the execution of a separate test binary.
// Files whose names begin with "_" (including "_test.go") or "." are ignored.
//
@@ -1405,7 +1586,8 @@
// used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas',
// 'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see
// the documentation for these and other vet tests via "go doc cmd/vet".
-// To disable the running of go vet, use the -vet=off flag.
+// To disable the running of go vet, use the -vet=off flag. To run all
+// checks, use the -vet=all flag.
//
// All test output and summary lines are printed to the go command's
// standard output, even if the test printed them to its own standard
@@ -1446,16 +1628,16 @@
// The rule for a match in the cache is that the run involves the same
// test binary and the flags on the command line come entirely from a
// restricted set of 'cacheable' test flags, defined as -benchtime, -cpu,
-// -list, -parallel, -run, -short, and -v. If a run of go test has any test
-// or non-test flags outside this set, the result is not cached. To
-// disable test caching, use any test flag or argument other than the
-// cacheable flags. The idiomatic way to disable test caching explicitly
-// is to use -count=1. Tests that open files within the package's source
-// root (usually $GOPATH) or that consult environment variables only
-// match future runs in which the files and environment variables are unchanged.
-// A cached test result is treated as executing in no time at all,
-// so a successful package test result will be cached and reused
-// regardless of -timeout setting.
+// -list, -parallel, -run, -short, -timeout, -failfast, and -v.
+// If a run of go test has any test or non-test flags outside this set,
+// the result is not cached. To disable test caching, use any test flag
+// or argument other than the cacheable flags. The idiomatic way to disable
+// test caching explicitly is to use -count=1. Tests that open files within
+// the package's source root (usually $GOPATH) or that consult environment
+// variables only match future runs in which the files and environment
+// variables are unchanged. A cached test result is treated as executing
+// in no time at all,so a successful package test result will be cached and
+// reused regardless of -timeout setting.
//
// In addition to the build flags, the flags handled by 'go test' itself are:
//
@@ -1746,6 +1928,13 @@
// See 'go help test' for details. Running 'go clean -testcache' removes
// all cached test results (but not cached build results).
//
+// The go command also caches values used in fuzzing with 'go test -fuzz',
+// specifically, values that expanded code coverage when passed to a
+// fuzz function. These values are not used for regular building and
+// testing, but they're stored in a subdirectory of the build cache.
+// Running 'go clean -fuzzcache' removes all cached fuzzing values.
+// This may make fuzzing less effective, temporarily.
+//
// The GODEBUG environment variable can enable printing of debugging
// information about the state of the cache:
//
@@ -1885,6 +2074,10 @@
// GO386
// For GOARCH=386, how to implement floating point instructions.
// Valid values are sse2 (default), softfloat.
+// GOAMD64
+// For GOARCH=amd64, the microarchitecture level for which to compile.
+// Valid values are v1 (default), v2, v3, v4.
+// See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels.
// GOMIPS
// For GOARCH=mips{,le}, whether to use floating point instructions.
// Valid values are hardfloat (default), softfloat.
@@ -2622,9 +2815,10 @@
// (for example, -benchtime 100x).
//
// -count n
-// Run each test and benchmark n times (default 1).
+// Run each test, benchmark, and fuzz seed n times (default 1).
// If -cpu is set, run n times for each GOMAXPROCS value.
-// Examples are always run once.
+// Examples are always run once. -count does not apply to
+// fuzz tests matched by -fuzz.
//
// -cover
// Enable coverage analysis.
@@ -2651,32 +2845,67 @@
// Sets -cover.
//
// -cpu 1,2,4
-// Specify a list of GOMAXPROCS values for which the tests or
-// benchmarks should be executed. The default is the current value
-// of GOMAXPROCS.
+// Specify a list of GOMAXPROCS values for which the tests, benchmarks or
+// fuzz tests should be executed. The default is the current value
+// of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
//
// -failfast
// Do not start new tests after the first test failure.
//
+// -fuzz regexp
+// Run the fuzz test matching the regular expression. When specified,
+// the command line argument must match exactly one package within the
+// main module, and regexp must match exactly one fuzz test within
+// that package. Fuzzing will occur after tests, benchmarks, seed corpora
+// of other fuzz tests, and examples have completed. See the Fuzzing
+// section of the testing package documentation for details.
+//
+// -fuzztime t
+// Run enough iterations of the fuzz target during fuzzing to take t,
+// specified as a time.Duration (for example, -fuzztime 1h30s).
+// The default is to run forever.
+// The special syntax Nx means to run the fuzz target N times
+// (for example, -fuzztime 1000x).
+//
+// -fuzzminimizetime t
+// Run enough iterations of the fuzz target during each minimization
+// attempt to take t, as specified as a time.Duration (for example,
+// -fuzzminimizetime 30s).
+// The default is 60s.
+// The special syntax Nx means to run the fuzz target N times
+// (for example, -fuzzminimizetime 100x).
+//
+// -json
+// Log verbose output and test results in JSON. This presents the
+// same information as the -v flag in a machine-readable format.
+//
// -list regexp
-// List tests, benchmarks, or examples matching the regular expression.
-// No tests, benchmarks or examples will be run. This will only
-// list top-level tests. No subtest or subbenchmarks will be shown.
+// List tests, benchmarks, fuzz tests, or examples matching the regular
+// expression. No tests, benchmarks, fuzz tests, or examples will be run.
+// This will only list top-level tests. No subtest or subbenchmarks will be
+// shown.
//
// -parallel n
-// Allow parallel execution of test functions that call t.Parallel.
+// Allow parallel execution of test functions that call t.Parallel, and
+// fuzz targets that call t.Parallel when running the seed corpus.
// The value of this flag is the maximum number of tests to run
-// simultaneously; by default, it is set to the value of GOMAXPROCS.
+// simultaneously.
+// While fuzzing, the value of this flag is the maximum number of
+// subprocesses that may call the fuzz function simultaneously, regardless of
+// whether T.Parallel is called.
+// By default, -parallel is set to the value of GOMAXPROCS.
+// Setting -parallel to values higher than GOMAXPROCS may cause degraded
+// performance due to CPU contention, especially when fuzzing.
// Note that -parallel only applies within a single test binary.
// The 'go test' command may run tests for different packages
// in parallel as well, according to the setting of the -p flag
// (see 'go help build').
//
// -run regexp
-// Run only those tests and examples matching the regular expression.
-// For tests, the regular expression is split by unbracketed slash (/)
-// characters into a sequence of regular expressions, and each part
-// of a test's identifier must match the corresponding element in
+// Run only those tests, examples, and fuzz tests matching the regular
+// expression. For tests, the regular expression is split by unbracketed
+// slash (/) characters into a sequence of regular expressions, and each
+// part of a test's identifier must match the corresponding element in
// the sequence, if any. Note that possible parents of matches are
// run too, so that -run=X/Y matches and runs and reports the result
// of all tests matching X, even those without sub-tests matching Y,
@@ -2689,11 +2918,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.
@@ -2789,7 +3018,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,
@@ -2843,6 +3076,10 @@
//
// func BenchmarkXxx(b *testing.B) { ... }
//
+// A fuzz test is one named FuzzXxx and should have the signature,
+//
+// func FuzzXxx(f *testing.F) { ... }
+//
// An example function is similar to a test function but, instead of using
// *testing.T to report success or failure, prints output to os.Stdout.
// If the last comment in the function starts with "Output:" then the output
@@ -2882,7 +3119,7 @@
//
// The entire test file is presented as the example when it contains a single
// example function, at least one other function, type, variable, or constant
-// declaration, and no test or benchmark functions.
+// declaration, and no tests, benchmarks, or fuzz tests.
//
// See the documentation of the testing package for more information.
//
diff --git a/src/cmd/go/go11.go b/src/cmd/go/go11.go
index a1f2727825ede1f1ead32d3e240311206a186ed5..9faa7cba42e097b094ca17d41c47dbbb5f54ba62 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 6ce276537babd88765937e1f7ec0f3c98cc5ac68..170c882df9e77158b9591370411bc6f728018825 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"
@@ -31,7 +32,6 @@ import (
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/robustio"
- "cmd/go/internal/work"
"cmd/internal/sys"
)
@@ -44,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 {
@@ -198,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
@@ -205,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")
@@ -806,7 +812,9 @@ func TestNewReleaseRebuildsStalePackagesInGOPATH(t *testing.T) {
"src/internal/abi",
"src/internal/bytealg",
"src/internal/cpu",
+ "src/internal/goarch",
"src/internal/goexperiment",
+ "src/internal/goos",
"src/math/bits",
"src/unsafe",
filepath.Join("pkg", runtime.GOOS+"_"+runtime.GOARCH),
@@ -1120,11 +1128,11 @@ func TestGoListTest(t *testing.T) {
tg.grepStdoutNot(`^testing \[sort.test\]$`, "unexpected test copy of testing")
tg.grepStdoutNot(`^testing$`, "unexpected real copy of testing")
- tg.run("list", "-test", "cmd/dist", "cmd/doc")
- tg.grepStdout(`^cmd/dist$`, "missing cmd/dist")
+ tg.run("list", "-test", "cmd/buildid", "cmd/doc")
+ tg.grepStdout(`^cmd/buildid$`, "missing cmd/buildid")
tg.grepStdout(`^cmd/doc$`, "missing cmd/doc")
tg.grepStdout(`^cmd/doc\.test$`, "missing cmd/doc test")
- tg.grepStdoutNot(`^cmd/dist\.test$`, "unexpected cmd/dist test")
+ tg.grepStdoutNot(`^cmd/buildid\.test$`, "unexpected cmd/buildid test")
tg.grepStdoutNot(`^testing`, "unexpected testing")
tg.run("list", "-test", "runtime/cgo")
@@ -1376,10 +1384,10 @@ func TestLdFlagsLongArgumentsIssue42295(t *testing.T) {
}`)
testStr := "test test test test test \n\\ "
var buf bytes.Buffer
- for buf.Len() < work.ArgLengthForResponseFile+1 {
+ 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")
}
@@ -2274,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)
@@ -2297,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")
}
@@ -2329,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)
@@ -2358,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)
@@ -2388,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 7d5ff9bbb748391dac7cc2f2be0c7fc032977bac..bab94944014afa330431c328ca22edce9d80ba31 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 954ce47a9899325014b4e99b5b6a1875a4120f30..c2d4e6b258887118b8d0822f6a3c246951b7bb77 100644
--- a/src/cmd/go/internal/base/base.go
+++ b/src/cmd/go/internal/base/base.go
@@ -117,12 +117,12 @@ func Exit() {
os.Exit(exitStatus)
}
-func Fatalf(format string, args ...interface{}) {
+func Fatalf(format string, args ...any) {
Errorf(format, args...)
Exit()
}
-func Errorf(format string, args ...interface{}) {
+func Errorf(format string, args ...any) {
log.Printf(format, args...)
SetExitStatus(1)
}
@@ -151,7 +151,7 @@ func GetExitStatus() int {
// Run runs the command, with stdout and stderr
// connected to the go command's own stdout and stderr.
// If the command fails, Run reports the error using Errorf.
-func Run(cmdargs ...interface{}) {
+func Run(cmdargs ...any) {
cmdline := str.StringList(cmdargs...)
if cfg.BuildN || cfg.BuildX {
fmt.Printf("%s\n", strings.Join(cmdline, " "))
diff --git a/src/cmd/go/internal/base/flag.go b/src/cmd/go/internal/base/flag.go
index 677f8196827f4c8fa18e49964ba2abc75a5861fa..2c72c7e562b7333be43813553321469a555b44a0 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/go/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{}
}
@@ -62,6 +62,13 @@ func AddModFlag(flags *flag.FlagSet) {
flags.Var(explicitStringFlag{value: &cfg.BuildMod, explicit: &cfg.BuildModExplicit}, "mod", "")
}
+// AddWorkfileFlag adds the workfile flag to the flag set. It enables workspace
+// mode for commands that support it by resetting the cfg.WorkFile variable
+// to "" (equivalent to auto) rather than off.
+func AddWorkfileFlag(flags *flag.FlagSet) {
+ flags.Var(explicitStringFlag{value: &cfg.WorkFile, explicit: &cfg.WorkFileExplicit}, "workfile", "")
+}
+
// AddModCommonFlags adds the module-related flags common to build commands
// and 'go mod' subcommands.
func AddModCommonFlags(flags *flag.FlagSet) {
diff --git a/src/cmd/go/internal/base/signal_notunix.go b/src/cmd/go/internal/base/signal_notunix.go
index 5cc0b0f1011e5a1a0c2ff3a0eb256960d90deb7c..682705f9b2cb41e3a6fc81fd74536a3aa4e42104 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 cdc2658372e429930170b5a25b6a8ee7948e0c9f..657ff38584104201de727e67d74f50ce798df8b1 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/base/tool.go b/src/cmd/go/internal/base/tool.go
index d0da65e03ced64a930867913843e534dd5701bd9..f927016965051e7d2d8f46c4d2af9b5123c92fe7 100644
--- a/src/cmd/go/internal/base/tool.go
+++ b/src/cmd/go/internal/base/tool.go
@@ -36,7 +36,7 @@ func Tool(toolName string) string {
}
// Give a nice message if there is no tool with that name.
if _, err := os.Stat(toolPath); err != nil {
- fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
+ fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName)
SetExitStatus(2)
Exit()
}
diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go
index 307527c695cbededab0ae1dfa4447e2fc3161c4b..702dc2a14acdaad63f13bcd802541dd3141f3362 100644
--- a/src/cmd/go/internal/bug/bug.go
+++ b/src/cmd/go/internal/bug/bug.go
@@ -40,7 +40,7 @@ func init() {
func runBug(ctx context.Context, cmd *base.Command, args []string) {
if len(args) > 0 {
- base.Fatalf("go bug: bug takes no arguments")
+ base.Fatalf("go: bug takes no arguments")
}
var buf bytes.Buffer
buf.WriteString(bugHeader)
@@ -106,8 +106,9 @@ func printGoEnv(w io.Writer) {
}
func printGoDetails(w io.Writer) {
- printCmdOut(w, "GOROOT/bin/go version: ", filepath.Join(runtime.GOROOT(), "bin/go"), "version")
- printCmdOut(w, "GOROOT/bin/go tool compile -V: ", filepath.Join(runtime.GOROOT(), "bin/go"), "tool", "compile", "-V")
+ gocmd := filepath.Join(runtime.GOROOT(), "bin/go")
+ printCmdOut(w, "GOROOT/bin/go version: ", gocmd, "version")
+ printCmdOut(w, "GOROOT/bin/go tool compile -V: ", gocmd, "tool", "compile", "-V")
}
func printOSDetails(w io.Writer) {
diff --git a/src/cmd/go/internal/cache/cache.go b/src/cmd/go/internal/cache/cache.go
index d592d7049786ce9d3fd5d15a67f2884327f149a0..93d7c25658fa07eef33d282ff3e61e90cf63d616 100644
--- a/src/cmd/go/internal/cache/cache.go
+++ b/src/cmd/go/internal/cache/cache.go
@@ -533,3 +533,15 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error {
return nil
}
+
+// FuzzDir returns a subdirectory within the cache for storing fuzzing data.
+// The subdirectory may not exist.
+//
+// This directory is managed by the internal/fuzz package. Files in this
+// directory aren't removed by the 'go clean -cache' command or by Trim.
+// They may be removed with 'go clean -fuzzcache'.
+//
+// TODO(#48526): make Trim remove unused files from this directory.
+func (c *Cache) FuzzDir() string {
+ return filepath.Join(c.dir, "fuzz")
+}
diff --git a/src/cmd/go/internal/cache/default.go b/src/cmd/go/internal/cache/default.go
index 0b1c1e0c203511ca619e8f0a409e088af9c35b0b..426dddfb978624a1843bdc6df66fdd7e96eb2ca6 100644
--- a/src/cmd/go/internal/cache/default.go
+++ b/src/cmd/go/internal/cache/default.go
@@ -30,6 +30,7 @@ var (
// README as a courtesy to explain where it came from.
const cacheREADME = `This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
+Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.
`
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index 57a3c1ff6fbdc1f9f29a10a4580e8d40e4f8c908..5b84d8be92117993e32047ae0dc97c3747ad6c3d 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
@@ -47,17 +50,26 @@ var (
BuildWork bool // -work flag
BuildX bool // -x flag
- ModCacheRW bool // -modcacherw flag
- ModFile string // -modfile flag
+ ModCacheRW bool // -modcacherw flag
+ ModFile string // -modfile flag
+ WorkFile string // -workfile flag
+ WorkFileExplicit bool // whether -workfile was set explicitly
CmdName string // "build", "install", "list", "mod tidy", etc.
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
DebugTrace string // -debug-trace flag
+
+ // GoPathError is set when GOPATH is not set. it contains an
+ // explanation why GOPATH is unset.
+ GoPathError string
+
+ GOEXPERIMENT = envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT)
)
func defaultContext() build.Context {
ctxt := build.Default
+
ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
ctxt.GOROOT = findGOROOT()
@@ -70,7 +82,7 @@ func defaultContext() build.Context {
build.ToolDir = filepath.Join(ctxt.GOROOT, "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
}
- ctxt.GOPATH = envOr("GOPATH", ctxt.GOPATH)
+ ctxt.GOPATH = envOr("GOPATH", gopath(ctxt))
// Override defaults computed in go/build with defaults
// from go environment configuration file, if known.
@@ -79,7 +91,7 @@ func defaultContext() build.Context {
// The experiments flags are based on GOARCH, so they may
// need to change. TODO: This should be cleaned up.
- buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, envOr("GOEXPERIMENT", buildcfg.DefaultGOEXPERIMENT))
+ buildcfg.UpdateExperiments(ctxt.GOOS, ctxt.GOARCH, GOEXPERIMENT)
ctxt.ToolTags = nil
for _, exp := range buildcfg.EnabledExperiments() {
ctxt.ToolTags = append(ctxt.ToolTags, "goexperiment."+exp)
@@ -261,6 +273,7 @@ var (
// Used in envcmd.MkEnv and build ID computations.
GOARM = envOr("GOARM", fmt.Sprint(buildcfg.GOARM))
GO386 = envOr("GO386", buildcfg.GO386)
+ GOAMD64 = envOr("GOAMD64", fmt.Sprintf("%s%d", "v", buildcfg.GOAMD64))
GOMIPS = envOr("GOMIPS", buildcfg.GOMIPS)
GOMIPS64 = envOr("GOMIPS64", buildcfg.GOMIPS64)
GOPPC64 = envOr("GOPPC64", fmt.Sprintf("%s%d", "power", buildcfg.GOPPC64))
@@ -287,6 +300,8 @@ func GetArchEnv() (key, val string) {
return "GOARM", GOARM
case "386":
return "GO386", GO386
+ case "amd64":
+ return "GOAMD64", GOAMD64
case "mips", "mipsle":
return "GOMIPS", GOMIPS
case "mips64", "mips64le":
@@ -396,3 +411,24 @@ func gopathDir(rel string) string {
}
return filepath.Join(list[0], rel)
}
+
+func gopath(ctxt build.Context) string {
+ if len(ctxt.GOPATH) > 0 {
+ return ctxt.GOPATH
+ }
+ env := "HOME"
+ if runtime.GOOS == "windows" {
+ env = "USERPROFILE"
+ } else if runtime.GOOS == "plan9" {
+ env = "home"
+ }
+ if home := os.Getenv(env); home != "" {
+ def := filepath.Join(home, "go")
+ if filepath.Clean(def) == filepath.Clean(runtime.GOROOT()) {
+ GoPathError = "cannot set GOROOT as GOPATH"
+ }
+ return ""
+ }
+ GoPathError = fmt.Sprintf("%s is not set", env)
+ return ""
+}
diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go
index fd4cb205591105d0ad527e25e811cd62dfadf5a8..dc93cdf5983126c42adeddb2504276a8a41f834f 100644
--- a/src/cmd/go/internal/clean/clean.go
+++ b/src/cmd/go/internal/clean/clean.go
@@ -75,6 +75,13 @@ The -modcache flag causes clean to remove the entire module
download cache, including unpacked source code of versioned
dependencies.
+The -fuzzcache flag causes clean to remove files stored in the Go build
+cache for fuzz testing. The fuzzing engine caches files that expand
+code coverage, so removing them may make fuzzing less effective until
+new inputs are found that provide the same coverage. These files are
+distinct from those stored in testdata directory; clean does not remove
+those files.
+
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@@ -85,6 +92,7 @@ var (
cleanI bool // clean -i flag
cleanR bool // clean -r flag
cleanCache bool // clean -cache flag
+ cleanFuzzcache bool // clean -fuzzcache flag
cleanModcache bool // clean -modcache flag
cleanTestcache bool // clean -testcache flag
)
@@ -96,6 +104,7 @@ func init() {
CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
+ CmdClean.Flag.BoolVar(&cleanFuzzcache, "fuzzcache", false, "")
CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
@@ -112,7 +121,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
// or no other target (such as a cache) was requested to be cleaned.
cleanPkg := len(args) > 0 || cleanI || cleanR
if (!modload.Enabled() || modload.HasModRoot()) &&
- !cleanCache && !cleanModcache && !cleanTestcache {
+ !cleanCache && !cleanModcache && !cleanTestcache && !cleanFuzzcache {
cleanPkg = true
}
@@ -144,7 +153,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
// This also mimics what os.RemoveAll(dir) would do.
if err := os.RemoveAll(d); err != nil && !printedErrors {
printedErrors = true
- base.Errorf("go clean -cache: %v", err)
+ base.Errorf("go: %v", err)
}
}
}
@@ -157,7 +166,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
if !cfg.BuildN {
if err := os.RemoveAll(logFile); err != nil && !printedErrors {
printedErrors = true
- base.Errorf("go clean -cache: %v", err)
+ base.Errorf("go: %v", err)
}
}
}
@@ -187,7 +196,7 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
}
if err != nil {
if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
- base.Errorf("go clean -testcache: %v", err)
+ base.Errorf("go: %v", err)
}
}
}
@@ -195,14 +204,26 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
if cleanModcache {
if cfg.GOMODCACHE == "" {
- base.Fatalf("go clean -modcache: no module cache")
+ base.Fatalf("go: cannot clean -modcache without a module cache")
}
if cfg.BuildN || cfg.BuildX {
b.Showcmd("", "rm -rf %s", cfg.GOMODCACHE)
}
if !cfg.BuildN {
if err := modfetch.RemoveAll(cfg.GOMODCACHE); err != nil {
- base.Errorf("go clean -modcache: %v", err)
+ base.Errorf("go: %v", err)
+ }
+ }
+ }
+
+ if cleanFuzzcache {
+ fuzzDir := cache.Default().FuzzDir()
+ if cfg.BuildN || cfg.BuildX {
+ b.Showcmd("", "rm -rf %s", fuzzDir)
+ }
+ if !cfg.BuildN {
+ if err := os.RemoveAll(fuzzDir); err != nil {
+ base.Errorf("go: %v", err)
}
}
}
@@ -245,7 +266,7 @@ func clean(p *load.Package) {
}
dirs, err := os.ReadDir(p.Dir)
if err != nil {
- base.Errorf("go clean %s: %v", p.Dir, err)
+ base.Errorf("go: %s: %v", p.Dir, err)
return
}
@@ -334,7 +355,7 @@ func clean(p *load.Package) {
}
}
if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil {
- base.Errorf("go clean: %v", err)
+ base.Errorf("go: %v", err)
}
}
continue
@@ -386,5 +407,5 @@ func removeFile(f string) {
return
}
}
- base.Errorf("go clean: %v", err)
+ base.Errorf("go: %v", err)
}
diff --git a/src/cmd/go/internal/cmdflag/flag.go b/src/cmd/go/internal/cmdflag/flag.go
index 8abb7e559f5ab5564f48a972bd084d2eb023a70f..a634bc1ab8d7ea1a256b386c2c6720009613284c 100644
--- a/src/cmd/go/internal/cmdflag/flag.go
+++ b/src/cmd/go/internal/cmdflag/flag.go
@@ -92,7 +92,7 @@ func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []st
// Use fs.Set instead of f.Value.Set below so that any subsequent call to
// fs.Visit will correctly visit the flags that have been set.
- failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) {
+ failf := func(format string, a ...any) (*flag.Flag, []string, error) {
return f, args, fmt.Errorf(format, a...)
}
diff --git a/src/cmd/go/internal/doc/doc.go b/src/cmd/go/internal/doc/doc.go
index 8580a5dc4d2482a7e2f88640abe9a76a245adb18..7741a9022c910a0bee6c4084e55503bc6aea9c09 100644
--- a/src/cmd/go/internal/doc/doc.go
+++ b/src/cmd/go/internal/doc/doc.go
@@ -60,9 +60,8 @@ The package path must be either a qualified path or a proper suffix of a
path. The go tool's usual package mechanism does not apply: package path
elements like . and ... are not implemented by go doc.
-When run with two arguments, the first must be a full package path (not just a
-suffix), and the second is a symbol, or symbol with method or struct field.
-This is similar to the syntax accepted by godoc:
+When run with two arguments, the first is a package path (full path or suffix),
+and the second is a symbol, or symbol with method or struct field:
go doc [.]
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index 1553d263914541f05a639e03b8041969ba244301..e56dd8223f0519e90e9c89cd8e576d5f227becc5 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -26,6 +26,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
+ "cmd/internal/quoted"
)
var CmdEnv = &base.Command{
@@ -104,13 +105,13 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: key, Value: val})
}
- cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
- if env := strings.Fields(cfg.Getenv("CC")); len(env) > 0 {
- cc = env[0]
+ cc := cfg.Getenv("CC")
+ if cc == "" {
+ cc = cfg.DefaultCC(cfg.Goos, cfg.Goarch)
}
- cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
- if env := strings.Fields(cfg.Getenv("CXX")); len(env) > 0 {
- cxx = env[0]
+ cxx := cfg.Getenv("CXX")
+ if cxx == "" {
+ cxx = cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
}
env = append(env, cfg.EnvVar{Name: "AR", Value: envOr("AR", "ar")})
env = append(env, cfg.EnvVar{Name: "CC", Value: cc})
@@ -145,13 +146,17 @@ func findEnv(env []cfg.EnvVar, name string) string {
// ExtraEnvVars returns environment variables that should not leak into child processes.
func ExtraEnvVars() []cfg.EnvVar {
gomod := ""
+ modload.Init()
if modload.HasModRoot() {
- gomod = filepath.Join(modload.ModRoot(), "go.mod")
+ gomod = modload.ModFilePath()
} else if modload.Enabled() {
gomod = os.DevNull
}
+ modload.InitWorkfile()
+ gowork := modload.WorkFilePath()
return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod},
+ {Name: "GOWORK", Value: gowork},
}
}
@@ -191,13 +196,13 @@ func argKey(arg string) string {
func runEnv(ctx context.Context, cmd *base.Command, args []string) {
if *envJson && *envU {
- base.Fatalf("go env: cannot use -json with -u")
+ base.Fatalf("go: cannot use -json with -u")
}
if *envJson && *envW {
- base.Fatalf("go env: cannot use -json with -w")
+ base.Fatalf("go: cannot use -json with -w")
}
if *envU && *envW {
- base.Fatalf("go env: cannot use -u with -w")
+ base.Fatalf("go: cannot use -u with -w")
}
// Handle 'go env -w' and 'go env -u' before calling buildcfg.Check,
@@ -275,7 +280,7 @@ func runEnv(ctx context.Context, cmd *base.Command, args []string) {
func runEnvW(args []string) {
// Process and sanity-check command line.
if len(args) == 0 {
- base.Fatalf("go env -w: no KEY=VALUE arguments given")
+ base.Fatalf("go: no KEY=VALUE arguments given")
}
osEnv := make(map[string]string)
for _, e := range cfg.OrigEnv {
@@ -287,14 +292,14 @@ func runEnvW(args []string) {
for _, arg := range args {
i := strings.Index(arg, "=")
if i < 0 {
- base.Fatalf("go env -w: arguments must be KEY=VALUE: invalid argument: %s", arg)
+ base.Fatalf("go: arguments must be KEY=VALUE: invalid argument: %s", arg)
}
key, val := arg[:i], arg[i+1:]
if err := checkEnvWrite(key, val); err != nil {
- base.Fatalf("go env -w: %v", err)
+ base.Fatalf("go: %v", err)
}
if _, ok := add[key]; ok {
- base.Fatalf("go env -w: multiple values for key: %s", key)
+ base.Fatalf("go: multiple values for key: %s", key)
}
add[key] = val
if osVal := osEnv[key]; osVal != "" && osVal != val {
@@ -303,13 +308,13 @@ func runEnvW(args []string) {
}
if err := checkBuildConfig(add, nil); err != nil {
- base.Fatalf("go env -w: %v", err)
+ base.Fatalf("go: %v", err)
}
gotmp, okGOTMP := add["GOTMPDIR"]
if okGOTMP {
if !filepath.IsAbs(gotmp) && gotmp != "" {
- base.Fatalf("go env -w: GOTMPDIR must be an absolute path")
+ base.Fatalf("go: GOTMPDIR must be an absolute path")
}
}
@@ -319,18 +324,18 @@ func runEnvW(args []string) {
func runEnvU(args []string) {
// Process and sanity-check command line.
if len(args) == 0 {
- base.Fatalf("go env -u: no arguments given")
+ base.Fatalf("go: 'go env -u' requires an argument")
}
del := make(map[string]bool)
for _, arg := range args {
if err := checkEnvWrite(arg, ""); err != nil {
- base.Fatalf("go env -u: %v", err)
+ base.Fatalf("go: %v", err)
}
del[arg] = true
}
if err := checkBuildConfig(nil, del); err != nil {
- base.Fatalf("go env -u: %v", err)
+ base.Fatalf("go: %v", err)
}
updateEnvFile(nil, del)
@@ -414,7 +419,7 @@ func printEnvAsJSON(env []cfg.EnvVar) {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t")
if err := enc.Encode(m); err != nil {
- base.Fatalf("go env -json: %s", err)
+ base.Fatalf("go: %s", err)
}
}
@@ -429,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)
@@ -457,10 +462,23 @@ func checkEnvWrite(key, val string) error {
if !filepath.IsAbs(val) && val != "" {
return fmt.Errorf("GOPATH entry is relative; must be absolute path: %q", val)
}
- // Make sure CC and CXX are absolute paths
- case "CC", "CXX", "GOMODCACHE":
- if !filepath.IsAbs(val) && val != "" && val != filepath.Base(val) {
- return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, val)
+ case "GOMODCACHE":
+ if !filepath.IsAbs(val) && val != "" {
+ return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q", val)
+ }
+ case "CC", "CXX":
+ if val == "" {
+ break
+ }
+ args, err := quoted.Split(val)
+ if err != nil {
+ return fmt.Errorf("invalid %s: %v", key, err)
+ }
+ if len(args) == 0 {
+ return fmt.Errorf("%s entry cannot contain only space", key)
+ }
+ if !filepath.IsAbs(args[0]) && args[0] != filepath.Base(args[0]) {
+ return fmt.Errorf("%s entry is relative; must be absolute path: %q", key, args[0])
}
}
@@ -479,11 +497,11 @@ func checkEnvWrite(key, val string) error {
func updateEnvFile(add map[string]string, del map[string]bool) {
file, err := cfg.EnvFile()
if file == "" {
- base.Fatalf("go env: cannot find go env config: %v", err)
+ base.Fatalf("go: cannot find go env config: %v", err)
}
data, err := os.ReadFile(file)
if err != nil && (!os.IsNotExist(err) || len(add) == 0) {
- base.Fatalf("go env: reading go env config: %v", err)
+ base.Fatalf("go: reading go env config: %v", err)
}
lines := strings.SplitAfter(string(data), "\n")
@@ -541,7 +559,7 @@ func updateEnvFile(add map[string]string, del map[string]bool) {
os.MkdirAll(filepath.Dir(file), 0777)
err = os.WriteFile(file, data, 0666)
if err != nil {
- base.Fatalf("go env: writing go env config: %v", err)
+ base.Fatalf("go: writing go env config: %v", err)
}
}
}
diff --git a/src/cmd/go/internal/fix/fix.go b/src/cmd/go/internal/fix/fix.go
index 988d45e71ccfe2ccf571e409962bdd3ade4e3430..d8ba353de6595091f10b693bcb3aed1fde9d2e0d 100644
--- a/src/cmd/go/internal/fix/fix.go
+++ b/src/cmd/go/internal/fix/fix.go
@@ -11,27 +11,39 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modload"
"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/fmtcmd/fmt.go b/src/cmd/go/internal/fmtcmd/fmt.go
index 8a040087539e8f9195c61a88d89883a379a963b2..19656eab7fc661c4cea1fabf55630f38ab2b3c98 100644
--- a/src/cmd/go/internal/fmtcmd/fmt.go
+++ b/src/cmd/go/internal/fmtcmd/fmt.go
@@ -11,14 +11,12 @@ import (
"fmt"
"os"
"path/filepath"
- "runtime"
- "sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
- "cmd/go/internal/str"
+ "cmd/internal/sys"
)
func init() {
@@ -53,18 +51,13 @@ See also: go fix, go vet.
func runFmt(ctx context.Context, cmd *base.Command, args []string) {
printed := false
gofmt := gofmtPath()
- procs := runtime.GOMAXPROCS(0)
- var wg sync.WaitGroup
- wg.Add(procs)
- fileC := make(chan string, 2*procs)
- for i := 0; i < procs; i++ {
- go func() {
- defer wg.Done()
- for file := range fileC {
- base.Run(str.StringList(gofmt, "-l", "-w", file))
- }
- }()
- }
+
+ gofmtArgs := []string{gofmt, "-l", "-w"}
+ gofmtArgLen := len(gofmt) + len(" -l -w")
+
+ baseGofmtArgs := len(gofmtArgs)
+ baseGofmtArgLen := gofmtArgLen
+
for _, pkg := range load.PackagesAndErrors(ctx, load.PackageOpts{}, args) {
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
if !printed {
@@ -89,11 +82,18 @@ func runFmt(ctx context.Context, cmd *base.Command, args []string) {
// not to packages in subdirectories.
files := base.RelPaths(pkg.InternalAllGoFiles())
for _, file := range files {
- fileC <- file
+ gofmtArgs = append(gofmtArgs, file)
+ gofmtArgLen += 1 + len(file) // plus separator
+ if gofmtArgLen >= sys.ExecArgLengthLimit {
+ base.Run(gofmtArgs)
+ gofmtArgs = gofmtArgs[:baseGofmtArgs]
+ gofmtArgLen = baseGofmtArgLen
+ }
}
}
- close(fileC)
- wg.Wait()
+ if len(gofmtArgs) > baseGofmtArgs {
+ base.Run(gofmtArgs)
+ }
}
func gofmtPath() string {
diff --git a/src/cmd/go/internal/fsys/fsys.go b/src/cmd/go/internal/fsys/fsys.go
index 0b806027e6469a3cac3b19e2e5e162b7ead8e92d..9a1bbf890e1191ebb68b19673f7ea647bae9a899 100644
--- a/src/cmd/go/internal/fsys/fsys.go
+++ b/src/cmd/go/internal/fsys/fsys.go
@@ -499,7 +499,7 @@ func (f fakeFile) Size() int64 { return f.real.Size() }
func (f fakeFile) Mode() fs.FileMode { return f.real.Mode() }
func (f fakeFile) ModTime() time.Time { return f.real.ModTime() }
func (f fakeFile) IsDir() bool { return f.real.IsDir() }
-func (f fakeFile) Sys() interface{} { return f.real.Sys() }
+func (f fakeFile) Sys() any { return f.real.Sys() }
// missingFile provides an fs.FileInfo for an overlaid file where the
// destination file in the overlay doesn't exist. It returns zero values
@@ -512,7 +512,7 @@ func (f missingFile) Size() int64 { return 0 }
func (f missingFile) Mode() fs.FileMode { return fs.ModeIrregular }
func (f missingFile) ModTime() time.Time { return time.Unix(0, 0) }
func (f missingFile) IsDir() bool { return false }
-func (f missingFile) Sys() interface{} { return nil }
+func (f missingFile) Sys() any { return nil }
// fakeDir provides an fs.FileInfo implementation for directories that are
// implicitly created by overlaid files. Each directory in the
@@ -524,7 +524,7 @@ func (f fakeDir) Size() int64 { return 0 }
func (f fakeDir) Mode() fs.FileMode { return fs.ModeDir | 0500 }
func (f fakeDir) ModTime() time.Time { return time.Unix(0, 0) }
func (f fakeDir) IsDir() bool { return true }
-func (f fakeDir) Sys() interface{} { return nil }
+func (f fakeDir) Sys() any { return nil }
// Glob is like filepath.Glob but uses the overlay file system.
func Glob(pattern string) (matches []string, err error) {
diff --git a/src/cmd/go/internal/fsys/fsys_test.go b/src/cmd/go/internal/fsys/fsys_test.go
index 7f175c7031169dded7b720e9899f7d38909f8d74..c080c14987c37cad5928771d49bb0a4b1b602953 100644
--- a/src/cmd/go/internal/fsys/fsys_test.go
+++ b/src/cmd/go/internal/fsys/fsys_test.go
@@ -1,7 +1,6 @@
package fsys
import (
- "cmd/go/internal/txtar"
"encoding/json"
"errors"
"fmt"
@@ -12,6 +11,8 @@ import (
"path/filepath"
"reflect"
"testing"
+
+ "golang.org/x/tools/txtar"
)
// initOverlay resets the overlay state to reflect the config.
diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go
index 80ea32b4284011712eb82b2bd671e4f5f9a35b2e..54ccfe78f24b66d526bc4b386b8ebc282603a140 100644
--- a/src/cmd/go/internal/generate/generate.go
+++ b/src/cmd/go/internal/generate/generate.go
@@ -38,7 +38,7 @@ Generate runs commands described by directives within existing
files. Those commands can run any process but the intent is to
create or update Go source files.
-Go generate is never run automatically by go build, go get, go test,
+Go generate is never run automatically by go build, go test,
and so on. It must be run explicitly.
Go generate scans the file for directives, which are lines of
@@ -408,7 +408,7 @@ var stop = fmt.Errorf("error in generation")
// errorf logs an error message prefixed with the file and line number.
// It then exits the program (with exit status 1) because generation stops
// at the first error.
-func (g *Generator) errorf(format string, args ...interface{}) {
+func (g *Generator) errorf(format string, args ...any) {
fmt.Fprintf(os.Stderr, "%s:%d: %s\n", base.ShortPath(g.path), g.lineNum,
fmt.Sprintf(format, args...))
panic(stop)
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 3c16dc3040facba13a7a59416268ae7944250599..8cf8fe6645f369d47d84de3321e195e0b727cf22 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -114,16 +114,16 @@ func init() {
func runGet(ctx context.Context, cmd *base.Command, args []string) {
if cfg.ModulesEnabled {
// Should not happen: main.go should install the separate module-enabled get code.
- base.Fatalf("go get: modules not implemented")
+ base.Fatalf("go: modules not implemented")
}
work.BuildInit()
if *getF && !*getU {
- base.Fatalf("go get: cannot use -f flag without -u")
+ base.Fatalf("go: cannot use -f flag without -u")
}
if *getInsecure {
- base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
+ base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")
}
// Disable any prompting for passwords by Git itself.
@@ -214,18 +214,19 @@ func downloadPaths(patterns []string) []string {
// if the argument has no slash or refers to an existing file.
if strings.HasSuffix(arg, ".go") {
if !strings.Contains(arg, "/") {
- base.Errorf("go get %s: arguments must be package or module paths", arg)
+ base.Errorf("go: %s: arguments must be package or module paths", arg)
continue
}
if fi, err := os.Stat(arg); err == nil && !fi.IsDir() {
- base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", arg)
+ base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", arg)
}
}
}
base.ExitIfErrors()
var pkgs []string
- for _, m := range search.ImportPathsQuiet(patterns) {
+ noModRoots := []string{}
+ for _, m := range search.ImportPathsQuiet(patterns, noModRoots) {
if len(m.Pkgs) == 0 && strings.Contains(m.Pattern(), "...") {
pkgs = append(pkgs, m.Pattern())
} else {
@@ -315,7 +316,8 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
if wildcardOkay && strings.Contains(arg, "...") {
match := search.NewMatch(arg)
if match.IsLocal() {
- match.MatchDirs()
+ noModRoots := []string{} // We're in gopath mode, so there are no modroots.
+ match.MatchDirs(noModRoots)
args = match.Dirs
} else {
match.MatchPackages()
@@ -415,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
@@ -444,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/help/help.go b/src/cmd/go/internal/help/help.go
index 7a730fc8eb8c5e5255c6121b8b26d81ba9b6ba99..2a07d2423bdd9e46eb397228214cde2b13226695 100644
--- a/src/cmd/go/internal/help/help.go
+++ b/src/cmd/go/internal/help/help.go
@@ -162,7 +162,7 @@ func (w *errWriter) Write(b []byte) (int, error) {
}
// tmpl executes the given template text on data, writing the result to w.
-func tmpl(w io.Writer, text string, data interface{}) {
+func tmpl(w io.Writer, text string, data any) {
t := template.New("top")
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
template.Must(t.Parse(text))
diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go
index 490ff1fb7cf05bd5a7e107c1ea40bb44174cc0db..035235fe1b5a1d39a3e8228daa20362fee8af86c 100644
--- a/src/cmd/go/internal/help/helpdoc.go
+++ b/src/cmd/go/internal/help/helpdoc.go
@@ -592,6 +592,10 @@ Architecture-specific environment variables:
GO386
For GOARCH=386, how to implement floating point instructions.
Valid values are sse2 (default), softfloat.
+ GOAMD64
+ For GOARCH=amd64, the microarchitecture level for which to compile.
+ Valid values are v1 (default), v2, v3, v4.
+ See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels.
GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
@@ -771,6 +775,13 @@ The go command also caches successful package test results.
See 'go help test' for details. Running 'go clean -testcache' removes
all cached test results (but not cached build results).
+The go command also caches values used in fuzzing with 'go test -fuzz',
+specifically, values that expanded code coverage when passed to a
+fuzz function. These values are not used for regular building and
+testing, but they're stored in a subdirectory of the build cache.
+Running 'go clean -fuzzcache' removes all cached fuzzing values.
+This may make fuzzing less effective, temporarily.
+
The GODEBUG environment variable can enable printing of debugging
information about the state of the cache:
diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go
index 50aeabc578c93094ce776e99d25d0612e7397bbc..ff6bea6777dce89c81385f1e38182e43de12f821 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,10 +68,61 @@ 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.
+ // Also identify any //go:build comments.
+ content, goBuild, _, err := parseFileHeader(content)
+ if err != nil {
+ return false
+ }
+
+ // 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
+ }
+ 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 shouldBuild
+}
+
+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 {
@@ -46,78 +131,61 @@ func ShouldBuild(content []byte, tags map[string]bool) bool {
p = p[len(p):]
}
line = bytes.TrimSpace(line)
- if len(line) == 0 { // Blank 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
+ continue Lines
}
- if !bytes.HasPrefix(line, slashslash) { // Not comment line
- break
+ if !bytes.HasPrefix(line, bSlashSlash) { // Not comment line
+ ended = true
}
- }
- 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):]
- }
- line = bytes.TrimSpace(line)
- if !bytes.HasPrefix(line, slashslash) {
- continue
+ if !inSlashStar && isGoBuildComment(line) {
+ if goBuild != nil {
+ return nil, nil, false, errMultipleGoBuild
+ }
+ goBuild = line
}
- 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
+
+ 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
}
}
- return allok
-}
-
-// 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
- }
- 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/imports/scan_test.go b/src/cmd/go/internal/imports/scan_test.go
index 2d245ee7872e73c706b15dcf6b2e1f9559248412..7e69c56513ac01a2acf0d5cd5d4372597abd96f0 100644
--- a/src/cmd/go/internal/imports/scan_test.go
+++ b/src/cmd/go/internal/imports/scan_test.go
@@ -33,7 +33,7 @@ func TestScan(t *testing.T) {
}
if p == "net/http" {
// A test import but not an import
- t.Errorf("json reported as importing encoding/binary but does not")
+ t.Errorf("json reported as importing net/http but does not")
}
}
if !foundBase64 {
diff --git a/src/cmd/go/internal/imports/testdata/android/e.go b/src/cmd/go/internal/imports/testdata/android/e.go
index d9b2db769b5f0c7c529cfeb9e437689dab8821f7..f1b9c888c2cafdd83ab651a52f75b31bb1f83516 100644
--- a/src/cmd/go/internal/imports/testdata/android/e.go
+++ b/src/cmd/go/internal/imports/testdata/android/e.go
@@ -1,3 +1,4 @@
+//go:build android
// +build android
package android
diff --git a/src/cmd/go/internal/imports/testdata/android/f.go b/src/cmd/go/internal/imports/testdata/android/f.go
index 281e4dd6b9898ed971ba7b8d19d20d2004c92ae2..bb0ff7b73f67c15fffeb5ea95f0628047a794cdf 100644
--- a/src/cmd/go/internal/imports/testdata/android/f.go
+++ b/src/cmd/go/internal/imports/testdata/android/f.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package android
diff --git a/src/cmd/go/internal/imports/testdata/android/g.go b/src/cmd/go/internal/imports/testdata/android/g.go
index 66a789c0ada4b58abccdfac60c056417847cb058..ee19424890a963fa1618310d2875de3438df2a0e 100644
--- a/src/cmd/go/internal/imports/testdata/android/g.go
+++ b/src/cmd/go/internal/imports/testdata/android/g.go
@@ -1,3 +1,4 @@
+//go:build !android
// +build !android
package android
diff --git a/src/cmd/go/internal/imports/testdata/illumos/e.go b/src/cmd/go/internal/imports/testdata/illumos/e.go
index 5e1ed3cb9decae06303aa7ecbdc8f8410baa2bff..fddf2c429909b7776cd4cdddce2ed70f0cd35542 100644
--- a/src/cmd/go/internal/imports/testdata/illumos/e.go
+++ b/src/cmd/go/internal/imports/testdata/illumos/e.go
@@ -1,3 +1,4 @@
+//go:build illumos
// +build illumos
package illumos
diff --git a/src/cmd/go/internal/imports/testdata/illumos/f.go b/src/cmd/go/internal/imports/testdata/illumos/f.go
index f3e3f728bce5d837453f3f88ed6c2de34c6c49d7..4b6d528e4c2225f29ed67e382a3561586b4f2170 100644
--- a/src/cmd/go/internal/imports/testdata/illumos/f.go
+++ b/src/cmd/go/internal/imports/testdata/illumos/f.go
@@ -1,3 +1,4 @@
+//go:build solaris
// +build solaris
package illumos
diff --git a/src/cmd/go/internal/imports/testdata/illumos/g.go b/src/cmd/go/internal/imports/testdata/illumos/g.go
index b30f1eb4037322ce7501948f9f8deceddeb605cf..1bf826b81510b42fafb4a11a0e9e92c7ad8d0860 100644
--- a/src/cmd/go/internal/imports/testdata/illumos/g.go
+++ b/src/cmd/go/internal/imports/testdata/illumos/g.go
@@ -1,3 +1,4 @@
+//go:build !illumos
// +build !illumos
package illumos
diff --git a/src/cmd/go/internal/imports/testdata/star/x1.go b/src/cmd/go/internal/imports/testdata/star/x1.go
index 6a9594aed035e6570d51457d8428cd0fc0a0f73f..eaaea979e9dc82285ab0f830cbc676295d0732d1 100644
--- a/src/cmd/go/internal/imports/testdata/star/x1.go
+++ b/src/cmd/go/internal/imports/testdata/star/x1.go
@@ -1,8 +1,5 @@
-// +build blahblh
-// +build linux
-// +build !linux
-// +build windows
-// +build darwin
+//go:build blahblh && linux && !linux && windows && darwin
+// +build blahblh,linux,!linux,windows,darwin
package x
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 7cb9ec6d9492428aa0c9afe111fde89bf35fa536..d9a7078ccf29f7aa16d1a9d10e3bf24d486f4495 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -316,6 +316,7 @@ For more about modules, see https://golang.org/ref/mod.
func init() {
CmdList.Run = runList // break init cycle
work.AddBuildFlags(CmdList, work.DefaultBuildFlags)
+ base.AddWorkfileFlag(&CmdList.Flag)
}
var (
@@ -336,6 +337,8 @@ var (
var nl = []byte{'\n'}
func runList(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
if *listFmt != "" && *listJson == true {
base.Fatalf("go list -f cannot be used with -json")
}
@@ -355,9 +358,9 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
}
- var do func(interface{})
+ var do func(any)
if *listJson {
- do = func(x interface{}) {
+ do = func(x any) {
b, err := json.MarshalIndent(x, "", "\t")
if err != nil {
out.Flush()
@@ -383,7 +386,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("%s", err)
}
- do = func(x interface{}) {
+ do = func(x any) {
if err := tmpl.Execute(out, x); err != nil {
out.Flush()
base.Fatalf("%s", err)
@@ -424,12 +427,12 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
if modload.Init(); !modload.Enabled() {
- base.Fatalf("go list -m: not using modules")
+ base.Fatalf("go: list -m cannot be used with GO111MODULE=off")
}
modload.LoadModFile(ctx) // Sets cfg.BuildMod as a side-effect.
if cfg.BuildMod == "vendor" {
- const actionDisabledFormat = "go list -m: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
+ const actionDisabledFormat = "go: can't %s using the vendor directory\n\t(Use -mod=mod or -mod=readonly to bypass.)"
if *listVersions {
base.Fatalf(actionDisabledFormat, "determine available versions")
@@ -468,11 +471,11 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
if !*listE {
for _, m := range mods {
if m.Error != nil {
- base.Errorf("go list -m: %v", m.Error.Err)
+ base.Errorf("go: %v", m.Error.Err)
}
}
if err != nil {
- base.Errorf("go list -m: %v", err)
+ base.Errorf("go: %v", err)
}
base.ExitIfErrors()
}
@@ -708,7 +711,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
}
rmods, err := modload.ListModules(ctx, args, mode)
if err != nil && !*listE {
- base.Errorf("go list -retracted: %v", err)
+ base.Errorf("go: %v", err)
}
for i, arg := range args {
rmod := rmods[i]
diff --git a/src/cmd/go/internal/load/flag.go b/src/cmd/go/internal/load/flag.go
index 440cb86134489a7e023ce516629fe9bccc00fe0c..de079decdf2541c6f7ac312e002aec761427bfbc 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/go/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 a83cc9a812b674fe863b19c16273f2f6626042f8..a891d601b1d4fdf62faebaa6eb5f6cd3b492ee79 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"
@@ -38,6 +40,7 @@ import (
"cmd/go/internal/search"
"cmd/go/internal/str"
"cmd/go/internal/trace"
+ "cmd/go/internal/vcs"
"cmd/internal/sys"
"golang.org/x/mod/modfile"
@@ -203,6 +206,7 @@ type PackageInternal struct {
Local bool // imported via local path (./ or ../)
LocalPrefix string // interpret ./ and ../ imports relative to this prefix
ExeName string // desired name for temporary executable
+ FuzzInstrument bool // package should be instrumented for fuzzing
CoverMode string // preprocess Go source files with the coverage tool in this mode
CoverVars map[string]*CoverVar // variables created by coverage analysis
OmitDebug bool // tell linker not to write debug information
@@ -494,7 +498,7 @@ type importError struct {
err error // created with fmt.Errorf
}
-func ImportErrorf(path, format string, args ...interface{}) ImportPathError {
+func ImportErrorf(path, format string, args ...any) ImportPathError {
err := &importError{importPath: path, err: fmt.Errorf(format, args...)}
if errStr := err.Error(); !strings.Contains(errStr, path) {
panic(fmt.Sprintf("path %q not in error %q", path, errStr))
@@ -585,10 +589,10 @@ func ClearPackageCachePartial(args []string) {
delete(packageCache, arg)
}
}
- resolvedImportCache.DeleteIf(func(key interface{}) bool {
+ resolvedImportCache.DeleteIf(func(key any) bool {
return shouldDelete[key.(importSpec).path]
})
- packageDataCache.DeleteIf(func(key interface{}) bool {
+ packageDataCache.DeleteIf(func(key any) bool {
return shouldDelete[key.(string)]
})
}
@@ -601,7 +605,7 @@ func ReloadPackageNoFlags(arg string, stk *ImportStack) *Package {
p := packageCache[arg]
if p != nil {
delete(packageCache, arg)
- resolvedImportCache.DeleteIf(func(key interface{}) bool {
+ resolvedImportCache.DeleteIf(func(key any) bool {
return key.(importSpec).path == p.ImportPath
})
packageDataCache.Delete(p.ImportPath)
@@ -813,7 +817,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
parentIsStd: parentIsStd,
mode: mode,
}
- r := resolvedImportCache.Do(importKey, func() interface{} {
+ r := resolvedImportCache.Do(importKey, func() any {
var r resolvedImport
if build.IsLocalImport(path) {
r.dir = filepath.Join(parentDir, path)
@@ -840,7 +844,7 @@ func loadPackageData(ctx context.Context, path, parentPath, parentDir, parentRoo
// Load the package from its directory. If we already found the package's
// directory when resolving its import path, use that.
- data := packageDataCache.Do(r.path, func() interface{} {
+ data := packageDataCache.Do(r.path, func() any {
loaded = true
var data packageData
if r.dir != "" {
@@ -1059,7 +1063,7 @@ func cleanImport(path string) string {
var isDirCache par.Cache
func isDir(path string) bool {
- return isDirCache.Do(path, func() interface{} {
+ return isDirCache.Do(path, func() any {
fi, err := fsys.Stat(path)
return err == nil && fi.IsDir()
}).(bool)
@@ -1187,7 +1191,7 @@ var (
// goModPath returns the module path in the go.mod in dir, if any.
func goModPath(dir string) (path string) {
- return goModPathCache.Do(dir, func() interface{} {
+ return goModPathCache.Do(dir, func() any {
data, err := os.ReadFile(filepath.Join(dir, "go.mod"))
if err != nil {
return ""
@@ -1450,9 +1454,9 @@ func disallowInternal(ctx context.Context, srcDir string, importer *Package, imp
// The importer is a list of command-line files.
// Pretend that the import path is the import path of the
// directory containing them.
- // If the directory is outside the main module, this will resolve to ".",
+ // If the directory is outside the main modules, this will resolve to ".",
// which is not a prefix of any valid module.
- importerPath = modload.DirImportPath(ctx, importer.Dir)
+ importerPath, _ = modload.MainModules.DirImportPath(ctx, importer.Dir)
}
parentOfInternal := p.ImportPath[:i]
if str.HasPathPrefix(importerPath, parentOfInternal) {
@@ -1622,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)
@@ -1677,9 +1682,10 @@ func (p *Package) DefaultExecName() string {
func (p *Package) load(ctx context.Context, opts PackageOpts, path string, stk *ImportStack, importPos []token.Position, bp *build.Package, err error) {
p.copyBuild(opts, bp)
- // The localPrefix is the path we interpret ./ imports relative to.
+ // The localPrefix is the path we interpret ./ imports relative to,
+ // if we support them at all (not in module mode!).
// Synthesized main packages sometimes override this.
- if p.Internal.Local {
+ if p.Internal.Local && !cfg.ModulesEnabled {
p.Internal.LocalPrefix = dirToImportPath(p.Dir)
}
@@ -1919,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.
@@ -2012,13 +2017,18 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st
for _, pattern = range patterns {
pid++
+ glob := pattern
+ all := strings.HasPrefix(pattern, "all:")
+ if all {
+ glob = pattern[len("all:"):]
+ }
// Check pattern is valid for //go:embed.
- if _, err := path.Match(pattern, ""); err != nil || !validEmbedPattern(pattern) {
+ if _, err := path.Match(glob, ""); err != nil || !validEmbedPattern(glob) {
return nil, nil, fmt.Errorf("invalid pattern syntax")
}
// Glob to find matches.
- match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(pattern))
+ match, err := fsys.Glob(pkgdir + string(filepath.Separator) + filepath.FromSlash(glob))
if err != nil {
return nil, nil, err
}
@@ -2081,7 +2091,7 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st
}
rel := filepath.ToSlash(path[len(pkgdir)+1:])
name := info.Name()
- if path != file && (isBadEmbedName(name) || name[0] == '.' || name[0] == '_') {
+ if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) {
// Ignore bad names, assuming they won't go into modules.
// Also avoid hidden files that user may not know about.
// See golang.org/issue/42328.
@@ -2193,6 +2203,229 @@ func (p *Package) collectDeps() {
}
}
+// vcsStatusCache maps repository directories (string)
+// to their VCS information (vcsStatusError).
+var vcsStatusCache par.Cache
+
+// 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 ...any) {
+ 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) {
+ value = strings.ReplaceAll(value, "\n", " ") // make value safe
+ 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.
+ // Please keep the list sorted.
+ if cfg.BuildBuildinfo && !p.Standard {
+ if cfg.BuildASan {
+ appendSetting("-asan", "true")
+ }
+ if BuildAsmflags.present {
+ appendSetting("-asmflags", BuildAsmflags.String())
+ }
+ appendSetting("-compiler", cfg.BuildContext.Compiler)
+ if BuildGccgoflags.present && cfg.BuildContext.Compiler == "gccgo" {
+ appendSetting("-gccgoflags", BuildGccgoflags.String())
+ }
+ if BuildGcflags.present && cfg.BuildContext.Compiler == "gc" {
+ appendSetting("-gcflags", BuildGcflags.String())
+ }
+ if BuildLdflags.present {
+ appendSetting("-ldflags", BuildLdflags.String())
+ }
+ if cfg.BuildMSan {
+ appendSetting("-msan", "true")
+ }
+ if cfg.BuildRace {
+ appendSetting("-race", "true")
+ }
+ if tags := cfg.BuildContext.BuildTags; len(tags) > 0 {
+ appendSetting("-tags", strings.Join(tags, ","))
+ }
+ cgo := "0"
+ if cfg.BuildContext.CgoEnabled {
+ cgo = "1"
+ }
+ appendSetting("CGO_ENABLED", cgo)
+ if cfg.BuildContext.CgoEnabled {
+ for _, name := range []string{"CGO_CFLAGS", "CGO_CPPFLAGS", "CGO_CXXFLAGS", "CGO_LDFLAGS"} {
+ appendSetting(name, cfg.Getenv(name))
+ }
+ }
+ appendSetting("GOARCH", cfg.BuildContext.GOARCH)
+ if cfg.GOEXPERIMENT != "" {
+ appendSetting("GOEXPERIMENT", cfg.GOEXPERIMENT)
+ }
+ appendSetting("GOOS", cfg.BuildContext.GOOS)
+ if key, val := cfg.GetArchEnv(); key != "" && val != "" {
+ appendSetting(key, val)
+ }
+ }
+
+ // 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
+ }
+
+ type vcsStatusError struct {
+ Status vcs.Status
+ Err error
+ }
+ cached := vcsStatusCache.Do(repoDir, func() any {
+ st, err := vcsCmd.Status(vcsCmd, repoDir)
+ return vcsStatusError{st, err}
+ }).(vcsStatusError)
+ if err := cached.Err; err != nil {
+ setVCSError(err)
+ return
+ }
+ st := cached.Status
+
+ appendSetting("vcs", vcsCmd.Cmd)
+ if st.Revision != "" {
+ appendSetting("vcs.revision", st.Revision)
+ }
+ if !st.CommitTime.IsZero() {
+ stamp := st.CommitTime.UTC().Format(time.RFC3339Nano)
+ appendSetting("vcs.time", stamp)
+ }
+ appendSetting("vcs.modified", 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.
@@ -2231,6 +2464,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
}
@@ -2447,7 +2684,8 @@ func PackagesAndErrors(ctx context.Context, opts PackageOpts, patterns []string)
}
matches, _ = modload.LoadPackages(ctx, modOpts, patterns...)
} else {
- matches = search.ImportPaths(patterns)
+ noModRoots := []string{}
+ matches = search.ImportPaths(patterns, noModRoots)
}
var (
@@ -2673,10 +2911,7 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa
if fi.IsDir() {
base.Fatalf("%s is a directory, should be a Go file", file)
}
- dir1, _ := filepath.Split(file)
- if dir1 == "" {
- dir1 = "./"
- }
+ dir1 := filepath.Dir(file)
if dir == "" {
dir = dir1
} else if dir != dir1 {
@@ -2704,7 +2939,9 @@ func GoFilesPackage(ctx context.Context, opts PackageOpts, gofiles []string) *Pa
pkg.Internal.Local = true
pkg.Internal.CmdlineFiles = true
pkg.load(ctx, opts, "command-line-arguments", &stk, nil, bp, err)
- pkg.Internal.LocalPrefix = dirToImportPath(dir)
+ if !cfg.ModulesEnabled {
+ pkg.Internal.LocalPrefix = dirToImportPath(dir)
+ }
pkg.ImportPath = "command-line-arguments"
pkg.Target = ""
pkg.Match = gofiles
diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go
index c8282965669c56d330c68a2390ba51d8c39452b8..6122428c9cf0a2f066535fa025153ab0185da82e 100644
--- a/src/cmd/go/internal/load/test.go
+++ b/src/cmd/go/internal/load/test.go
@@ -555,6 +555,7 @@ func formatTestmain(t *testFuncs) ([]byte, error) {
type testFuncs struct {
Tests []testFunc
Benchmarks []testFunc
+ FuzzTargets []testFunc
Examples []testFunc
TestMain *testFunc
Package *Package
@@ -653,6 +654,13 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
}
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
*doImport, *seen = true, true
+ case isTest(name, "Fuzz"):
+ err := checkTestFunc(n, "F")
+ if err != nil {
+ return err
+ }
+ t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false})
+ *doImport, *seen = true, true
}
}
ex := doc.Examples(f)
@@ -670,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
}
@@ -716,6 +730,12 @@ var benchmarks = []testing.InternalBenchmark{
{{end}}
}
+var fuzzTargets = []testing.InternalFuzzTarget{
+{{range .FuzzTargets}}
+ {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
var examples = []testing.InternalExample{
{{range .Examples}}
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
@@ -774,7 +794,7 @@ func main() {
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
- m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
+ m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
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 a37b2ad6d184b0f89169e4e0e1d8ff5e396b8370..09354d23061ec78452c36398c6775bbfbfec7c9f 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 70f5d7a688a070e5381c7da252fcd153cda80fb6..491bec39af935468b0fb7541e5faeb3c1c4dcfae 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 908afb6c8cb7687d4d34545038b831e27434c2d8..54b2c946e0d6b2272e5758ac638e0bef49aa3e8e 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 640d4406f4200cd1e44c426421955678feb66f91..7bd7bd28f55ab7830fe9bb0e86327272877de5d5 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 878a1e770d4d42a754950bc86f21221446c20262..d7778d05de1884f88bcbdbbc13e30a26c86d43ba 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 dd27ce92bd8d617449952f0c792f7966d10d2b84..e2ca5383046bbe13bd3dd5bb7d3c43bcab190d22 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 e4923f68764dad89a06539bbffccbe112e645996..1a677a7fe4a60d3a98866a846bbd6e39b057cbe9 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 979118b10ae83d13d01064ca3eb896f5b6a38d48..35669388e0531318a8d3cdb65f2c992bccbe9e7d 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 3acc6695a748003e6f0fc7107bc49ffcf5995162..c9907db46cecb36e043a1ff8105ac386bcec9e95 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 b753346e7da50bccbb6d904a3f59c30648093153..3c1caa334eb4df6bbaec7f1dae4228286df11067 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 0e5af852376e76c5363d595d15c55f18d4acd8f6..6b8a010fd9d71ca248b91a2ccd7375e3305f432a 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
@@ -66,6 +70,7 @@ func init() {
// TODO(jayconrod): https://golang.org/issue/35849 Apply -x to other 'go mod' commands.
cmdDownload.Flag.BoolVar(&cfg.BuildX, "x", false, "")
base.AddModCommonFlags(&cmdDownload.Flag)
+ base.AddWorkfileFlag(&cmdDownload.Flag)
}
type moduleJSON struct {
@@ -81,27 +86,68 @@ type moduleJSON struct {
}
func runDownload(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
// Check whether modules are enabled and whether we're in a module.
modload.ForceUseModules = true
- if !modload.HasModRoot() && len(args) == 0 {
- base.Fatalf("go mod download: no modules specified (see 'go help mod download')")
- }
+ modload.ExplicitWriteGoMod = true
haveExplicitArgs := len(args) > 0
- if !haveExplicitArgs {
- args = []string{"all"}
- }
- if modload.HasModRoot() {
- modload.LoadModFile(ctx) // to fill Target
- targetAtUpgrade := modload.Target.Path + "@upgrade"
- targetAtPatch := modload.Target.Path + "@patch"
- for _, arg := range args {
- switch arg {
- case modload.Target.Path, targetAtUpgrade, targetAtPatch:
- os.Stderr.WriteString("go mod download: skipping argument " + arg + " that resolves to the main module\n")
+
+ if modload.HasModRoot() || modload.WorkFilePath() != "" {
+ modload.LoadModFile(ctx) // to fill MainModules
+
+ if haveExplicitArgs {
+ for _, mainModule := range modload.MainModules.Versions() {
+ 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 if modload.WorkFilePath() != "" {
+ // TODO(#44435): Think about what the correct query is to download the
+ // right set of modules. Also see code review comment at
+ // https://go-review.googlesource.com/c/go/+/359794/comments/ce946a80_6cf53992.
+ args = []string{"all"}
+ } else {
+ mainModule := modload.MainModules.Versions()[0]
+ 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)
@@ -140,13 +186,16 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
if !haveExplicitArgs {
// 'go mod download' is sometimes run without arguments to pre-populate the
// module cache. It may fetch modules that aren't needed to build packages
- // in the main mdoule. This is usually not intended, so don't save sums for
- // downloaded modules (golang.org/issue/45332).
- // TODO(golang.org/issue/45551): For now, in ListModules, save sums needed
- // to load the build list (same as 1.15 behavior). In the future, report an
- // error if go.mod or go.sum need to be updated after loading the build
- // list.
- modload.DisallowWriteGoMod()
+ // in the main module. This is usually not intended, so don't save sums for
+ // downloaded modules (golang.org/issue/45332). We do still fix
+ // inconsistencies in go.mod though.
+ //
+ // TODO(#45551): In the future, report an error if go.mod or go.sum need to
+ // be updated after loading the build list. This may require setting
+ // the mode to "mod" or "readonly" depending on haveExplicitArgs.
+ if err := modload.WriteGoMod(ctx); err != nil {
+ base.Fatalf("go: %v", err)
+ }
}
for _, info := range infos {
@@ -183,7 +232,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
for _, m := range mods {
b, err := json.MarshalIndent(m, "", "\t")
if err != nil {
- base.Fatalf("go mod download: %v", err)
+ base.Fatalf("go: %v", err)
}
os.Stdout.Write(append(b, '\n'))
if m.Error != "" {
@@ -193,7 +242,7 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
} else {
for _, m := range mods {
if m.Error != "" {
- base.Errorf("go mod download: %v", m.Error)
+ base.Errorf("go: %v", m.Error)
}
}
base.ExitIfErrors()
@@ -206,13 +255,15 @@ func runDownload(ctx context.Context, cmd *base.Command, args []string) {
//
// Don't save sums for 'go mod download' without arguments; see comment above.
if haveExplicitArgs {
- modload.WriteGoMod(ctx)
+ if err := modload.WriteGoMod(ctx); err != nil {
+ base.Errorf("go: %v", err)
+ }
}
// If there was an error matching some of the requested packages, emit it now
// (after we've written the checksums for the modules that were downloaded
// successfully).
if infosErr != nil {
- base.Errorf("go mod download: %v", infosErr)
+ base.Errorf("go: %v", infosErr)
}
}
diff --git a/src/cmd/go/internal/modcmd/edit.go b/src/cmd/go/internal/modcmd/edit.go
index bb3d5210926aef7993e93a8beb0fbd189859e399..e5182a9590adc866532d0dfde80130808706faad 100644
--- a/src/cmd/go/internal/modcmd/edit.go
+++ b/src/cmd/go/internal/modcmd/edit.go
@@ -171,15 +171,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) {
len(edits) > 0
if !anyFlags {
- base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').")
+ base.Fatalf("go: no flags specified (see 'go help mod edit').")
}
if *editJSON && *editPrint {
- base.Fatalf("go mod edit: cannot use both -json and -print")
+ base.Fatalf("go: cannot use both -json and -print")
}
if len(args) > 1 {
- base.Fatalf("go mod edit: too many arguments")
+ base.Fatalf("go: too many arguments")
}
var gomod string
if len(args) == 1 {
@@ -190,7 +190,7 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) {
if *editModule != "" {
if err := module.CheckImportPath(*editModule); err != nil {
- base.Fatalf("go mod: invalid -module: %v", err)
+ base.Fatalf("go: invalid -module: %v", err)
}
}
@@ -264,15 +264,15 @@ func runEdit(ctx context.Context, cmd *base.Command, args []string) {
func parsePathVersion(flag, arg string) (path, version string) {
i := strings.Index(arg, "@")
if i < 0 {
- base.Fatalf("go mod: -%s=%s: need path@version", flag, arg)
+ base.Fatalf("go: -%s=%s: need path@version", flag, arg)
}
path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
if err := module.CheckImportPath(path); err != nil {
- base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+ base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
}
if !allowedVersionArg(version) {
- base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
+ base.Fatalf("go: -%s=%s: invalid version %q", flag, arg, version)
}
return path, version
@@ -281,11 +281,11 @@ func parsePathVersion(flag, arg string) (path, version string) {
// parsePath parses -flag=arg expecting arg to be path (not path@version).
func parsePath(flag, arg string) (path string) {
if strings.Contains(arg, "@") {
- base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg)
+ base.Fatalf("go: -%s=%s: need just path, not path@version", flag, arg)
}
path = arg
if err := module.CheckImportPath(path); err != nil {
- base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
+ base.Fatalf("go: -%s=%s: invalid path: %v", flag, arg, err)
}
return path
}
@@ -350,7 +350,7 @@ func flagRequire(arg string) {
path, version := parsePathVersion("require", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.AddRequire(path, version); err != nil {
- base.Fatalf("go mod: -require=%s: %v", arg, err)
+ base.Fatalf("go: -require=%s: %v", arg, err)
}
})
}
@@ -360,7 +360,7 @@ func flagDropRequire(arg string) {
path := parsePath("droprequire", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.DropRequire(path); err != nil {
- base.Fatalf("go mod: -droprequire=%s: %v", arg, err)
+ base.Fatalf("go: -droprequire=%s: %v", arg, err)
}
})
}
@@ -370,7 +370,7 @@ func flagExclude(arg string) {
path, version := parsePathVersion("exclude", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.AddExclude(path, version); err != nil {
- base.Fatalf("go mod: -exclude=%s: %v", arg, err)
+ base.Fatalf("go: -exclude=%s: %v", arg, err)
}
})
}
@@ -380,7 +380,7 @@ func flagDropExclude(arg string) {
path, version := parsePathVersion("dropexclude", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.DropExclude(path, version); err != nil {
- base.Fatalf("go mod: -dropexclude=%s: %v", arg, err)
+ base.Fatalf("go: -dropexclude=%s: %v", arg, err)
}
})
}
@@ -389,27 +389,27 @@ func flagDropExclude(arg string) {
func flagReplace(arg string) {
var i int
if i = strings.Index(arg, "="); i < 0 {
- base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
+ base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
}
old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
if strings.HasPrefix(new, ">") {
- base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
+ base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
}
oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
if err != nil {
- base.Fatalf("go mod: -replace=%s: %v", arg, err)
+ base.Fatalf("go: -replace=%s: %v", arg, err)
}
newPath, newVersion, err := parsePathVersionOptional("new", new, true)
if err != nil {
- base.Fatalf("go mod: -replace=%s: %v", arg, err)
+ base.Fatalf("go: -replace=%s: %v", arg, err)
}
if newPath == new && !modfile.IsDirectoryPath(new) {
- base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
+ base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
}
edits = append(edits, func(f *modfile.File) {
if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
- base.Fatalf("go mod: -replace=%s: %v", arg, err)
+ base.Fatalf("go: -replace=%s: %v", arg, err)
}
})
}
@@ -418,11 +418,11 @@ func flagReplace(arg string) {
func flagDropReplace(arg string) {
path, version, err := parsePathVersionOptional("old", arg, true)
if err != nil {
- base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+ base.Fatalf("go: -dropreplace=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.DropReplace(path, version); err != nil {
- base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
+ base.Fatalf("go: -dropreplace=%s: %v", arg, err)
}
})
}
@@ -431,11 +431,11 @@ func flagDropReplace(arg string) {
func flagRetract(arg string) {
vi, err := parseVersionInterval(arg)
if err != nil {
- base.Fatalf("go mod: -retract=%s: %v", arg, err)
+ base.Fatalf("go: -retract=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.AddRetract(vi, ""); err != nil {
- base.Fatalf("go mod: -retract=%s: %v", arg, err)
+ base.Fatalf("go: -retract=%s: %v", arg, err)
}
})
}
@@ -444,11 +444,11 @@ func flagRetract(arg string) {
func flagDropRetract(arg string) {
vi, err := parseVersionInterval(arg)
if err != nil {
- base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+ base.Fatalf("go: -dropretract=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.DropRetract(vi); err != nil {
- base.Fatalf("go mod: -dropretract=%s: %v", arg, err)
+ base.Fatalf("go: -dropretract=%s: %v", arg, err)
}
})
}
diff --git a/src/cmd/go/internal/modcmd/graph.go b/src/cmd/go/internal/modcmd/graph.go
index ac81f26dadea69dd3f58d7491caaa39aba7cfcad..9b6aa1fb14dddc5ade58f888a5bf01f2ba7ed140 100644
--- a/src/cmd/go/internal/modcmd/graph.go
+++ b/src/cmd/go/internal/modcmd/graph.go
@@ -42,11 +42,14 @@ var (
func init() {
cmdGraph.Flag.Var(&graphGo, "go", "")
base.AddModCommonFlags(&cmdGraph.Flag)
+ base.AddWorkfileFlag(&cmdGraph.Flag)
}
func runGraph(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
if len(args) > 0 {
- base.Fatalf("go mod graph: graph takes no arguments")
+ base.Fatalf("go: 'go mod graph' accepts no arguments")
}
modload.ForceUseModules = true
modload.RootMode = modload.NeedRoot
diff --git a/src/cmd/go/internal/modcmd/init.go b/src/cmd/go/internal/modcmd/init.go
index 958c3066ac11082dee31fe8b0cc4f3f049124e5e..bc4620a2a8d3f2121145ee64038ba0329fda599d 100644
--- a/src/cmd/go/internal/modcmd/init.go
+++ b/src/cmd/go/internal/modcmd/init.go
@@ -39,7 +39,7 @@ func init() {
func runInit(ctx context.Context, cmd *base.Command, args []string) {
if len(args) > 1 {
- base.Fatalf("go mod init: too many arguments")
+ base.Fatalf("go: 'go mod init' accepts at most one argument")
}
var modPath string
if len(args) == 1 {
diff --git a/src/cmd/go/internal/modcmd/tidy.go b/src/cmd/go/internal/modcmd/tidy.go
index fe25507e94f4fe365ec17d48b09e91be1109d702..d35476eb5393d3e92c6e61f384c99f5bf04463a7 100644
--- a/src/cmd/go/internal/modcmd/tidy.go
+++ b/src/cmd/go/internal/modcmd/tidy.go
@@ -75,8 +75,8 @@ type goVersionFlag struct {
v string
}
-func (f *goVersionFlag) String() string { return f.v }
-func (f *goVersionFlag) Get() interface{} { return f.v }
+func (f *goVersionFlag) String() string { return f.v }
+func (f *goVersionFlag) Get() any { return f.v }
func (f *goVersionFlag) Set(s string) error {
if s != "" {
@@ -95,7 +95,7 @@ func (f *goVersionFlag) Set(s string) error {
func runTidy(ctx context.Context, cmd *base.Command, args []string) {
if len(args) > 0 {
- base.Fatalf("go mod tidy: no arguments allowed")
+ base.Fatalf("go: 'go mod tidy' accepts no arguments")
}
// Tidy aims to make 'go test' reproducible for any package in 'all', so we
diff --git a/src/cmd/go/internal/modcmd/vendor.go b/src/cmd/go/internal/modcmd/vendor.go
index 713d5f9f3faa6ac9fb58dffc561c96d2dd5b1b70..ef123700aa3ca5ae8600f5b09bd84c4f4bc09a6d 100644
--- a/src/cmd/go/internal/modcmd/vendor.go
+++ b/src/cmd/go/internal/modcmd/vendor.go
@@ -31,7 +31,7 @@ import (
)
var cmdVendor = &base.Command{
- UsageLine: "go mod vendor [-e] [-v]",
+ UsageLine: "go mod vendor [-e] [-v] [-o outdir]",
Short: "make vendored copy of dependencies",
Long: `
Vendor resets the main module's vendor directory to include all packages
@@ -44,22 +44,29 @@ modules and packages to standard error.
The -e flag causes vendor to attempt to proceed despite errors
encountered while loading packages.
+The -o flag causes vendor to create the vendor directory at the given
+path instead of "vendor". The go command can only use a vendor directory
+named "vendor" within the module root directory, so this flag is
+primarily useful for other tools.
+
See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'.
`,
Run: runVendor,
}
-var vendorE bool // if true, report errors but proceed anyway
+var vendorE bool // if true, report errors but proceed anyway
+var vendorO string // if set, overrides the default output directory
func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
cmdVendor.Flag.BoolVar(&vendorE, "e", false, "")
+ cmdVendor.Flag.StringVar(&vendorO, "o", "", "")
base.AddModCommonFlags(&cmdVendor.Flag)
}
func runVendor(ctx context.Context, cmd *base.Command, args []string) {
if len(args) != 0 {
- base.Fatalf("go mod vendor: vendor takes no arguments")
+ base.Fatalf("go: 'go mod vendor' accepts no arguments")
}
modload.ForceUseModules = true
modload.RootMode = modload.NeedRoot
@@ -74,15 +81,23 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
_, pkgs := modload.LoadPackages(ctx, loadOpts, "all")
- vdir := filepath.Join(modload.ModRoot(), "vendor")
+ var vdir string
+ switch {
+ case filepath.IsAbs(vendorO):
+ vdir = vendorO
+ case vendorO != "":
+ vdir = filepath.Join(base.Cwd(), vendorO)
+ default:
+ vdir = filepath.Join(modload.VendorDir())
+ }
if err := os.RemoveAll(vdir); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
modpkgs := make(map[module.Version][]string)
for _, pkg := range pkgs {
m := modload.PackageModule(pkg)
- if m.Path == "" || m == modload.Target {
+ if m.Path == "" || m.Version == "" && modload.MainModules.Contains(m.Path) {
continue
}
modpkgs[m] = append(modpkgs[m], pkg)
@@ -128,7 +143,8 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
for _, m := range vendorMods {
- line := moduleLine(m, modload.Replacement(m))
+ replacement := modload.Replacement(m)
+ line := moduleLine(m, replacement)
io.WriteString(w, line)
goVersion := ""
@@ -177,11 +193,11 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
}
if err := os.MkdirAll(vdir, 0777); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
}
@@ -242,14 +258,14 @@ func vendorPkg(vdir, pkg string) {
if err != nil {
if errors.As(err, &noGoError) {
return // No source files in this package are built. Skip embeds in ignored files.
- } else if !errors.As(err, &multiplePackageError) { // multiplePackgeErrors are okay, but others are not.
+ } else if !errors.As(err, &multiplePackageError) { // multiplePackageErrors are OK, but others are not.
base.Fatalf("internal error: failed to find embedded files of %s: %v\n", pkg, err)
}
}
embedPatterns := str.StringList(bp.EmbedPatterns, bp.TestEmbedPatterns, bp.XTestEmbedPatterns)
embeds, err := load.ResolveEmbed(bp.Dir, embedPatterns)
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
for _, embed := range embeds {
embedDst := filepath.Join(dst, embed)
@@ -260,21 +276,21 @@ func vendorPkg(vdir, pkg string) {
// Copy the file as is done by copyDir below.
r, err := os.Open(filepath.Join(src, embed))
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
if err := os.MkdirAll(filepath.Dir(embedDst), 0777); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
w, err := os.Create(embedDst)
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
if _, err := io.Copy(w, r); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
r.Close()
if err := w.Close(); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
}
}
@@ -353,7 +369,7 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool {
if strings.HasSuffix(info.Name(), ".go") {
f, err := fsys.Open(filepath.Join(dir, info.Name()))
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
defer f.Close()
@@ -375,10 +391,10 @@ func matchPotentialSourceFile(dir string, info fs.DirEntry) bool {
func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, copiedFiles map[string]bool) {
files, err := os.ReadDir(src)
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
if err := os.MkdirAll(dst, 0777); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
for _, file := range files {
if file.IsDir() || !file.Type().IsRegular() || !match(src, file) {
@@ -387,20 +403,20 @@ func copyDir(dst, src string, match func(dir string, info fs.DirEntry) bool, cop
copiedFiles[file.Name()] = true
r, err := os.Open(filepath.Join(src, file.Name()))
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
dstPath := filepath.Join(dst, file.Name())
copiedFiles[dstPath] = true
w, err := os.Create(dstPath)
if err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
if _, err := io.Copy(w, r); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
r.Close()
if err := w.Close(); err != nil {
- base.Fatalf("go mod vendor: %v", err)
+ base.Fatalf("go: %v", err)
}
}
}
diff --git a/src/cmd/go/internal/modcmd/verify.go b/src/cmd/go/internal/modcmd/verify.go
index 5a6eca32cfb706bfcd50906937d9eec70e4fc4c4..3f0c005d5d91482c87d764f155168ed3a579151c 100644
--- a/src/cmd/go/internal/modcmd/verify.go
+++ b/src/cmd/go/internal/modcmd/verify.go
@@ -39,12 +39,15 @@ See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
func init() {
base.AddModCommonFlags(&cmdVerify.Flag)
+ base.AddWorkfileFlag(&cmdVerify.Flag)
}
func runVerify(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
if len(args) != 0 {
// NOTE(rsc): Could take a module pattern.
- base.Fatalf("go mod verify: verify takes no arguments")
+ base.Fatalf("go: verify takes no arguments")
}
modload.ForceUseModules = true
modload.RootMode = modload.NeedRoot
diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go
index 3b14b27c8c780d954a133cd46dc4bee2f875e442..d8355cca957a460788b1f7f146679c79bdfe169c 100644
--- a/src/cmd/go/internal/modcmd/why.go
+++ b/src/cmd/go/internal/modcmd/why.go
@@ -12,8 +12,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/imports"
"cmd/go/internal/modload"
-
- "golang.org/x/mod/module"
)
var cmdWhy = &base.Command{
@@ -61,11 +59,14 @@ var (
func init() {
cmdWhy.Run = runWhy // break init cycle
base.AddModCommonFlags(&cmdWhy.Flag)
+ base.AddWorkfileFlag(&cmdWhy.Flag)
}
func runWhy(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
modload.ForceUseModules = true
modload.RootMode = modload.NeedRoot
+ modload.ExplicitWriteGoMod = true // don't write go.mod in ListModules
loadOpts := modload.PackageOpts{
Tags: imports.AnyTags(),
@@ -78,28 +79,28 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) {
if *whyM {
for _, arg := range args {
if strings.Contains(arg, "@") {
- base.Fatalf("go mod why: module query not allowed")
+ base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg)
}
}
mods, err := modload.ListModules(ctx, args, 0)
if err != nil {
- base.Fatalf("go mod why: %v", err)
+ base.Fatalf("go: %v", err)
}
- byModule := make(map[module.Version][]string)
+ byModule := make(map[string][]string)
_, pkgs := modload.LoadPackages(ctx, loadOpts, "all")
for _, path := range pkgs {
m := modload.PackageModule(path)
if m.Path != "" {
- byModule[m] = append(byModule[m], path)
+ byModule[m.Path] = append(byModule[m.Path], path)
}
}
sep := ""
for _, m := range mods {
best := ""
bestDepth := 1000000000
- for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
+ for _, path := range byModule[m.Path] {
d := modload.WhyDepth(path)
if d > 0 && d < bestDepth {
best = path
diff --git a/src/cmd/go/internal/modfetch/bootstrap.go b/src/cmd/go/internal/modfetch/bootstrap.go
index ed694581a7c6af47ba12c789ece904fb15c1099c..e23669fb00c76b7bb5d6a1f20523051354f2f3f5 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/cache.go b/src/cmd/go/internal/modfetch/cache.go
index b01b4674131e7af76436c5b5513b6dbc1c155cbe..c682447900fbc39a1d1ab8c21c1146c940c2f7f6 100644
--- a/src/cmd/go/internal/modfetch/cache.go
+++ b/src/cmd/go/internal/modfetch/cache.go
@@ -204,7 +204,7 @@ func (r *cachingRepo) Versions(prefix string) ([]string, error) {
list []string
err error
}
- c := r.cache.Do("versions:"+prefix, func() interface{} {
+ c := r.cache.Do("versions:"+prefix, func() any {
list, err := r.repo().Versions(prefix)
return cached{list, err}
}).(cached)
@@ -221,7 +221,7 @@ type cachedInfo struct {
}
func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
- c := r.cache.Do("stat:"+rev, func() interface{} {
+ c := r.cache.Do("stat:"+rev, func() any {
file, info, err := readDiskStat(r.path, rev)
if err == nil {
return cachedInfo{info, nil}
@@ -233,7 +233,7 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
// then save the information under the proper version, for future use.
if info.Version != rev {
file, _ = CachePath(module.Version{Path: r.path, Version: info.Version}, "info")
- r.cache.Do("stat:"+info.Version, func() interface{} {
+ r.cache.Do("stat:"+info.Version, func() any {
return cachedInfo{info, err}
})
}
@@ -253,12 +253,12 @@ func (r *cachingRepo) Stat(rev string) (*RevInfo, error) {
}
func (r *cachingRepo) Latest() (*RevInfo, error) {
- c := r.cache.Do("latest:", func() interface{} {
+ c := r.cache.Do("latest:", func() any {
info, err := r.repo().Latest()
// Save info for likely future Stat call.
if err == nil {
- r.cache.Do("stat:"+info.Version, func() interface{} {
+ r.cache.Do("stat:"+info.Version, func() any {
return cachedInfo{info, err}
})
if file, _, err := readDiskStat(r.path, info.Version); err != nil {
@@ -281,7 +281,7 @@ func (r *cachingRepo) GoMod(version string) ([]byte, error) {
text []byte
err error
}
- c := r.cache.Do("gomod:"+version, func() interface{} {
+ c := r.cache.Do("gomod:"+version, func() any {
file, text, err := readDiskGoMod(r.path, version)
if err == nil {
// Note: readDiskGoMod already called checkGoMod.
@@ -720,7 +720,7 @@ func checkCacheDir() error {
if cfg.GOMODCACHE == "" {
// modload.Init exits if GOPATH[0] is empty, and cfg.GOMODCACHE
// is set to GOPATH[0]/pkg/mod if GOMODCACHE is empty, so this should never happen.
- return fmt.Errorf("internal error: cfg.GOMODCACHE not set")
+ return fmt.Errorf("module cache not found: neither GOMODCACHE nor GOPATH is set")
}
if !filepath.IsAbs(cfg.GOMODCACHE) {
return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q.\n", cfg.GOMODCACHE)
diff --git a/src/cmd/go/internal/modfetch/codehost/codehost.go b/src/cmd/go/internal/modfetch/codehost/codehost.go
index 378fbae34f9530378ced67a8298f797891af9e72..4a0e2241e50ac828a92c5add57d174faa70ba6a9 100644
--- a/src/cmd/go/internal/modfetch/codehost/codehost.go
+++ b/src/cmd/go/internal/modfetch/codehost/codehost.go
@@ -55,21 +55,6 @@ type Repo interface {
// os.IsNotExist(err) returns true.
ReadFile(rev, file string, maxSize int64) (data []byte, err error)
- // ReadFileRevs reads a single file at multiple versions.
- // It should refuse to read more than maxSize bytes.
- // The result is a map from each requested rev strings
- // to the associated FileRev. The map must have a non-nil
- // entry for every requested rev (unless ReadFileRevs returned an error).
- // A file simply being missing or even corrupted in revs[i]
- // should be reported only in files[revs[i]].Err, not in the error result
- // from ReadFileRevs.
- // The overall call should return an error (and no map) only
- // in the case of a problem with obtaining the data, such as
- // a network failure.
- // Implementations may assume that revs only contain tags,
- // not direct commit hashes.
- ReadFileRevs(revs []string, file string, maxSize int64) (files map[string]*FileRev, err error)
-
// ReadZip downloads a zip file for the subdir subdirectory
// of the given revision to a new file in a given temporary directory.
// It should refuse to read more than maxSize bytes.
@@ -243,7 +228,7 @@ var dirLock sync.Map
// It returns the standard output and, for a non-zero exit,
// a *RunError indicating the command, exit status, and standard error.
// Standard error is unavailable for commands that exit successfully.
-func Run(dir string, cmdline ...interface{}) ([]byte, error) {
+func Run(dir string, cmdline ...any) ([]byte, error) {
return RunWithStdin(dir, nil, cmdline...)
}
@@ -251,7 +236,7 @@ func Run(dir string, cmdline ...interface{}) ([]byte, error) {
// See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html.
var bashQuoter = strings.NewReplacer(`"`, `\"`, `$`, `\$`, "`", "\\`", `\`, `\\`)
-func RunWithStdin(dir string, stdin io.Reader, cmdline ...interface{}) ([]byte, error) {
+func RunWithStdin(dir string, stdin io.Reader, cmdline ...any) ([]byte, error) {
if dir != "" {
muIface, ok := dirLock.Load(dir)
if !ok {
diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go
index 4d4964edf447ebb7e963acd2c0defb74bb5957f6..34f453c855ef8d6b3dd7f6edbab34f30430b7c08 100644
--- a/src/cmd/go/internal/modfetch/codehost/git.go
+++ b/src/cmd/go/internal/modfetch/codehost/git.go
@@ -56,7 +56,7 @@ func newGitRepoCached(remote string, localOK bool) (Repo, error) {
err error
}
- c := gitRepoCache.Do(key{remote, localOK}, func() interface{} {
+ c := gitRepoCache.Do(key{remote, localOK}, func() any {
repo, err := newGitRepo(remote, localOK)
return cached{repo, err}
}).(cached)
@@ -170,59 +170,63 @@ func (r *gitRepo) loadLocalTags() {
}
// loadRefs loads heads and tags references from the remote into the map r.refs.
-// Should only be called as r.refsOnce.Do(r.loadRefs).
-func (r *gitRepo) loadRefs() {
- // The git protocol sends all known refs and ls-remote filters them on the client side,
- // so we might as well record both heads and tags in one shot.
- // Most of the time we only care about tags but sometimes we care about heads too.
- out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote)
- if gitErr != nil {
- if rerr, ok := gitErr.(*RunError); ok {
- if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) {
- rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information."
+// The result is cached in memory.
+func (r *gitRepo) loadRefs() (map[string]string, error) {
+ r.refsOnce.Do(func() {
+ // The git protocol sends all known refs and ls-remote filters them on the client side,
+ // so we might as well record both heads and tags in one shot.
+ // Most of the time we only care about tags but sometimes we care about heads too.
+ out, gitErr := Run(r.dir, "git", "ls-remote", "-q", r.remote)
+ if gitErr != nil {
+ if rerr, ok := gitErr.(*RunError); ok {
+ if bytes.Contains(rerr.Stderr, []byte("fatal: could not read Username")) {
+ rerr.HelpText = "Confirm the import path was entered correctly.\nIf this is a private repository, see https://golang.org/doc/faq#git_https for additional information."
+ }
}
- }
- // If the remote URL doesn't exist at all, ideally we should treat the whole
- // repository as nonexistent by wrapping the error in a notExistError.
- // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL
- // ourselves and see what code it serves.
- if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") {
- if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) {
- gitErr = notExistError{gitErr}
+ // If the remote URL doesn't exist at all, ideally we should treat the whole
+ // repository as nonexistent by wrapping the error in a notExistError.
+ // For HTTP and HTTPS, that's easy to detect: we'll try to fetch the URL
+ // ourselves and see what code it serves.
+ if u, err := url.Parse(r.remoteURL); err == nil && (u.Scheme == "http" || u.Scheme == "https") {
+ if _, err := web.GetBytes(u); errors.Is(err, fs.ErrNotExist) {
+ gitErr = notExistError{gitErr}
+ }
}
- }
- r.refsErr = gitErr
- return
- }
-
- r.refs = make(map[string]string)
- for _, line := range strings.Split(string(out), "\n") {
- f := strings.Fields(line)
- if len(f) != 2 {
- continue
+ r.refsErr = gitErr
+ return
}
- if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
- r.refs[f[1]] = f[0]
+
+ refs := make(map[string]string)
+ for _, line := range strings.Split(string(out), "\n") {
+ f := strings.Fields(line)
+ if len(f) != 2 {
+ continue
+ }
+ if f[1] == "HEAD" || strings.HasPrefix(f[1], "refs/heads/") || strings.HasPrefix(f[1], "refs/tags/") {
+ refs[f[1]] = f[0]
+ }
}
- }
- for ref, hash := range r.refs {
- if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
- r.refs[strings.TrimSuffix(ref, "^{}")] = hash
- delete(r.refs, ref)
+ for ref, hash := range refs {
+ if strings.HasSuffix(ref, "^{}") { // record unwrapped annotated tag as value of tag
+ refs[strings.TrimSuffix(ref, "^{}")] = hash
+ delete(refs, ref)
+ }
}
- }
+ r.refs = refs
+ })
+ return r.refs, r.refsErr
}
func (r *gitRepo) Tags(prefix string) ([]string, error) {
- r.refsOnce.Do(r.loadRefs)
- if r.refsErr != nil {
- return nil, r.refsErr
+ refs, err := r.loadRefs()
+ if err != nil {
+ return nil, err
}
tags := []string{}
- for ref := range r.refs {
+ for ref := range refs {
if !strings.HasPrefix(ref, "refs/tags/") {
continue
}
@@ -237,14 +241,14 @@ func (r *gitRepo) Tags(prefix string) ([]string, error) {
}
func (r *gitRepo) Latest() (*RevInfo, error) {
- r.refsOnce.Do(r.loadRefs)
- if r.refsErr != nil {
- return nil, r.refsErr
+ refs, err := r.loadRefs()
+ if err != nil {
+ return nil, err
}
- if r.refs["HEAD"] == "" {
+ if refs["HEAD"] == "" {
return nil, ErrNoCommits
}
- return r.Stat(r.refs["HEAD"])
+ return r.Stat(refs["HEAD"])
}
// findRef finds some ref name for the given hash,
@@ -252,8 +256,11 @@ func (r *gitRepo) Latest() (*RevInfo, error) {
// There may be multiple ref names for a given hash,
// in which case this returns some name - it doesn't matter which.
func (r *gitRepo) findRef(hash string) (ref string, ok bool) {
- r.refsOnce.Do(r.loadRefs)
- for ref, h := range r.refs {
+ refs, err := r.loadRefs()
+ if err != nil {
+ return "", false
+ }
+ for ref, h := range refs {
if h == hash {
return ref, true
}
@@ -295,29 +302,32 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
// Maybe rev is the name of a tag or branch on the remote server.
// Or maybe it's the prefix of a hash of a named ref.
// Try to resolve to both a ref (git name) and full (40-hex-digit) commit hash.
- r.refsOnce.Do(r.loadRefs)
+ refs, err := r.loadRefs()
+ if err != nil {
+ return nil, err
+ }
// loadRefs may return an error if git fails, for example segfaults, or
// could not load a private repo, but defer checking to the else block
// below, in case we already have the rev in question in the local cache.
var ref, hash string
- if r.refs["refs/tags/"+rev] != "" {
+ if refs["refs/tags/"+rev] != "" {
ref = "refs/tags/" + rev
- hash = r.refs[ref]
+ hash = refs[ref]
// Keep rev as is: tags are assumed not to change meaning.
- } else if r.refs["refs/heads/"+rev] != "" {
+ } else if refs["refs/heads/"+rev] != "" {
ref = "refs/heads/" + rev
- hash = r.refs[ref]
+ hash = refs[ref]
rev = hash // Replace rev, because meaning of refs/heads/foo can change.
- } else if rev == "HEAD" && r.refs["HEAD"] != "" {
+ } else if rev == "HEAD" && refs["HEAD"] != "" {
ref = "HEAD"
- hash = r.refs[ref]
+ hash = refs[ref]
rev = hash // Replace rev, because meaning of HEAD can change.
} else if len(rev) >= minHashDigits && len(rev) <= 40 && AllHex(rev) {
// At the least, we have a hash prefix we can look up after the fetch below.
// Maybe we can map it to a full hash using the known refs.
prefix := rev
// Check whether rev is prefix of known ref hash.
- for k, h := range r.refs {
+ for k, h := range refs {
if strings.HasPrefix(h, prefix) {
if hash != "" && hash != h {
// Hash is an ambiguous hash prefix.
@@ -335,9 +345,6 @@ func (r *gitRepo) stat(rev string) (*RevInfo, error) {
hash = rev
}
} else {
- if r.refsErr != nil {
- return nil, r.refsErr
- }
return nil, &UnknownRevisionError{Rev: rev}
}
@@ -496,7 +503,7 @@ func (r *gitRepo) Stat(rev string) (*RevInfo, error) {
info *RevInfo
err error
}
- c := r.statCache.Do(rev, func() interface{} {
+ c := r.statCache.Do(rev, func() any {
info, err := r.stat(rev)
return cached{info, err}
}).(cached)
@@ -516,140 +523,6 @@ func (r *gitRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
return out, nil
}
-func (r *gitRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) {
- // Create space to hold results.
- files := make(map[string]*FileRev)
- for _, rev := range revs {
- f := &FileRev{Rev: rev}
- files[rev] = f
- }
-
- // Collect locally-known revs.
- need, err := r.readFileRevs(revs, file, files)
- if err != nil {
- return nil, err
- }
- if len(need) == 0 {
- return files, nil
- }
-
- // Build list of known remote refs that might help.
- var redo []string
- r.refsOnce.Do(r.loadRefs)
- if r.refsErr != nil {
- return nil, r.refsErr
- }
- for _, tag := range need {
- if r.refs["refs/tags/"+tag] != "" {
- redo = append(redo, tag)
- }
- }
- if len(redo) == 0 {
- return files, nil
- }
-
- // Protect r.fetchLevel and the "fetch more and more" sequence.
- // See stat method above.
- unlock, err := r.mu.Lock()
- if err != nil {
- return nil, err
- }
- defer unlock()
-
- if err := r.fetchRefsLocked(); err != nil {
- return nil, err
- }
-
- if _, err := r.readFileRevs(redo, file, files); err != nil {
- return nil, err
- }
-
- return files, nil
-}
-
-func (r *gitRepo) readFileRevs(tags []string, file string, fileMap map[string]*FileRev) (missing []string, err error) {
- var stdin bytes.Buffer
- for _, tag := range tags {
- fmt.Fprintf(&stdin, "refs/tags/%s\n", tag)
- fmt.Fprintf(&stdin, "refs/tags/%s:%s\n", tag, file)
- }
-
- data, err := RunWithStdin(r.dir, &stdin, "git", "cat-file", "--batch")
- if err != nil {
- return nil, err
- }
-
- next := func() (typ string, body []byte, ok bool) {
- var line string
- i := bytes.IndexByte(data, '\n')
- if i < 0 {
- return "", nil, false
- }
- line, data = string(bytes.TrimSpace(data[:i])), data[i+1:]
- if strings.HasSuffix(line, " missing") {
- return "missing", nil, true
- }
- f := strings.Fields(line)
- if len(f) != 3 {
- return "", nil, false
- }
- n, err := strconv.Atoi(f[2])
- if err != nil || n > len(data) {
- return "", nil, false
- }
- body, data = data[:n], data[n:]
- if len(data) > 0 && data[0] == '\r' {
- data = data[1:]
- }
- if len(data) > 0 && data[0] == '\n' {
- data = data[1:]
- }
- return f[1], body, true
- }
-
- badGit := func() ([]string, error) {
- return nil, fmt.Errorf("malformed output from git cat-file --batch")
- }
-
- for _, tag := range tags {
- commitType, _, ok := next()
- if !ok {
- return badGit()
- }
- fileType, fileData, ok := next()
- if !ok {
- return badGit()
- }
- f := fileMap[tag]
- f.Data = nil
- f.Err = nil
- switch commitType {
- default:
- f.Err = fmt.Errorf("unexpected non-commit type %q for rev %s", commitType, tag)
-
- case "missing":
- // Note: f.Err must not satisfy os.IsNotExist. That's reserved for the file not existing in a valid commit.
- f.Err = fmt.Errorf("no such rev %s", tag)
- missing = append(missing, tag)
-
- case "tag", "commit":
- switch fileType {
- default:
- f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fmt.Errorf("unexpected non-blob type %q", fileType)}
- case "missing":
- f.Err = &fs.PathError{Path: tag + ":" + file, Op: "read", Err: fs.ErrNotExist}
- case "blob":
- f.Data = fileData
- }
- }
- }
- if len(bytes.TrimSpace(data)) != 0 {
- return badGit()
- }
-
- return missing, nil
-}
-
func (r *gitRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
info, err := r.Stat(rev)
if err != nil {
diff --git a/src/cmd/go/internal/modfetch/codehost/shell.go b/src/cmd/go/internal/modfetch/codehost/shell.go
index 0e9f38196676b108a28c77e9c2d5b591ecb0ae02..eaa01950b95ef395e57ac6f8d85036c44b6ff911 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 c2cca084e3077a8b3f9627e8b212491c1baf06dd..de62265efc5a722abae770da0486ef04c2e5a205 100644
--- a/src/cmd/go/internal/modfetch/codehost/vcs.go
+++ b/src/cmd/go/internal/modfetch/codehost/vcs.go
@@ -38,7 +38,7 @@ type VCSError struct {
func (e *VCSError) Error() string { return e.Err.Error() }
-func vcsErrorf(format string, a ...interface{}) error {
+func vcsErrorf(format string, a ...any) error {
return &VCSError{Err: fmt.Errorf(format, a...)}
}
@@ -51,7 +51,7 @@ func NewRepo(vcs, remote string) (Repo, error) {
repo Repo
err error
}
- c := vcsRepoCache.Do(key{vcs, remote}, func() interface{} {
+ c := vcsRepoCache.Do(key{vcs, remote}, func() any {
repo, err := newVCSRepo(vcs, remote)
if err != nil {
err = &VCSError{err}
@@ -382,19 +382,6 @@ func (r *vcsRepo) ReadFile(rev, file string, maxSize int64) ([]byte, error) {
return out, nil
}
-func (r *vcsRepo) ReadFileRevs(revs []string, file string, maxSize int64) (map[string]*FileRev, error) {
- // We don't technically need to lock here since we're returning an error
- // uncondititonally, but doing so anyway will help to avoid baking in
- // lock-inversion bugs.
- unlock, err := r.mu.Lock()
- if err != nil {
- return nil, err
- }
- defer unlock()
-
- return nil, vcsErrorf("ReadFileRevs not implemented")
-}
-
func (r *vcsRepo) RecentTag(rev, prefix string, allowed func(string) bool) (tag string, err error) {
// We don't technically need to lock here since we're returning an error
// uncondititonally, but doing so anyway will help to avoid baking in
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index dfef9f73c27aefa1bd3073706280a62f5cd8173e..79da010809b7407b12e2a1a823abb7ee3c022cce 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -321,7 +321,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
return ok
}
- invalidf := func(format string, args ...interface{}) error {
+ invalidf := func(format string, args ...any) error {
return &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
@@ -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)
}
}
@@ -1066,7 +1066,7 @@ func (fi dataFileInfo) Size() int64 { return int64(len(fi.f.data)) }
func (fi dataFileInfo) Mode() fs.FileMode { return 0644 }
func (fi dataFileInfo) ModTime() time.Time { return time.Time{} }
func (fi dataFileInfo) IsDir() bool { return false }
-func (fi dataFileInfo) Sys() interface{} { return nil }
+func (fi dataFileInfo) Sys() any { return nil }
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
diff --git a/src/cmd/go/internal/modfetch/fetch.go b/src/cmd/go/internal/modfetch/fetch.go
index d3d30d970b3b979b14741ce2a2da38c8812a422c..12b7431570930c1e97f16734e309d6112473d951 100644
--- a/src/cmd/go/internal/modfetch/fetch.go
+++ b/src/cmd/go/internal/modfetch/fetch.go
@@ -48,7 +48,7 @@ func Download(ctx context.Context, mod module.Version) (dir string, err error) {
dir string
err error
}
- c := downloadCache.Do(mod, func() interface{} {
+ c := downloadCache.Do(mod, func() any {
dir, err := download(ctx, mod)
if err != nil {
return cached{"", err}
@@ -165,7 +165,7 @@ func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err e
zipfile string
err error
}
- c := downloadZipCache.Do(mod, func() interface{} {
+ c := downloadZipCache.Do(mod, func() any {
zipfile, err := CachePath(mod, "zip")
if err != nil {
return cached{"", err}
@@ -384,7 +384,8 @@ func RemoveAll(dir string) error {
return robustio.RemoveAll(dir)
}
-var GoSumFile string // path to go.sum; set by package modload
+var GoSumFile string // path to go.sum; set by package modload
+var WorkspaceGoSumFiles []string // path to module go.sums in workspace; set by package modload
type modSum struct {
mod module.Version
@@ -393,10 +394,11 @@ type modSum struct {
var goSum struct {
mu sync.Mutex
- m map[module.Version][]string // content of go.sum file
- status map[modSum]modSumStatus // state of sums in m
- overwrite bool // if true, overwrite go.sum without incorporating its contents
- enabled bool // whether to use go.sum at all
+ m map[module.Version][]string // content of go.sum file
+ w map[string]map[module.Version][]string // sum file in workspace -> content of that sum file
+ status map[modSum]modSumStatus // state of sums in m
+ overwrite bool // if true, overwrite go.sum without incorporating its contents
+ enabled bool // whether to use go.sum at all
}
type modSumStatus struct {
@@ -417,23 +419,38 @@ func initGoSum() (bool, error) {
goSum.m = make(map[module.Version][]string)
goSum.status = make(map[modSum]modSumStatus)
+ goSum.w = make(map[string]map[module.Version][]string)
+
+ for _, f := range WorkspaceGoSumFiles {
+ goSum.w[f] = make(map[module.Version][]string)
+ _, err := readGoSumFile(goSum.w[f], f)
+ if err != nil {
+ return false, err
+ }
+ }
+
+ enabled, err := readGoSumFile(goSum.m, GoSumFile)
+ goSum.enabled = enabled
+ return enabled, err
+}
+
+func readGoSumFile(dst map[module.Version][]string, file string) (bool, error) {
var (
data []byte
err error
)
- if actualSumFile, ok := fsys.OverlayPath(GoSumFile); ok {
+ if actualSumFile, ok := fsys.OverlayPath(file); ok {
// Don't lock go.sum if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(actualSumFile)
} else {
- data, err = lockedfile.Read(GoSumFile)
+ data, err = lockedfile.Read(file)
}
if err != nil && !os.IsNotExist(err) {
return false, err
}
- goSum.enabled = true
- readGoSum(goSum.m, GoSumFile, data)
+ readGoSum(dst, file, data)
return true, nil
}
@@ -485,6 +502,16 @@ func HaveSum(mod module.Version) bool {
if err != nil || !inited {
return false
}
+ for _, goSums := range goSum.w {
+ for _, h := range goSums[mod] {
+ if !strings.HasPrefix(h, "h1:") {
+ continue
+ }
+ if !goSum.status[modSum{mod, h}].dirty {
+ return true
+ }
+ }
+ }
for _, h := range goSum.m[mod] {
if !strings.HasPrefix(h, "h1:") {
continue
@@ -602,15 +629,32 @@ func checkModSum(mod module.Version, h string) error {
// If it finds a conflicting pair instead, it calls base.Fatalf.
// goSum.mu must be locked.
func haveModSumLocked(mod module.Version, h string) bool {
+ sumFileName := "go.sum"
+ if strings.HasSuffix(GoSumFile, "go.work.sum") {
+ sumFileName = "go.work.sum"
+ }
for _, vh := range goSum.m[mod] {
if h == vh {
return true
}
if strings.HasPrefix(vh, "h1:") {
- base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v"+goSumMismatch, mod.Path, mod.Version, h, vh)
+ base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, sumFileName, vh)
+ }
+ }
+ // Also check workspace sums.
+ foundMatch := false
+ // Check sums from all files in case there are conflicts between
+ // the files.
+ for goSumFile, goSums := range goSum.w {
+ for _, vh := range goSums[mod] {
+ if h == vh {
+ foundMatch = true
+ } else if strings.HasPrefix(vh, "h1:") {
+ base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, goSumFile, vh)
+ }
}
}
- return false
+ return foundMatch
}
// addModSumLocked adds the pair mod,h to go.sum.
@@ -693,19 +737,21 @@ func isValidSum(data []byte) bool {
return true
}
+var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly")
+
// WriteGoSum writes the go.sum file if it needs to be updated.
//
// keep is used to check whether a newly added sum should be saved in go.sum.
// It should have entries for both module content sums and go.mod sums
// (version ends with "/go.mod"). Existing sums will be preserved unless they
// have been marked for deletion with TrimGoSum.
-func WriteGoSum(keep map[module.Version]bool) {
+func WriteGoSum(keep map[module.Version]bool, readonly bool) error {
goSum.mu.Lock()
defer goSum.mu.Unlock()
// If we haven't read the go.sum file yet, don't bother writing it.
if !goSum.enabled {
- return
+ return nil
}
// Check whether we need to add sums for which keep[m] is true or remove
@@ -723,10 +769,10 @@ Outer:
}
}
if !dirty {
- return
+ return nil
}
- if cfg.BuildMod == "readonly" {
- base.Fatalf("go: updates to go.sum needed, disabled by -mod=readonly")
+ if readonly {
+ return ErrGoSumDirty
}
if _, ok := fsys.OverlayPath(GoSumFile); ok {
base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
@@ -747,7 +793,7 @@ Outer:
goSum.m = make(map[module.Version][]string, len(goSum.m))
readGoSum(goSum.m, GoSumFile, data)
for ms, st := range goSum.status {
- if st.used {
+ if st.used && !sumInWorkspaceModulesLocked(ms.mod) {
addModSumLocked(ms.mod, ms.sum)
}
}
@@ -765,7 +811,7 @@ Outer:
sort.Strings(list)
for _, h := range list {
st := goSum.status[modSum{m, h}]
- if !st.dirty || (st.used && keep[m]) {
+ if (!st.dirty || (st.used && keep[m])) && !sumInWorkspaceModulesLocked(m) {
fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
}
}
@@ -774,11 +820,21 @@ Outer:
})
if err != nil {
- base.Fatalf("go: updating go.sum: %v", err)
+ return fmt.Errorf("updating go.sum: %w", err)
}
goSum.status = make(map[modSum]modSumStatus)
goSum.overwrite = false
+ return nil
+}
+
+func sumInWorkspaceModulesLocked(m module.Version) bool {
+ for _, goSums := range goSum.w {
+ if _, ok := goSums[m]; ok {
+ return true
+ }
+ }
+ return false
}
// TrimGoSum trims go.sum to contain only the modules needed for reproducible
diff --git a/src/cmd/go/internal/modfetch/repo.go b/src/cmd/go/internal/modfetch/repo.go
index 0bffa55af6f2ed6c4b12ec8363a8bb4f7f434bc6..1b42ecb6edb45c0ce6dcf8ee88fdcb047e061b61 100644
--- a/src/cmd/go/internal/modfetch/repo.go
+++ b/src/cmd/go/internal/modfetch/repo.go
@@ -196,7 +196,7 @@ func Lookup(proxy, path string) Repo {
type cached struct {
r Repo
}
- c := lookupCache.Do(lookupCacheKey{proxy, path}, func() interface{} {
+ c := lookupCache.Do(lookupCacheKey{proxy, path}, func() any {
r := newCachingRepo(path, func() (Repo, error) {
r, err := lookup(proxy, path)
if err == nil && traceRepo {
@@ -308,7 +308,7 @@ func newLoggingRepo(r Repo) *loggingRepo {
// defer logCall("hello %s", arg)()
//
// Note the final ().
-func logCall(format string, args ...interface{}) func() {
+func logCall(format string, args ...any) func() {
start := time.Now()
fmt.Fprintf(os.Stderr, "+++ %s\n", fmt.Sprintf(format, args...))
return func() {
@@ -371,7 +371,7 @@ type notExistError struct {
err error
}
-func notExistErrorf(format string, args ...interface{}) error {
+func notExistErrorf(format string, args ...any) error {
return notExistError{fmt.Errorf(format, args...)}
}
diff --git a/src/cmd/go/internal/modfetch/sumdb.go b/src/cmd/go/internal/modfetch/sumdb.go
index f233cba6df1bb7e996735147834e9c40960158f4..492b03bd84ac8b6e0d3758accb79e614dd567fe2 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
@@ -201,7 +200,8 @@ func (c *dbClient) ReadConfig(file string) (data []byte, err error) {
}
if cfg.SumdbDir == "" {
- return nil, errors.New("could not locate sumdb file: missing $GOPATH")
+ return nil, fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s",
+ cfg.GoPathError)
}
targ := filepath.Join(cfg.SumdbDir, file)
data, err = lockedfile.Read(targ)
@@ -220,7 +220,8 @@ func (*dbClient) WriteConfig(file string, old, new []byte) error {
return fmt.Errorf("cannot write key")
}
if cfg.SumdbDir == "" {
- return errors.New("could not locate sumdb file: missing $GOPATH")
+ return fmt.Errorf("could not locate sumdb file: missing $GOPATH: %s",
+ cfg.GoPathError)
}
targ := filepath.Join(cfg.SumdbDir, file)
os.MkdirAll(filepath.Dir(targ), 0777)
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index 9672e5598e0d3a980570899a4770925effaee087..3d8463e892c69ad5fdd5cb7885a7d4b65d6e0893 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -37,7 +37,6 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/imports"
- "cmd/go/internal/load"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/par"
@@ -50,14 +49,14 @@ import (
)
var CmdGet = &base.Command{
- // Note: -d -u are listed explicitly because they are the most common get flags.
+ // Note: flags below are listed explicitly because they're the most common.
// Do not send CLs removing them because they're covered by [get flags].
- UsageLine: "go get [-d] [-t] [-u] [-v] [build flags] [packages]",
+ UsageLine: "go get [-t] [-u] [-v] [build flags] [packages]",
Short: "add dependencies to current module and install them",
Long: `
Get resolves its command-line arguments to packages at specific module versions,
-updates go.mod to require those versions, downloads source code into the
-module cache, then builds and installs the named packages.
+updates go.mod to require those versions, and downloads source code into the
+module cache.
To add a dependency for a package or upgrade it to its latest version:
@@ -73,17 +72,18 @@ To remove a dependency on a module and downgrade modules that require it:
See https://golang.org/ref/mod#go-get for details.
-The 'go install' command may be used to build and install packages. When a
-version is specified, 'go install' runs in module-aware mode and ignores
-the go.mod file in the current directory. For example:
+In earlier versions of Go, 'go get' was used to build and install packages.
+Now, 'go get' is dedicated to adjusting dependencies in go.mod. 'go install'
+may be used to build and install commands instead. When a version is specified,
+'go install' runs in module-aware mode and ignores the go.mod file in the
+current directory. For example:
go install example.com/pkg@v1.2.3
go install example.com/pkg@latest
See 'go help install' or https://golang.org/ref/mod#go-install for details.
-In addition to build flags (listed in 'go help build') 'go get' accepts the
-following flags.
+'go get' accepts the following flags.
The -t flag instructs get to consider modules needed to build tests of
packages specified on the command line.
@@ -98,15 +98,9 @@ but changes the default to select patch releases.
When the -t and -u flags are used together, get will update
test dependencies as well.
-The -d flag instructs get not to build or install packages. get will only
-update go.mod and download source code needed to build packages.
-
-Building and installing packages with get is deprecated. In a future release,
-the -d flag will be enabled by default, and 'go get' will be only be used to
-adjust dependencies of the current module. To install a package using
-dependencies from the current module, use 'go install'. To install a package
-ignoring the current module, use 'go install' with an @version suffix like
-"@latest" after each argument.
+The -x flag prints commands as they are executed. This is useful for
+debugging version control commands when a module is downloaded directly
+from a repository.
For more about modules, see https://golang.org/ref/mod.
@@ -218,7 +212,7 @@ variable for future go command invocations.
}
var (
- getD = CmdGet.Flag.Bool("d", false, "")
+ getD = CmdGet.Flag.Bool("d", true, "")
getF = CmdGet.Flag.Bool("f", false, "")
getFix = CmdGet.Flag.Bool("fix", false, "")
getM = CmdGet.Flag.Bool("m", false, "")
@@ -263,30 +257,50 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
case "", "upgrade", "patch":
// ok
default:
- base.Fatalf("go get: unknown upgrade flag -u=%s", getU.rawVersion)
+ base.Fatalf("go: unknown upgrade flag -u=%s", getU.rawVersion)
+ }
+ // TODO(#43684): in the future (Go 1.20), warn that -d is a no-op.
+ if !*getD {
+ base.Fatalf("go: -d flag may not be disabled")
}
if *getF {
- fmt.Fprintf(os.Stderr, "go get: -f flag is a no-op when using modules\n")
+ fmt.Fprintf(os.Stderr, "go: -f flag is a no-op when using modules\n")
}
if *getFix {
- fmt.Fprintf(os.Stderr, "go get: -fix flag is a no-op when using modules\n")
+ fmt.Fprintf(os.Stderr, "go: -fix flag is a no-op when using modules\n")
}
if *getM {
- base.Fatalf("go get: -m flag is no longer supported; consider -d to skip building packages")
+ base.Fatalf("go: -m flag is no longer supported")
}
if *getInsecure {
- base.Fatalf("go get: -insecure flag is no longer supported; use GOINSECURE instead")
+ base.Fatalf("go: -insecure flag is no longer supported; use GOINSECURE instead")
}
+ modload.ForceUseModules = true
+
// Do not allow any updating of go.mod until we've applied
// all the requested changes and checked that the result matches
// what was requested.
- modload.DisallowWriteGoMod()
+ modload.ExplicitWriteGoMod = true
// Allow looking up modules for import paths when outside of a module.
// 'go get' is expected to do this, unlike other commands.
modload.AllowMissingModuleImports()
+ // 'go get' no longer builds or installs packages, so there's nothing to do
+ // if there's no go.mod file.
+ // TODO(#40775): make modload.Init return ErrNoModRoot instead of exiting.
+ // We could handle that here by printing a different message.
+ modload.Init()
+ if !modload.HasModRoot() {
+ base.Fatalf("go: go.mod file not found in current directory or any parent directory.\n" +
+ "\t'go get' is no longer supported outside a module.\n" +
+ "\tTo build and install a command, use 'go install' with a version,\n" +
+ "\tlike 'go install example.com/cmd@latest'\n" +
+ "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n" +
+ "\tor run 'go help get' or 'go help install'.")
+ }
+
queries := parseArgs(ctx, args)
r := newResolver(ctx, queries)
@@ -356,74 +370,12 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
}
r.checkPackageProblems(ctx, pkgPatterns)
- // We've already downloaded modules (and identified direct and indirect
- // dependencies) by loading packages in findAndUpgradeImports.
- // So if -d is set, we're done after the module work.
- //
- // Otherwise, we need to build and install the packages matched by
- // command line arguments.
- // Note that 'go get -u' without arguments is equivalent to
- // 'go get -u .', so we'll typically build the package in the current
- // directory.
- if !*getD && len(pkgPatterns) > 0 {
- work.BuildInit()
-
- pkgOpts := load.PackageOpts{ModResolveTests: *getT}
- var pkgs []*load.Package
- for _, pkg := range load.PackagesAndErrors(ctx, pkgOpts, pkgPatterns) {
- if pkg.Error != nil {
- var noGo *load.NoGoError
- if errors.As(pkg.Error.Err, &noGo) {
- if m := modload.PackageModule(pkg.ImportPath); m.Path == pkg.ImportPath {
- // pkg is at the root of a module, and doesn't exist with the current
- // build tags. Probably the user just wanted to change the version of
- // that module — not also build the package — so suppress the error.
- // (See https://golang.org/issue/33526.)
- continue
- }
- }
- }
- pkgs = append(pkgs, pkg)
- }
- load.CheckPackageErrors(pkgs)
-
- haveExternalExe := false
- for _, pkg := range pkgs {
- if pkg.Name == "main" && pkg.Module != nil && pkg.Module.Path != modload.Target.Path {
- haveExternalExe = true
- break
- }
- }
- if haveExternalExe {
- fmt.Fprint(os.Stderr, "go get: installing executables with 'go get' in module mode is deprecated.")
- var altMsg string
- if modload.HasModRoot() {
- altMsg = `
- To adjust and download dependencies of the current module, use 'go get -d'.
- To install using requirements of the current module, use 'go install'.
- To install ignoring the current module, use 'go install' with a version,
- like 'go install example.com/cmd@latest'.
-`
- } else {
- altMsg = "\n\tUse 'go install pkg@version' instead.\n"
- }
- fmt.Fprint(os.Stderr, altMsg)
- fmt.Fprintf(os.Stderr, "\tFor more information, see https://golang.org/doc/go-get-install-deprecation\n\tor run 'go help get' or 'go help install'.\n")
- }
-
- work.InstallPackages(ctx, pkgPatterns, pkgs)
- }
-
- if !modload.HasModRoot() {
- return
- }
-
// Everything succeeded. Update go.mod.
oldReqs := reqsFromGoMod(modload.ModFile())
- modload.AllowWriteGoMod()
- modload.WriteGoMod(ctx)
- modload.DisallowWriteGoMod()
+ if err := modload.WriteGoMod(ctx); err != nil {
+ base.Fatalf("go: %v", err)
+ }
newReqs := reqsFromGoMod(modload.ModFile())
r.reportChanges(oldReqs, newReqs)
@@ -440,7 +392,7 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query {
for _, arg := range search.CleanPatterns(rawArgs) {
q, err := newQuery(arg)
if err != nil {
- base.Errorf("go get: %v", err)
+ base.Errorf("go: %v", err)
continue
}
@@ -455,11 +407,11 @@ func parseArgs(ctx context.Context, rawArgs []string) []*query {
// if the argument has no version and either has no slash or refers to an existing file.
if strings.HasSuffix(q.raw, ".go") && q.rawVersion == "" {
if !strings.Contains(q.raw, "/") {
- base.Errorf("go get %s: arguments must be package or module paths", q.raw)
+ base.Errorf("go: %s: arguments must be package or module paths", q.raw)
continue
}
if fi, err := os.Stat(q.raw); err == nil && !fi.IsDir() {
- base.Errorf("go get: %s exists as a file, but 'go get' requires package arguments", q.raw)
+ base.Errorf("go: %s exists as a file, but 'go get' requires package arguments", q.raw)
continue
}
}
@@ -649,7 +601,7 @@ func (r *resolver) matchInModule(ctx context.Context, pattern string, m module.V
err error
}
- e := r.matchInModuleCache.Do(key{pattern, m}, func() interface{} {
+ e := r.matchInModuleCache.Do(key{pattern, m}, func() any {
match := modload.MatchInModule(ctx, pattern, m, imports.AnyTags())
if len(match.Errs) > 0 {
return entry{match.Pkgs, match.Errs[0]}
@@ -675,7 +627,9 @@ func (r *resolver) queryNone(ctx context.Context, q *query) {
if !q.isWildcard() {
q.pathOnce(q.pattern, func() pathSet {
- if modload.HasModRoot() && q.pattern == modload.Target.Path {
+ hasModRoot := modload.HasModRoot()
+ if hasModRoot && modload.MainModules.Contains(q.pattern) {
+ v := module.Version{Path: q.pattern}
// The user has explicitly requested to downgrade their own module to
// version "none". This is not an entirely unreasonable request: it
// could plausibly mean “downgrade away everything that depends on any
@@ -686,7 +640,7 @@ func (r *resolver) queryNone(ctx context.Context, q *query) {
// However, neither of those behaviors would be consistent with the
// plain meaning of the query. To try to reduce confusion, reject the
// query explicitly.
- return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version})
+ return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{v}, Pattern: q.pattern, Query: q.version})
}
return pathSet{mod: module.Version{Path: q.pattern, Version: "none"}}
@@ -698,8 +652,8 @@ func (r *resolver) queryNone(ctx context.Context, q *query) {
continue
}
q.pathOnce(curM.Path, func() pathSet {
- if modload.HasModRoot() && curM == modload.Target {
- return errSet(&modload.QueryMatchesMainModuleError{Pattern: q.pattern, Query: q.version})
+ if modload.HasModRoot() && curM.Version == "" && modload.MainModules.Contains(curM.Path) {
+ return errSet(&modload.QueryMatchesMainModulesError{MainModules: []module.Version{curM}, Pattern: q.pattern, Query: q.version})
}
return pathSet{mod: module.Version{Path: curM.Path, Version: "none"}}
})
@@ -718,28 +672,38 @@ func (r *resolver) performLocalQueries(ctx context.Context) {
// Absolute paths like C:\foo and relative paths like ../foo... are
// restricted to matching packages in the main module.
- pkgPattern := modload.DirImportPath(ctx, q.pattern)
+ pkgPattern, mainModule := modload.MainModules.DirImportPath(ctx, q.pattern)
if pkgPattern == "." {
- return errSet(fmt.Errorf("%s%s is not within module rooted at %s", q.pattern, absDetail, modload.ModRoot()))
+ modload.MustHaveModRoot()
+ var modRoots []string
+ for _, m := range modload.MainModules.Versions() {
+ modRoots = append(modRoots, modload.MainModules.ModRoot(m))
+ }
+ var plural string
+ if len(modRoots) != 1 {
+ plural = "s"
+ }
+ return errSet(fmt.Errorf("%s%s is not within module%s rooted at %s", q.pattern, absDetail, plural, strings.Join(modRoots, ", ")))
}
- match := modload.MatchInModule(ctx, pkgPattern, modload.Target, imports.AnyTags())
+ match := modload.MatchInModule(ctx, pkgPattern, mainModule, imports.AnyTags())
if len(match.Errs) > 0 {
return pathSet{err: match.Errs[0]}
}
if len(match.Pkgs) == 0 {
if q.raw == "" || q.raw == "." {
- return errSet(fmt.Errorf("no package in current directory"))
+ return errSet(fmt.Errorf("no package to get in current directory"))
}
if !q.isWildcard() {
- return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.ModRoot()))
+ modload.MustHaveModRoot()
+ return errSet(fmt.Errorf("%s%s is not a package in module rooted at %s", q.pattern, absDetail, modload.MainModules.ModRoot(mainModule)))
}
search.WarnUnmatched([]*search.Match{match})
return pathSet{}
}
- return pathSet{pkgMods: []module.Version{modload.Target}}
+ return pathSet{pkgMods: []module.Version{mainModule}}
})
}
}
@@ -789,11 +753,12 @@ func (r *resolver) queryWildcard(ctx context.Context, q *query) {
return pathSet{}
}
- if curM.Path == modload.Target.Path && !versionOkForMainModule(q.version) {
+ if modload.MainModules.Contains(curM.Path) && !versionOkForMainModule(q.version) {
if q.matchesPath(curM.Path) {
- return errSet(&modload.QueryMatchesMainModuleError{
- Pattern: q.pattern,
- Query: q.version,
+ return errSet(&modload.QueryMatchesMainModulesError{
+ MainModules: []module.Version{curM},
+ Pattern: q.pattern,
+ Query: q.version,
})
}
@@ -928,7 +893,7 @@ func (r *resolver) checkWildcardVersions(ctx context.Context) {
// curM at its original version contains a path matching q.pattern,
// but at rev.Version it does not, so (somewhat paradoxically) if
// we changed the version of curM it would no longer match the query.
- var version interface{} = m
+ var version any = m
if rev.Version != q.version {
version = fmt.Sprintf("%s@%s (%s)", m.Path, q.version, m.Version)
}
@@ -1159,8 +1124,8 @@ func (r *resolver) loadPackages(ctx context.Context, patterns []string, findPack
}
opts.AllowPackage = func(ctx context.Context, path string, m module.Version) error {
- if m.Path == "" || m == modload.Target {
- // Packages in the standard library and main module are already at their
+ if m.Path == "" || m.Version == "" {
+ // Packages in the standard library and main modules are already at their
// latest (and only) available versions.
return nil
}
@@ -1327,7 +1292,7 @@ func (r *resolver) applyUpgrades(ctx context.Context, upgrades []pathSet) (chang
var tentative []module.Version
for _, cs := range upgrades {
if cs.err != nil {
- base.Errorf("go get: %v", cs.err)
+ base.Errorf("go: %v", cs.err)
continue
}
@@ -1370,11 +1335,11 @@ func (r *resolver) disambiguate(cs pathSet) (filtered pathSet, isPackage bool, m
continue
}
- if m.Path == modload.Target.Path {
- if m.Version == modload.Target.Version {
+ if modload.MainModules.Contains(m.Path) {
+ if m.Version == "" {
return pathSet{}, true, m, true
}
- // The main module can only be set to its own version.
+ // A main module can only be set to its own version.
continue
}
@@ -1720,13 +1685,13 @@ func (r *resolver) reportChanges(oldReqs, newReqs []module.Version) {
})
for _, c := range sortedChanges {
if c.old == "" {
- fmt.Fprintf(os.Stderr, "go get: added %s %s\n", c.path, c.new)
+ fmt.Fprintf(os.Stderr, "go: added %s %s\n", c.path, c.new)
} else if c.new == "none" || c.new == "" {
- fmt.Fprintf(os.Stderr, "go get: removed %s %s\n", c.path, c.old)
+ fmt.Fprintf(os.Stderr, "go: removed %s %s\n", c.path, c.old)
} else if semver.Compare(c.new, c.old) > 0 {
- fmt.Fprintf(os.Stderr, "go get: upgraded %s %s => %s\n", c.path, c.old, c.new)
+ fmt.Fprintf(os.Stderr, "go: upgraded %s %s => %s\n", c.path, c.old, c.new)
} else {
- fmt.Fprintf(os.Stderr, "go get: downgraded %s %s => %s\n", c.path, c.old, c.new)
+ fmt.Fprintf(os.Stderr, "go: downgraded %s %s => %s\n", c.path, c.old, c.new)
}
}
@@ -1744,10 +1709,11 @@ func (r *resolver) resolve(q *query, m module.Version) {
panic("internal error: resolving a module.Version with an empty path")
}
- if m.Path == modload.Target.Path && m.Version != modload.Target.Version {
- reportError(q, &modload.QueryMatchesMainModuleError{
- Pattern: q.pattern,
- Query: q.version,
+ if modload.MainModules.Contains(m.Path) && m.Version != "" {
+ reportError(q, &modload.QueryMatchesMainModulesError{
+ MainModules: []module.Version{{Path: m.Path}},
+ Pattern: q.pattern,
+ Query: q.version,
})
return
}
@@ -1775,7 +1741,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
resolved := make([]module.Version, 0, len(r.resolvedVersion))
for mPath, rv := range r.resolvedVersion {
- if mPath != modload.Target.Path {
+ if !modload.MainModules.Contains(mPath) {
resolved = append(resolved, module.Version{Path: mPath, Version: rv.version})
}
}
@@ -1784,7 +1750,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
if err != nil {
var constraint *modload.ConstraintError
if !errors.As(err, &constraint) {
- base.Errorf("go get: %v", err)
+ base.Errorf("go: %v", err)
return false
}
@@ -1796,7 +1762,7 @@ func (r *resolver) updateBuildList(ctx context.Context, additions []module.Versi
return rv.reason.ResolvedString(module.Version{Path: m.Path, Version: rv.version})
}
for _, c := range constraint.Conflicts {
- base.Errorf("go get: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint))
+ base.Errorf("go: %v requires %v, not %v", reason(c.Source), c.Dep, reason(c.Constraint))
}
return false
}
diff --git a/src/cmd/go/internal/modget/query.go b/src/cmd/go/internal/modget/query.go
index 1a5a60f7eb98fb97454ded379eeff593e8df97da..887cb51b317f7f3c46db33192452197571467a09 100644
--- a/src/cmd/go/internal/modget/query.go
+++ b/src/cmd/go/internal/modget/query.go
@@ -192,9 +192,9 @@ func (q *query) validate() error {
// TODO(bcmills): "all@none" seems like a totally reasonable way to
// request that we remove all module requirements, leaving only the main
// module and standard library. Perhaps we should implement that someday.
- return &modload.QueryMatchesMainModuleError{
- Pattern: q.pattern,
- Query: q.version,
+ return &modload.QueryUpgradesAllError{
+ MainModules: modload.MainModules.Versions(),
+ Query: q.version,
}
}
}
@@ -284,21 +284,21 @@ func reportError(q *query, err error) {
patternRE := regexp.MustCompile("(?m)(?:[ \t(\"`]|^)" + regexp.QuoteMeta(q.pattern) + "(?:[ @:;)\"`]|$)")
if patternRE.MatchString(errStr) {
if q.rawVersion == "" {
- base.Errorf("go get: %s", errStr)
+ base.Errorf("go: %s", errStr)
return
}
versionRE := regexp.MustCompile("(?m)(?:[ @(\"`]|^)" + regexp.QuoteMeta(q.version) + "(?:[ :;)\"`]|$)")
if versionRE.MatchString(errStr) {
- base.Errorf("go get: %s", errStr)
+ base.Errorf("go: %s", errStr)
return
}
}
if qs := q.String(); qs != "" {
- base.Errorf("go get %s: %s", qs, errStr)
+ base.Errorf("go: %s: %s", qs, errStr)
} else {
- base.Errorf("go get: %s", errStr)
+ base.Errorf("go: %s", errStr)
}
}
diff --git a/src/cmd/go/internal/modload/build.go b/src/cmd/go/internal/modload/build.go
index 76e1ad589f43c1c6c12fed6faa038186a19453d7..bfc73cc2f9ab59238da44f0aefd58382295d04ea 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"
@@ -80,7 +79,7 @@ func ModuleInfo(ctx context.Context, path string) *modinfo.ModulePublic {
v string
ok bool
)
- if rs.depth == lazy {
+ if rs.pruning == pruned {
v, ok = rs.rootSelected(path)
}
if !ok {
@@ -212,20 +211,20 @@ func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) {
// in rs (which may be nil to indicate that m was not loaded from a requirement
// graph).
func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode ListMode) *modinfo.ModulePublic {
- if m == Target {
+ if m.Version == "" && MainModules.Contains(m.Path) {
info := &modinfo.ModulePublic{
Path: m.Path,
Version: m.Version,
Main: true,
}
- if v, ok := rawGoVersion.Load(Target); ok {
+ if v, ok := rawGoVersion.Load(m); ok {
info.GoVersion = v.(string)
} else {
panic("internal error: GoVersion not set for main module")
}
- if HasModRoot() {
- info.Dir = ModRoot()
- info.GoMod = ModFilePath()
+ if modRoot := MainModules.ModRoot(m); modRoot != "" {
+ info.Dir = modRoot
+ info.GoMod = modFilePath(modRoot)
}
return info
}
@@ -322,7 +321,7 @@ 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(ModRoot(), r.Path)
+ info.Replace.Dir = filepath.Join(replaceRelativeTo(), r.Path)
}
info.Replace.GoMod = filepath.Join(info.Replace.Dir, "go.mod")
}
@@ -336,115 +335,33 @@ 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 isStandardImportPath(path) || !Enabled() {
- return ""
- }
-
- target := mustFindModule(loaded, path, path)
- mdeps := make(map[module.Version]bool)
- for _, dep := range deps {
- if !isStandardImportPath(dep) {
- mdeps[mustFindModule(loaded, path, dep)] = 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))
- }
- }
-
- writeEntry("mod", target)
- for _, mod := range mods {
- writeEntry("dep", mod)
- }
-
- return buf.String()
-}
-
-// mustFindModule is like findModule, but it calls base.Fatalf if the
-// module can't be found.
-//
-// TODO(jayconrod): remove this. Callers should use findModule and return
-// errors instead of relying on base.Fatalf.
-func mustFindModule(ld *loader, target, path string) module.Version {
- pkg, ok := ld.pkgCache.Get(path).(*loadPkg)
- if ok {
- if pkg.err != nil {
- base.Fatalf("build %v: cannot load %v: %v", target, path, pkg.err)
- }
- return pkg.mod
- }
-
- if path == "command-line-arguments" {
- return Target
- }
-
- base.Fatalf("build %v: cannot find module for path %v", target, path)
- panic("unreachable")
-}
-
// 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 returend.
+// Otherwise, module.Version{} and false are returned.
func findModule(ld *loader, path string) (module.Version, bool) {
if pkg, ok := ld.pkgCache.Get(path).(*loadPkg); ok {
return pkg.mod, pkg.mod != module.Version{}
}
- if path == "command-line-arguments" {
- return Target, true
- }
return module.Version{}, false
}
func ModInfoProg(info string, isgccgo bool) []byte {
- // Inject a variable with the debug information as runtime.modinfo,
- // but compile it in package main so that it is specific to the binary.
- // The variable must be a literal so that it will have the correct value
- // before the initializer for package main runs.
- //
- // The runtime startup code refers to the variable, which keeps it live
- // in all binaries.
- //
- // Note: we use an alternate recipe below for gccgo (based on an
- // init function) due to the fact that gccgo does not support
- // applying a "//go:linkname" directive to a variable. This has
- // drawbacks in that other packages may want to look at the module
- // info in their init functions (see issue 29628), which won't
- // work for gccgo. See also issue 30344.
-
- if !isgccgo {
- return []byte(fmt.Sprintf(`package main
-import _ "unsafe"
-//go:linkname __debug_modinfo__ runtime.modinfo
-var __debug_modinfo__ = %q
-`, string(infoStart)+info+string(infoEnd)))
- } else {
+ // Inject an init function to set runtime.modinfo.
+ // This is only used for gccgo - with gc we hand the info directly to the linker.
+ // The init function has the drawback that packages may want to
+ // look at the module info in their init functions (see issue 29628),
+ // which won't work. See also issue 30344.
+ if isgccgo {
return []byte(fmt.Sprintf(`package main
import _ "unsafe"
//go:linkname __set_debug_modinfo__ runtime.setmodinfo
func __set_debug_modinfo__(string)
func init() { __set_debug_modinfo__(%q) }
-`, string(infoStart)+info+string(infoEnd)))
+`, ModInfoData(info)))
}
+ return nil
+}
+
+func ModInfoData(info string) []byte {
+ return []byte(string(infoStart) + info + string(infoEnd))
}
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index bf6956731676d39e66e74642f6a43b40607d619a..45be51f1c649cce2b8541d68b8d64300c9042db1 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -30,18 +30,25 @@ func capVersionSlice(s []module.Version) []module.Version {
// A Requirements represents a logically-immutable set of root module requirements.
type Requirements struct {
- // depth is the depth at which the requirement graph is computed.
+ // pruning is the pruning at which the requirement graph is computed.
//
- // If eager, the graph includes all transitive requirements regardless of depth.
+ // If unpruned, the graph includes all transitive requirements regardless
+ // of whether the requiring module supports pruning.
//
- // If lazy, the graph includes only the root modules, the explicit
+ // If pruned, the graph includes only the root modules, the explicit
// requirements of those root modules, and the transitive requirements of only
- // the *non-lazy* root modules.
- depth modDepth
-
- // rootModules is the set of module versions explicitly required by the main
- // module, sorted and capped to length. It may contain duplicates, and may
- // contain multiple versions for a given module path.
+ // the root modules that do not support pruning.
+ //
+ // If workspace, the graph includes only the workspace modules, the explicit
+ // requirements of the workspace modules, and the transitive requirements of
+ // the workspace modules that do not support pruning.
+ pruning modPruning
+
+ // rootModules is the set of root modules of the graph, sorted and capped to
+ // length. It may contain duplicates, and may contain multiple versions for a
+ // given module path. The root modules of the groph are the set of main
+ // modules in workspace mode, and the main module's direct requirements
+ // outside workspace mode.
rootModules []module.Version
maxRootVersion map[string]string
@@ -97,10 +104,23 @@ var requirements *Requirements
//
// If vendoring is in effect, the caller must invoke initVendor on the returned
// *Requirements before any other method.
-func newRequirements(depth modDepth, rootModules []module.Version, direct map[string]bool) *Requirements {
+func newRequirements(pruning modPruning, rootModules []module.Version, direct map[string]bool) *Requirements {
+ if pruning == workspace {
+ return &Requirements{
+ pruning: pruning,
+ rootModules: capVersionSlice(rootModules),
+ maxRootVersion: nil,
+ direct: direct,
+ }
+ }
+
+ if workFilePath != "" && pruning != workspace {
+ panic("in workspace mode, but pruning is not workspace in newRequirements")
+ }
+
for i, m := range rootModules {
- if m == Target {
- panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is Target", i))
+ if m.Version == "" && MainModules.Contains(m.Path) {
+ panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is a main module", i))
}
if m.Path == "" || m.Version == "" {
panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m))
@@ -114,7 +134,7 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st
}
rs := &Requirements{
- depth: depth,
+ pruning: pruning,
rootModules: capVersionSlice(rootModules),
maxRootVersion: make(map[string]string, len(rootModules)),
direct: direct,
@@ -135,13 +155,18 @@ func newRequirements(depth modDepth, rootModules []module.Version, direct map[st
func (rs *Requirements) initVendor(vendorList []module.Version) {
rs.graphOnce.Do(func() {
mg := &ModuleGraph{
- g: mvs.NewGraph(cmpVersion, []module.Version{Target}),
+ g: mvs.NewGraph(cmpVersion, MainModules.Versions()),
+ }
+
+ if MainModules.Len() != 1 {
+ panic("There should be exactly one main module in Vendor mode.")
}
+ mainModule := MainModules.Versions()[0]
- if rs.depth == lazy {
- // The roots of a lazy module should already include every module in the
- // vendor list, because the vendored modules are the same as those
- // maintained as roots by the lazy loading “import invariant”.
+ if rs.pruning == pruned {
+ // The roots of a pruned module should already include every module in the
+ // vendor list, because the vendored modules are the same as those needed
+ // for graph pruning.
//
// Just to be sure, we'll double-check that here.
inconsistent := false
@@ -156,9 +181,9 @@ func (rs *Requirements) initVendor(vendorList []module.Version) {
}
// Now we can treat the rest of the module graph as effectively “pruned
- // out”, like a more aggressive version of lazy loading: in vendor mode,
- // the root requirements *are* the complete module graph.
- mg.g.Require(Target, rs.rootModules)
+ // out”, as though we are viewing the main module from outside: in vendor
+ // mode, the root requirements *are* the complete module graph.
+ mg.g.Require(mainModule, rs.rootModules)
} else {
// The transitive requirements of the main module are not in general available
// from the vendor directory, and we don't actually know how we got from
@@ -170,7 +195,7 @@ func (rs *Requirements) initVendor(vendorList []module.Version) {
// graph, but still distinguishes between direct and indirect
// dependencies.
vendorMod := module.Version{Path: "vendor/modules.txt", Version: ""}
- mg.g.Require(Target, append(rs.rootModules, vendorMod))
+ mg.g.Require(mainModule, append(rs.rootModules, vendorMod))
mg.g.Require(vendorMod, vendorList)
}
@@ -182,8 +207,8 @@ func (rs *Requirements) initVendor(vendorList []module.Version) {
// path, or the zero module.Version and ok=false if the module is not a root
// dependency.
func (rs *Requirements) rootSelected(path string) (version string, ok bool) {
- if path == Target.Path {
- return Target.Version, true
+ if MainModules.Contains(path) {
+ return "", true
}
if v, ok := rs.maxRootVersion[path]; ok {
return v, true
@@ -197,7 +222,7 @@ func (rs *Requirements) rootSelected(path string) (version string, ok bool) {
// selection.
func (rs *Requirements) hasRedundantRoot() bool {
for i, m := range rs.rootModules {
- if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) {
+ if MainModules.Contains(m.Path) || (i > 0 && m.Path == rs.rootModules[i-1].Path) {
return true
}
}
@@ -214,7 +239,7 @@ func (rs *Requirements) hasRedundantRoot() bool {
// returns a non-nil error of type *mvs.BuildListError.
func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) {
rs.graphOnce.Do(func() {
- mg, mgErr := readModGraph(ctx, rs.depth, rs.rootModules)
+ mg, mgErr := readModGraph(ctx, rs.pruning, rs.rootModules)
rs.graph.Store(cachedGraph{mg, mgErr})
})
cached := rs.graph.Load().(cachedGraph)
@@ -230,7 +255,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
@@ -254,8 +279,16 @@ var readModGraphDebugOnce sync.Once
//
// Unlike LoadModGraph, readModGraph does not attempt to diagnose or update
// inconsistent roots.
-func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (*ModuleGraph, error) {
- if depth == lazy {
+func readModGraph(ctx context.Context, pruning modPruning, roots []module.Version) (*ModuleGraph, error) {
+ if pruning == pruned {
+ // Enable diagnostics for lazy module loading
+ // (https://golang.org/ref/mod#lazy-loading) only if the module graph is
+ // pruned.
+ //
+ // In unpruned modules,we load the module graph much more aggressively (in
+ // order to detect inconsistencies that wouldn't be feasible to spot-check),
+ // so it wouldn't be useful to log when that occurs (because it happens in
+ // normal operation all the time).
readModGraphDebugOnce.Do(func() {
for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
switch f {
@@ -274,21 +307,26 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (
mu sync.Mutex // guards mg.g and hasError during loading
hasError bool
mg = &ModuleGraph{
- g: mvs.NewGraph(cmpVersion, []module.Version{Target}),
+ g: mvs.NewGraph(cmpVersion, MainModules.Versions()),
}
)
- mg.g.Require(Target, roots)
+ if pruning != workspace {
+ if inWorkspaceMode() {
+ panic("pruning is not workspace in workspace mode")
+ }
+ mg.g.Require(MainModules.mustGetSingleMainModule(), roots)
+ }
var (
- loadQueue = par.NewQueue(runtime.GOMAXPROCS(0))
- loadingEager sync.Map // module.Version → nil; the set of modules that have been or are being loaded via eager roots
+ loadQueue = par.NewQueue(runtime.GOMAXPROCS(0))
+ loadingUnpruned sync.Map // module.Version → nil; the set of modules that have been or are being loaded via roots that do not support pruning
)
// loadOne synchronously loads the explicit requirements for module m.
// It does not load the transitive requirements of m even if the go version in
- // m's go.mod file indicates eager loading.
+ // m's go.mod file indicates that it supports graph pruning.
loadOne := func(m module.Version) (*modFileSummary, error) {
- cached := mg.loadCache.Do(m, func() interface{} {
+ cached := mg.loadCache.Do(m, func() any {
summary, err := goModSummary(m)
mu.Lock()
@@ -305,15 +343,15 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (
return cached.summary, cached.err
}
- var enqueue func(m module.Version, depth modDepth)
- enqueue = func(m module.Version, depth modDepth) {
+ var enqueue func(m module.Version, pruning modPruning)
+ enqueue = func(m module.Version, pruning modPruning) {
if m.Version == "none" {
return
}
- if depth == eager {
- if _, dup := loadingEager.LoadOrStore(m, nil); dup {
- // m has already been enqueued for loading. Since eager loading may
+ if pruning == unpruned {
+ if _, dup := loadingUnpruned.LoadOrStore(m, nil); dup {
+ // m has already been enqueued for loading. Since unpruned loading may
// follow cycles in the the requirement graph, we need to return early
// to avoid making the load queue infinitely long.
return
@@ -326,21 +364,25 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (
return // findError will report the error later.
}
- // If the version in m's go.mod file implies eager loading, then we cannot
- // assume that the explicit requirements of m (added by loadOne) are
- // sufficient to build the packages it contains. We must load its full
+ // If the version in m's go.mod file does not support pruning, then we
+ // cannot assume that the explicit requirements of m (added by loadOne)
+ // are sufficient to build the packages it contains. We must load its full
// transitive dependency graph to be sure that we see all relevant
// dependencies.
- if depth == eager || summary.depth == eager {
+ if pruning != pruned || summary.pruning == unpruned {
+ nextPruning := summary.pruning
+ if pruning == unpruned {
+ nextPruning = unpruned
+ }
for _, r := range summary.require {
- enqueue(r, eager)
+ enqueue(r, nextPruning)
}
}
})
}
for _, m := range roots {
- enqueue(m, depth)
+ enqueue(m, pruning)
}
<-loadQueue.Idle()
@@ -351,8 +393,7 @@ func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (
}
// RequiredBy returns the dependencies required by module m in the graph,
-// or ok=false if module m's dependencies are not relevant (such as if they
-// are pruned out by lazy loading).
+// or ok=false if module m's dependencies are pruned out.
//
// The caller must not modify the returned slice, but may safely append to it
// and may rely on it not to be modified.
@@ -404,7 +445,12 @@ func (mg *ModuleGraph) findError() error {
}
func (mg *ModuleGraph) allRootsSelected() bool {
- roots, _ := mg.g.RequiredBy(Target)
+ var roots []module.Version
+ if inWorkspaceMode() {
+ roots = MainModules.Versions()
+ } else {
+ roots, _ = mg.g.RequiredBy(MainModules.mustGetSingleMainModule())
+ }
for _, m := range roots {
if mg.Selected(m.Path) != m.Version {
return false
@@ -427,12 +473,12 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
rs := LoadModFile(ctx)
if goVersion != "" {
- depth := modDepthFromGoVersion(goVersion)
- if depth == eager && rs.depth != eager {
+ pruning := pruningForGoVersion(goVersion)
+ if pruning == unpruned && rs.pruning != unpruned {
// Use newRequirements instead of convertDepth because convertDepth
// also updates roots; here, we want to report the unmodified roots
// even though they may seem inconsistent.
- rs = newRequirements(eager, rs.rootModules, rs.direct)
+ rs = newRequirements(unpruned, rs.rootModules, rs.direct)
}
mg, err := rs.Graph(ctx)
@@ -447,7 +493,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
base.Fatalf("go: %v", err)
}
- commitRequirements(ctx, modFileGoVersion(), rs)
+ requirements = rs
+
return mg
}
@@ -455,9 +502,8 @@ func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
//
// If the complete graph reveals that some root of rs is not actually the
// selected version of its path, expandGraph computes a new set of roots that
-// are consistent. (When lazy loading is implemented, this may result in
-// upgrades to other modules due to requirements that were previously pruned
-// out.)
+// are consistent. (With a pruned module graph, this may result in upgrades to
+// other modules due to requirements that were previously pruned out.)
//
// expandGraph returns the updated roots, along with the module graph loaded
// from those roots and any error encountered while loading that graph.
@@ -473,9 +519,9 @@ func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleG
if !mg.allRootsSelected() {
// The roots of rs are not consistent with the rest of the graph. Update
- // them. In an eager module this is a no-op for the build list as a whole —
+ // them. In an unpruned module this is a no-op for the build list as a whole —
// it just promotes what were previously transitive requirements to be
- // roots — but in a lazy module it may pull in previously-irrelevant
+ // roots — but in a pruned module it may pull in previously-irrelevant
// transitive dependencies.
newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
@@ -513,7 +559,7 @@ func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (chang
if err != nil {
return false, err
}
- commitRequirements(ctx, modFileGoVersion(), rs)
+ requirements = rs
return changed, err
}
@@ -544,23 +590,45 @@ type Conflict struct {
// tidyRoots trims the root dependencies to the minimal requirements needed to
// both retain the same versions of all packages in pkgs and satisfy the
-// lazy loading invariants (if applicable).
+// graph-pruning invariants (if applicable).
func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Requirements, error) {
- if rs.depth == eager {
- return tidyEagerRoots(ctx, rs.direct, pkgs)
+ mainModule := MainModules.mustGetSingleMainModule()
+ if rs.pruning == unpruned {
+ return tidyUnprunedRoots(ctx, mainModule, rs.direct, pkgs)
}
- return tidyLazyRoots(ctx, rs.direct, pkgs)
+ return tidyPrunedRoots(ctx, mainModule, rs.direct, pkgs)
}
func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
- if rs.depth == eager {
- return updateEagerRoots(ctx, direct, rs, add)
+ switch rs.pruning {
+ case unpruned:
+ return updateUnprunedRoots(ctx, direct, rs, add)
+ case pruned:
+ return updatePrunedRoots(ctx, direct, rs, pkgs, add, rootsImported)
+ case workspace:
+ return updateWorkspaceRoots(ctx, rs, add)
+ default:
+ panic(fmt.Sprintf("unsupported pruning mode: %v", rs.pruning))
+ }
+}
+
+func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Version) (*Requirements, error) {
+ if len(add) != 0 {
+ // add should be empty in workspace mode because workspace mode implies
+ // -mod=readonly, which in turn implies no new requirements. The code path
+ // that would result in add being non-empty returns an error before it
+ // reaches this point: The set of modules to add comes from
+ // resolveMissingImports, which in turn resolves each package by calling
+ // queryImport. But queryImport explicitly checks for -mod=readonly, and
+ // return an error.
+ panic("add is not empty")
}
- return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported)
+ return rs, nil
}
-// tidyLazyRoots returns a minimal set of root requirements that maintains the
-// "lazy loading" invariants of the go.mod file for the given packages:
+// tidyPrunedRoots returns a minimal set of root requirements that maintains the
+// invariants of the go.mod file needed to support graph pruning for the given
+// packages:
//
// 1. For each package marked with pkgInAll, the module path that provided that
// package is included as a root.
@@ -568,16 +636,16 @@ func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements,
// selected at the same version or is upgraded by the dependencies of a
// root.
//
-// If any module that provided a package has been upgraded above its previous,
+// If any module that provided a package has been upgraded above its previous
// version, the caller may need to reload and recompute the package graph.
//
// To ensure that the loading process eventually converges, the caller should
// add any needed roots from the tidy root set (without removing existing untidy
// roots) until the set of roots has converged.
-func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
+func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
var (
roots []module.Version
- pathIncluded = map[string]bool{Target.Path: true}
+ pathIncluded = map[string]bool{mainModule.Path: true}
)
// We start by adding roots for every package in "all".
//
@@ -605,7 +673,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
queued[pkg] = true
}
module.Sort(roots)
- tidy := newRequirements(lazy, roots, direct)
+ tidy := newRequirements(pruned, roots, direct)
for len(queue) > 0 {
roots = tidy.rootModules
@@ -641,7 +709,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
if len(roots) > len(tidy.rootModules) {
module.Sort(roots)
- tidy = newRequirements(lazy, roots, tidy.direct)
+ tidy = newRequirements(pruned, roots, tidy.direct)
}
}
@@ -652,8 +720,8 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
return tidy, nil
}
-// updateLazyRoots returns a set of root requirements that maintains the “lazy
-// loading” invariants of the go.mod file:
+// updatePrunedRoots returns a set of root requirements that maintains the
+// invariants of the go.mod file needed to support graph pruning:
//
// 1. The selected version of the module providing each package marked with
// either pkgInAll or pkgIsRoot is included as a root.
@@ -670,7 +738,7 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
// The packages in pkgs are assumed to have been loaded from either the roots of
// rs or the modules selected in the graph of rs.
//
-// The above invariants together imply the “lazy loading” invariants for the
+// The above invariants together imply the graph-pruning invariants for the
// go.mod file:
//
// 1. (The import invariant.) Every module that provides a package transitively
@@ -690,13 +758,13 @@ func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg)
// it requires explicitly. This invariant is left up to the caller, who must
// not load packages from outside the module graph but may add roots to the
// graph, but is facilited by (3). If the caller adds roots to the graph in
-// order to resolve missing packages, then updateLazyRoots will retain them,
+// order to resolve missing packages, then updatePrunedRoots will retain them,
// the selected versions of those roots cannot regress, and they will
// eventually be written back to the main module's go.mod file.
//
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
// detail.)
-func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
+func updatePrunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
roots := rs.rootModules
rootsUpgraded := false
@@ -717,11 +785,11 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
// pkg is transitively imported by a package or test in the main module.
// We need to promote the module that maintains it to a root: if some
// other module depends on the main module, and that other module also
- // uses lazy loading, it will expect to find all of our transitive
- // dependencies by reading just our go.mod file, not the go.mod files of
- // everything we depend on.
+ // uses a pruned module graph, it will expect to find all of our
+ // transitive dependencies by reading just our go.mod file, not the go.mod
+ // files of everything we depend on.
//
- // (This is the “import invariant” that makes lazy loading possible.)
+ // (This is the “import invariant” that makes graph pruning possible.)
case rootsImported && pkg.flags.has(pkgFromRoot):
// pkg is a transitive dependency of some root, and we are treating the
@@ -732,17 +800,18 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
// it matches a command-line argument.) We want future invocations of the
// 'go' command — such as 'go test' on the same package — to continue to
// use the same versions of its dependencies that we are using right now.
- // So we need to bring this package's dependencies inside the lazy-loading
- // horizon.
+ // So we need to bring this package's dependencies inside the pruned
+ // module graph.
//
// Making the module containing this package a root of the module graph
- // does exactly that: if the module containing the package is lazy it
- // should satisfy the import invariant itself, so all of its dependencies
- // should be in its go.mod file, and if the module containing the package
- // is eager then if we make it a root we will load all of its transitive
- // dependencies into the module graph.
+ // does exactly that: if the module containing the package supports graph
+ // pruning then it should satisfy the import invariant itself, so all of
+ // its dependencies should be in its go.mod file, and if the module
+ // containing the package does not support pruning then if we make it a
+ // root we will load all of its (unpruned) transitive dependencies into
+ // the module graph.
//
- // (This is the “argument invariant” of lazy loading, and is important for
+ // (This is the “argument invariant”, and is important for
// reproducibility.)
default:
@@ -807,16 +876,15 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
// We've added or upgraded one or more roots, so load the full module
// graph so that we can update those roots to be consistent with other
// requirements.
- if cfg.BuildMod != "mod" {
+ if mustHaveCompleteRequirements() {
// Our changes to the roots may have moved dependencies into or out of
- // the lazy-loading horizon, which could in turn change the selected
- // versions of other modules. (Unlike for eager modules, for lazy
- // modules adding or removing an explicit root is a semantic change, not
- // just a cosmetic one.)
+ // the graph-pruning horizon, which could in turn change the selected
+ // versions of other modules. (For pruned modules adding or removing an
+ // explicit root is a semantic change, not just a cosmetic one.)
return rs, errGoModDirty
}
- rs = newRequirements(lazy, roots, direct)
+ rs = newRequirements(pruned, roots, direct)
var err error
mg, err = rs.Graph(ctx)
if err != nil {
@@ -831,7 +899,7 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
if rs.graph.Load() != nil {
// We've already loaded the full module graph, which includes the
// requirements of all of the root modules — even the transitive
- // requirements, if they are eager!
+ // requirements, if they are unpruned!
mg, _ = rs.Graph(ctx)
} else if cfg.BuildMod == "vendor" {
// We can't spot-check the requirements of other modules because we
@@ -855,7 +923,9 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
roots = make([]module.Version, 0, len(rs.rootModules))
rootsUpgraded = false
inRootPaths := make(map[string]bool, len(rs.rootModules)+1)
- inRootPaths[Target.Path] = true
+ for _, mm := range MainModules.Versions() {
+ inRootPaths[mm.Path] = true
+ }
for _, m := range rs.rootModules {
if inRootPaths[m.Path] {
// This root specifies a redundant path. We already retained the
@@ -908,12 +978,12 @@ func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requiremen
}
}
- if rs.depth == lazy && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
- // The root set is unchanged and rs was already lazy, so keep rs to
+ if rs.pruning == pruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
+ // The root set is unchanged and rs was already pruned, so keep rs to
// preserve its cached ModuleGraph (if any).
return rs, nil
}
- return newRequirements(lazy, roots, direct), nil
+ return newRequirements(pruned, roots, direct), nil
}
// spotCheckRoots reports whether the versions of the roots in rs satisfy the
@@ -955,17 +1025,37 @@ func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Versi
return true
}
-// tidyEagerRoots returns a minimal set of root requirements that maintains the
-// selected version of every module that provided a package in pkgs, and
-// includes the selected version of every such module in direct as a root.
-func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
+// tidyUnprunedRoots returns a minimal set of root requirements that maintains
+// the selected version of every module that provided or lexically could have
+// provided a package in pkgs, and includes the selected version of every such
+// module in direct as a root.
+func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
var (
+ // keep is a set of of modules that provide packages or are needed to
+ // disambiguate imports.
keep []module.Version
keptPath = map[string]bool{}
- )
- var (
- rootPaths []string // module paths that should be included as roots
+
+ // rootPaths is a list of module paths that provide packages directly
+ // imported from the main module. They should be included as roots.
+ rootPaths []string
inRootPaths = map[string]bool{}
+
+ // altMods is a set of paths of modules that lexically could have provided
+ // imported packages. It may be okay to remove these from the list of
+ // explicit requirements if that removes them from the module graph. If they
+ // are present in the module graph reachable from rootPaths, they must not
+ // be at a lower version. That could cause a missing sum error or a new
+ // import ambiguity.
+ //
+ // For example, suppose a developer rewrites imports from example.com/m to
+ // example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the
+ // requirement on example.com/m if there is no other transitive requirement
+ // on it. However, if example.com/m were downgraded to a version not in
+ // go.sum, when package example.com/m/v2/p is loaded, we'd get an error
+ // trying to disambiguate the import, since we can't check example.com/m
+ // without its sum. See #47738.
+ altMods = map[string]string{}
)
for _, pkg := range pkgs {
if !pkg.fromExternalModule() {
@@ -979,16 +1069,48 @@ func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg
inRootPaths[m.Path] = true
}
}
+ for _, m := range pkg.altMods {
+ altMods[m.Path] = m.Version
+ }
}
- min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep})
+ // Construct a build list with a minimal set of roots.
+ // This may remove or downgrade modules in altMods.
+ reqs := &mvsReqs{roots: keep}
+ min, err := mvs.Req(mainModule, rootPaths, reqs)
if err != nil {
return nil, err
}
- return newRequirements(eager, min, direct), nil
+ buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs)
+ if err != nil {
+ return nil, err
+ }
+
+ // Check if modules in altMods were downgraded but not removed.
+ // If so, add them to roots, which will retain an "// indirect" requirement
+ // in go.mod. See comment on altMods above.
+ keptAltMod := false
+ for _, m := range buildList {
+ if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 {
+ keep = append(keep, module.Version{Path: m.Path, Version: v})
+ keptAltMod = true
+ }
+ }
+ if keptAltMod {
+ // We must run mvs.Req again instead of simply adding altMods to min.
+ // It's possible that a requirement in altMods makes some other
+ // explicit indirect requirement unnecessary.
+ reqs.roots = keep
+ min, err = mvs.Req(mainModule, rootPaths, reqs)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return newRequirements(unpruned, min, direct), nil
}
-// updateEagerRoots returns a set of root requirements that includes the selected
+// updateUnprunedRoots returns a set of root requirements that includes the selected
// version of every module path in direct as a root, and maintains the selected
// version of every module selected in the graph of rs.
//
@@ -1002,7 +1124,7 @@ func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg
// by a dependency in add.
// 4. Every version in add is selected at its given version unless upgraded by
// (the dependencies of) an existing root or another module in add.
-func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
+func updateUnprunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
mg, err := rs.Graph(ctx)
if err != nil {
// We can't ignore errors in the module graph even if the user passed the -e
@@ -1011,7 +1133,7 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme
return rs, err
}
- if cfg.BuildMod != "mod" {
+ if mustHaveCompleteRequirements() {
// Instead of actually updating the requirements, just check that no updates
// are needed.
if rs == nil {
@@ -1067,10 +1189,10 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme
// “The selected version of every module path in direct is included as a root.”
//
- // This is only for convenience and clarity for end users: in an eager module,
+ // This is only for convenience and clarity for end users: in an unpruned module,
// the choice of explicit vs. implicit dependency has no impact on MVS
// selection (for itself or any other module).
- keep := append(mg.BuildList()[1:], add...)
+ keep := append(mg.BuildList()[MainModules.Len():], add...)
for _, m := range keep {
if direct[m.Path] && !inRootPaths[m.Path] {
rootPaths = append(rootPaths, m.Path)
@@ -1078,44 +1200,53 @@ func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requireme
}
}
- min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep})
- if err != nil {
- return rs, err
+ var roots []module.Version
+ for _, mainModule := range MainModules.Versions() {
+ min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep})
+ if err != nil {
+ return rs, err
+ }
+ roots = append(roots, min...)
}
- if rs.depth == eager && reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
- // The root set is unchanged and rs was already eager, so keep rs to
+ if MainModules.Len() > 1 {
+ module.Sort(roots)
+ }
+ if rs.pruning == unpruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
+ // The root set is unchanged and rs was already unpruned, so keep rs to
// preserve its cached ModuleGraph (if any).
return rs, nil
}
- return newRequirements(eager, min, direct), nil
+
+ return newRequirements(unpruned, roots, direct), nil
}
-// convertDepth returns a version of rs with the given depth.
-// If rs already has the given depth, convertDepth returns rs unmodified.
-func convertDepth(ctx context.Context, rs *Requirements, depth modDepth) (*Requirements, error) {
- if rs.depth == depth {
+// convertPruning returns a version of rs with the given pruning behavior.
+// If rs already has the given pruning, convertPruning returns rs unmodified.
+func convertPruning(ctx context.Context, rs *Requirements, pruning modPruning) (*Requirements, error) {
+ if rs.pruning == pruning {
return rs, nil
+ } else if rs.pruning == workspace || pruning == workspace {
+ panic("attempthing to convert to/from workspace pruning and another pruning type")
}
- if depth == eager {
- // We are converting a lazy module to an eager one. The roots of an eager
- // module graph are a superset of the roots of a lazy graph, so we don't
- // need to add any new roots — we just need to prune away the ones that are
- // redundant given eager loading, which is exactly what updateEagerRoots
- // does.
- return updateEagerRoots(ctx, rs.direct, rs, nil)
+ if pruning == unpruned {
+ // We are converting a pruned module to an unpruned one. The roots of a
+ // ppruned module graph are a superset of the roots of an unpruned one, so
+ // we don't need to add any new roots — we just need to drop the ones that
+ // are redundant, which is exactly what updateUnprunedRoots does.
+ return updateUnprunedRoots(ctx, rs.direct, rs, nil)
}
- // We are converting an eager module to a lazy one. The module graph of an
- // eager module includes the transitive dependencies of every module in the
- // build list.
+ // We are converting an unpruned module to a pruned one.
//
- // Hey, we can express that as a lazy root set! “Include the transitive
- // dependencies of every module in the build list” is exactly what happens in
- // a lazy module if we promote every module in the build list to a root!
+ // An unpruned module graph includes the transitive dependencies of every
+ // module in the build list. As it turns out, we can express that as a pruned
+ // root set! “Include the transitive dependencies of every module in the build
+ // list” is exactly what happens in a pruned module if we promote every module
+ // in the build list to a root.
mg, err := rs.Graph(ctx)
if err != nil {
return rs, err
}
- return newRequirements(lazy, mg.BuildList()[1:], rs.direct), nil
+ return newRequirements(pruned, mg.BuildList()[MainModules.Len():], rs.direct), nil
}
diff --git a/src/cmd/go/internal/modload/edit.go b/src/cmd/go/internal/modload/edit.go
index c350b9d1b5c571ed0e0754a07ad58fa3c9b26fe9..0f37e3b2e94773e983fc70f161ea2c768d759833 100644
--- a/src/cmd/go/internal/modload/edit.go
+++ b/src/cmd/go/internal/modload/edit.go
@@ -21,7 +21,7 @@ import (
// 2. Each module version in tryUpgrade is upgraded toward the indicated
// version as far as can be done without violating (1).
//
-// 3. Each module version in rs.rootModules (or rs.graph, if rs.depth is eager)
+// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned)
// is downgraded from its original version only to the extent needed to
// satisfy (1), or upgraded only to the extent needed to satisfy (1) and
// (2).
@@ -69,13 +69,14 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
}
var roots []module.Version
- if rs.depth == eager {
- // In an eager module, modules that provide packages imported by the main
- // module may either be explicit roots or implicit transitive dependencies.
- // We promote the modules in mustSelect to be explicit requirements.
+ if rs.pruning == unpruned {
+ // In a module without graph pruning, modules that provide packages imported
+ // by the main module may either be explicit roots or implicit transitive
+ // dependencies. We promote the modules in mustSelect to be explicit
+ // requirements.
var rootPaths []string
for _, m := range mustSelect {
- if m.Version != "none" && m.Path != Target.Path {
+ if m.Version != "none" && !MainModules.Contains(m.Path) {
rootPaths = append(rootPaths, m.Path)
}
}
@@ -97,13 +98,13 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
}
}
- roots, err = mvs.Req(Target, rootPaths, &mvsReqs{roots: mods})
+ roots, err = mvs.Req(MainModules.mustGetSingleMainModule(), rootPaths, &mvsReqs{roots: mods})
if err != nil {
return nil, false, err
}
} else {
- // In a lazy module, every module that provides a package imported by the
- // main module must be retained as a root.
+ // In a module with a pruned graph, every module that provides a package
+ // imported by the main module must be retained as a root.
roots = mods
if !changed {
// Because the roots we just computed are unchanged, the entire graph must
@@ -126,7 +127,7 @@ func editRequirements(ctx context.Context, rs *Requirements, tryUpgrade, mustSel
direct[m.Path] = true
}
}
- return newRequirements(rs.depth, roots, direct), changed, nil
+ return newRequirements(rs.pruning, roots, direct), changed, nil
}
// limiterForEdit returns a versionLimiter with its max versions set such that
@@ -149,11 +150,12 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
}
}
- if rs.depth == eager {
- // Eager go.mod files don't indicate which transitive dependencies are
- // actually relevant to the main module, so we have to assume that any module
- // that could have provided any package — that is, any module whose selected
- // version was not "none" — may be relevant.
+ if rs.pruning == unpruned {
+ // go.mod files that do not support graph pruning don't indicate which
+ // transitive dependencies are actually relevant to the main module, so we
+ // have to assume that any module that could have provided any package —
+ // that is, any module whose selected version was not "none" — may be
+ // relevant.
for _, m := range mg.BuildList() {
restrictTo(m)
}
@@ -175,7 +177,7 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
}
}
- if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.depth, tryUpgrade, mustSelect); err != nil {
+ if err := raiseLimitsForUpgrades(ctx, maxVersion, rs.pruning, tryUpgrade, mustSelect); err != nil {
return nil, err
}
@@ -185,22 +187,22 @@ func limiterForEdit(ctx context.Context, rs *Requirements, tryUpgrade, mustSelec
restrictTo(m)
}
- return newVersionLimiter(rs.depth, maxVersion), nil
+ return newVersionLimiter(rs.pruning, maxVersion), nil
}
// raiseLimitsForUpgrades increases the module versions in maxVersions to the
// versions that would be needed to allow each of the modules in tryUpgrade
-// (individually) and all of the modules in mustSelect (simultaneously) to be
-// added as roots.
+// (individually or in any combination) and all of the modules in mustSelect
+// (simultaneously) to be added as roots.
//
// Versions not present in maxVersion are unrestricted, and it is assumed that
// they will not be promoted to root requirements (and thus will not contribute
-// their own dependencies if the main module is lazy).
+// their own dependencies if the main module supports graph pruning).
//
// These limits provide an upper bound on how far a module may be upgraded as
// part of an incidental downgrade, if downgrades are needed in order to select
// the versions in mustSelect.
-func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, depth modDepth, tryUpgrade []module.Version, mustSelect []module.Version) error {
+func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, pruning modPruning, tryUpgrade []module.Version, mustSelect []module.Version) error {
// allow raises the limit for m.Path to at least m.Version.
// If m.Path was already unrestricted, it remains unrestricted.
allow := func(m module.Version) {
@@ -213,51 +215,88 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
}
}
- var eagerUpgrades []module.Version
- if depth == eager {
- eagerUpgrades = tryUpgrade
+ var (
+ unprunedUpgrades []module.Version
+ isPrunedRootPath map[string]bool
+ )
+ if pruning == unpruned {
+ unprunedUpgrades = tryUpgrade
} else {
+ isPrunedRootPath = make(map[string]bool, len(maxVersion))
+ for p := range maxVersion {
+ isPrunedRootPath[p] = true
+ }
for _, m := range tryUpgrade {
- if m.Path == Target.Path {
- // Target is already considered to be higher than any possible m, so we
- // won't be upgrading to it anyway and there is no point scanning its
- // dependencies.
- continue
+ isPrunedRootPath[m.Path] = true
+ }
+ for _, m := range mustSelect {
+ isPrunedRootPath[m.Path] = true
+ }
+
+ allowedRoot := map[module.Version]bool{}
+
+ var allowRoot func(m module.Version) error
+ allowRoot = func(m module.Version) error {
+ if allowedRoot[m] {
+ return nil
+ }
+ allowedRoot[m] = true
+
+ if MainModules.Contains(m.Path) {
+ // The main module versions are already considered to be higher than any
+ // possible m, so m cannot be selected as a root and there is no point
+ // scanning its dependencies.
+ return nil
}
+ allow(m)
+
summary, err := goModSummary(m)
if err != nil {
return err
}
- if summary.depth == eager {
- // For efficiency, we'll load all of the eager upgrades as one big
+ if summary.pruning == unpruned {
+ // For efficiency, we'll load all of the unpruned upgrades as one big
// graph, rather than loading the (potentially-overlapping) subgraph for
// each upgrade individually.
- eagerUpgrades = append(eagerUpgrades, m)
- continue
+ unprunedUpgrades = append(unprunedUpgrades, m)
+ return nil
}
-
for _, r := range summary.require {
- allow(r)
+ if isPrunedRootPath[r.Path] {
+ // r could become a root as the result of an upgrade or downgrade,
+ // in which case its dependencies will not be pruned out.
+ // We need to allow those dependencies to be upgraded too.
+ if err := allowRoot(r); err != nil {
+ return err
+ }
+ } else {
+ // r will not become a root, so its dependencies don't matter.
+ // Allow only r itself.
+ allow(r)
+ }
}
+ return nil
+ }
+
+ for _, m := range tryUpgrade {
+ allowRoot(m)
}
}
- if len(eagerUpgrades) > 0 {
- // Compute the max versions for eager upgrades all together.
- // Since these modules are eager, we'll end up scanning all of their
+ if len(unprunedUpgrades) > 0 {
+ // Compute the max versions for unpruned upgrades all together.
+ // Since these modules are unpruned, we'll end up scanning all of their
// transitive dependencies no matter which versions end up selected,
// and since we have a large dependency graph to scan we might get
// a significant benefit from not revisiting dependencies that are at
// common versions among multiple upgrades.
- upgradeGraph, err := readModGraph(ctx, eager, eagerUpgrades)
+ upgradeGraph, err := readModGraph(ctx, unpruned, unprunedUpgrades)
if err != nil {
- if go117LazyTODO {
- // Compute the requirement path from a module path in tryUpgrade to the
- // error, and the requirement path (if any) from rs.rootModules to the
- // tryUpgrade module path. Return a *mvs.BuildListError showing the
- // concatenation of the paths (with an upgrade in the middle).
- }
+ // Compute the requirement path from a module path in tryUpgrade to the
+ // error, and the requirement path (if any) from rs.rootModules to the
+ // tryUpgrade module path. Return a *mvs.BuildListError showing the
+ // concatenation of the paths (with an upgrade in the middle).
return err
}
@@ -268,16 +307,41 @@ func raiseLimitsForUpgrades(ctx context.Context, maxVersion map[string]string, d
}
}
- if len(mustSelect) > 0 {
- mustGraph, err := readModGraph(ctx, depth, mustSelect)
+ // Explicitly allow any (transitive) upgrades implied by mustSelect.
+ nextRoots := append([]module.Version(nil), mustSelect...)
+ for nextRoots != nil {
+ module.Sort(nextRoots)
+ rs := newRequirements(pruning, nextRoots, nil)
+ nextRoots = nil
+
+ rs, mustGraph, err := expandGraph(ctx, rs)
if err != nil {
return err
}
for _, r := range mustGraph.BuildList() {
- // Some module in mustSelect requires r, so we must allow at least r.Version
- // unless it conflicts with an entry in mustSelect.
+ // Some module in mustSelect requires r, so we must allow at least
+ // r.Version (unless it conflicts with another entry in mustSelect, in
+ // which case we will error out either way).
allow(r)
+
+ if isPrunedRootPath[r.Path] {
+ if v, ok := rs.rootSelected(r.Path); ok && r.Version == v {
+ // r is already a root, so its requirements are already included in
+ // the build list.
+ continue
+ }
+
+ // The dependencies in mustSelect may upgrade (or downgrade) an existing
+ // root to match r, which will remain as a root. However, since r is not
+ // a root of rs, its dependencies have been pruned out of this build
+ // list. We need to add it back explicitly so that we allow any
+ // transitive upgrades that r will pull in.
+ if nextRoots == nil {
+ nextRoots = rs.rootModules // already capped
+ }
+ nextRoots = append(nextRoots, r)
+ }
}
}
@@ -301,12 +365,12 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
}
var initial []module.Version
- if rs.depth == eager {
+ if rs.pruning == unpruned {
mg, err := rs.Graph(ctx)
if err != nil {
return nil, false, err
}
- initial = mg.BuildList()[1:]
+ initial = mg.BuildList()[MainModules.Len():]
} else {
initial = rs.rootModules
}
@@ -318,7 +382,7 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
mods = make([]module.Version, 0, len(limiter.selected))
for path, v := range limiter.selected {
- if v != "none" && path != Target.Path {
+ if v != "none" && !MainModules.Contains(path) {
mods = append(mods, module.Version{Path: path, Version: v})
}
}
@@ -328,13 +392,13 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
// downgraded module may require a higher (but still allowed) version of
// another. The lower version may require extraneous dependencies that aren't
// actually relevant, so we need to compute the actual selected versions.
- mg, err := readModGraph(ctx, rs.depth, mods)
+ mg, err := readModGraph(ctx, rs.pruning, mods)
if err != nil {
return nil, false, err
}
mods = make([]module.Version, 0, len(limiter.selected))
for path, _ := range limiter.selected {
- if path != Target.Path {
+ if !MainModules.Contains(path) {
if v := mg.Selected(path); v != "none" {
mods = append(mods, module.Version{Path: path, Version: v})
}
@@ -350,16 +414,16 @@ func selectPotentiallyImportedModules(ctx context.Context, limiter *versionLimit
// A versionLimiter tracks the versions that may be selected for each module
// subject to constraints on the maximum versions of transitive dependencies.
type versionLimiter struct {
- // depth is the depth at which the dependencies of the modules passed to
+ // pruning is the pruning at which the dependencies of the modules passed to
// Select and UpgradeToward are loaded.
- depth modDepth
+ pruning modPruning
// max maps each module path to the maximum version that may be selected for
// that path.
//
// Paths with no entry are unrestricted, and we assume that they will not be
// promoted to root dependencies (so will not contribute dependencies if the
- // main module is lazy).
+ // main module supports graph pruning).
max map[string]string
// selected maps each module path to a version of that path (if known) whose
@@ -411,14 +475,18 @@ func (dq dqState) isDisqualified() bool {
// in the map are unrestricted. The limiter assumes that unrestricted paths will
// not be promoted to root dependencies.
//
-// If depth is lazy, then if a module passed to UpgradeToward or Select is
-// itself lazy, its unrestricted dependencies are skipped when scanning
-// requirements.
-func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter {
+// If module graph pruning is in effect, then if a module passed to
+// UpgradeToward or Select supports pruning, its unrestricted dependencies are
+// skipped when scanning requirements.
+func newVersionLimiter(pruning modPruning, max map[string]string) *versionLimiter {
+ selected := make(map[string]string)
+ for _, m := range MainModules.Versions() {
+ selected[m.Path] = m.Version
+ }
return &versionLimiter{
- depth: depth,
+ pruning: pruning,
max: max,
- selected: map[string]string{Target.Path: Target.Version},
+ selected: selected,
dqReason: map[module.Version]dqState{},
requiring: map[module.Version][]module.Version{},
}
@@ -427,8 +495,8 @@ func newVersionLimiter(depth modDepth, max map[string]string) *versionLimiter {
// UpgradeToward attempts to upgrade the selected version of m.Path as close as
// possible to m.Version without violating l's maximum version limits.
//
-// If depth is lazy and m itself is lazy, the the dependencies of unrestricted
-// dependencies of m will not be followed.
+// If module graph pruning is in effect and m itself supports pruning, the
+// dependencies of unrestricted dependencies of m will not be followed.
func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) error {
selected, ok := l.selected[m.Path]
if ok {
@@ -440,7 +508,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
selected = "none"
}
- if l.check(m, l.depth).isDisqualified() {
+ if l.check(m, l.pruning).isDisqualified() {
candidates, err := versions(ctx, m.Path, CheckAllowed)
if err != nil {
// This is likely a transient error reaching the repository,
@@ -457,7 +525,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
})
candidates = candidates[:i]
- for l.check(m, l.depth).isDisqualified() {
+ for l.check(m, l.pruning).isDisqualified() {
n := len(candidates)
if n == 0 || cmpVersion(selected, candidates[n-1]) >= 0 {
// We couldn't find a suitable candidate above the already-selected version.
@@ -474,7 +542,7 @@ func (l *versionLimiter) UpgradeToward(ctx context.Context, m module.Version) er
// Select attempts to set the selected version of m.Path to exactly m.Version.
func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err error) {
- dq := l.check(m, l.depth)
+ dq := l.check(m, l.pruning)
if !dq.isDisqualified() {
l.selected[m.Path] = m.Version
}
@@ -484,15 +552,15 @@ func (l *versionLimiter) Select(m module.Version) (conflict module.Version, err
// check determines whether m (or its transitive dependencies) would violate l's
// maximum version limits if added to the module requirement graph.
//
-// If depth is lazy and m itself is lazy, then the dependencies of unrestricted
-// dependencies of m will not be followed. If the lazy loading invariants hold
-// for the main module up to this point, the packages in those modules are at
-// best only imported by tests of dependencies that are themselves loaded from
-// outside modules. Although we would like to keep 'go test all' as reproducible
-// as is feasible, we don't want to retain test dependencies that are only
-// marginally relevant at best.
-func (l *versionLimiter) check(m module.Version, depth modDepth) dqState {
- if m.Version == "none" || m == Target {
+// If pruning is in effect and m itself supports graph pruning, the dependencies
+// of unrestricted dependencies of m will not be followed. If the graph-pruning
+// invariants hold for the main module up to this point, the packages in those
+// modules are at best only imported by tests of dependencies that are
+// themselves loaded from outside modules. Although we would like to keep
+// 'go test all' as reproducible as is feasible, we don't want to retain test
+// dependencies that are only marginally relevant at best.
+func (l *versionLimiter) check(m module.Version, pruning modPruning) dqState {
+ if m.Version == "none" || m == MainModules.mustGetSingleMainModule() {
// version "none" has no requirements, and the dependencies of Target are
// tautological.
return dqState{}
@@ -522,20 +590,20 @@ func (l *versionLimiter) check(m module.Version, depth modDepth) dqState {
return l.disqualify(m, dqState{err: err})
}
- if summary.depth == eager {
- depth = eager
+ if summary.pruning == unpruned {
+ pruning = unpruned
}
for _, r := range summary.require {
- if depth == lazy {
+ if pruning == pruned {
if _, restricted := l.max[r.Path]; !restricted {
// r.Path is unrestricted, so we don't care at what version it is
// selected. We assume that r.Path will not become a root dependency, so
- // since m is lazy, r's dependencies won't be followed.
+ // since m supports pruning, r's dependencies won't be followed.
continue
}
}
- if dq := l.check(r, depth); dq.isDisqualified() {
+ if dq := l.check(r, pruning); dq.isDisqualified() {
return l.disqualify(m, dq)
}
diff --git a/src/cmd/go/internal/modload/import.go b/src/cmd/go/internal/modload/import.go
index d2bbe5cbe0b1eae58ac5a041792d0b4d0e8a3c85..812e48a1568f16e62ac33f07a90f8e4d9d8595e7 100644
--- a/src/cmd/go/internal/modload/import.go
+++ b/src/cmd/go/internal/modload/import.go
@@ -32,6 +32,8 @@ type ImportMissingError struct {
Module module.Version
QueryErr error
+ ImportingMainModule module.Version
+
// isStd indicates whether we would expect to find the package in the standard
// library. This is normally true for all dotless import paths, but replace
// directives can cause us to treat the replaced paths as also being in
@@ -71,6 +73,9 @@ func (e *ImportMissingError) Error() string {
if e.QueryErr != nil {
return fmt.Sprintf("%s: %v", message, e.QueryErr)
}
+ if e.ImportingMainModule.Path != "" && e.ImportingMainModule != MainModules.ModContainingCWD() {
+ return fmt.Sprintf("%s; to add it:\n\tcd %s\n\tgo get %s", message, MainModules.ModRoot(e.ImportingMainModule), e.Path)
+ }
return fmt.Sprintf("%s; to add it:\n\tgo get %s", message, e.Path)
}
@@ -238,55 +243,63 @@ func (e *invalidImportError) Unwrap() error {
//
// If the package is not present in any module selected from the requirement
// graph, importFromModules returns an *ImportMissingError.
-func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) {
+//
+// If the package is present in exactly one module, importFromModules will
+// return the module, its root directory, and a list of other modules that
+// lexically could have provided the package but did not.
+func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) {
if strings.Contains(path, "@") {
- return module.Version{}, "", fmt.Errorf("import path should not have @version")
+ return module.Version{}, "", nil, fmt.Errorf("import path should not have @version")
}
if build.IsLocalImport(path) {
- return module.Version{}, "", fmt.Errorf("relative import not supported")
+ return module.Version{}, "", nil, fmt.Errorf("relative import not supported")
}
if path == "C" {
// There's no directory for import "C".
- return module.Version{}, "", nil
+ return module.Version{}, "", nil, nil
}
// Before any further lookup, check that the path is valid.
if err := module.CheckImportPath(path); err != nil {
- return module.Version{}, "", &invalidImportError{importPath: path, err: err}
+ return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err}
}
// Is the package in the standard library?
pathIsStd := search.IsStandardImportPath(path)
if pathIsStd && goroot.IsStandardPackage(cfg.GOROOT, cfg.BuildContext.Compiler, path) {
- if targetInGorootSrc {
- if dir, ok, err := dirInModule(path, targetPrefix, ModRoot(), true); err != nil {
- return module.Version{}, dir, err
- } else if ok {
- return Target, dir, nil
+ for _, mainModule := range MainModules.Versions() {
+ if MainModules.InGorootSrc(mainModule) {
+ if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil {
+ return module.Version{}, dir, nil, err
+ } else if ok {
+ return mainModule, dir, nil, nil
+ }
}
}
dir := filepath.Join(cfg.GOROOT, "src", path)
- return module.Version{}, dir, nil
+ return module.Version{}, dir, nil, nil
}
// -mod=vendor is special.
// Everything must be in the main module or the main module's vendor directory.
if cfg.BuildMod == "vendor" {
- mainDir, mainOK, mainErr := dirInModule(path, targetPrefix, ModRoot(), true)
- vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(ModRoot(), "vendor"), false)
+ mainModule := MainModules.mustGetSingleMainModule()
+ modRoot := MainModules.ModRoot(mainModule)
+ mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true)
+ vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false)
if mainOK && vendorOK {
- return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
+ return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
}
// Prefer to return main directory if there is one,
// Note that we're not checking that the package exists.
// We'll leave that for load.
if !vendorOK && mainDir != "" {
- return Target, mainDir, nil
+ return mainModule, mainDir, nil, nil
}
if mainErr != nil {
- return module.Version{}, "", mainErr
+ return module.Version{}, "", nil, mainErr
}
- readVendorList()
- return vendorPkgModule[path], vendorDir, nil
+ readVendorList(mainModule)
+ return vendorPkgModule[path], vendorDir, nil, nil
}
// Check each module on the build list.
@@ -307,7 +320,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
// already non-nil, then we attempt to load the package using the full
// requirements in mg.
for {
- var sumErrMods []module.Version
+ var sumErrMods, altMods []module.Version
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
var (
v string
@@ -341,13 +354,15 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
// continue the loop and find the package in some other module,
// we need to look at this module to make sure the import is
// not ambiguous.
- return module.Version{}, "", err
+ return module.Version{}, "", nil, err
}
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
- return module.Version{}, "", err
+ return module.Version{}, "", nil, err
} else if ok {
mods = append(mods, m)
dirs = append(dirs, dir)
+ } else {
+ altMods = append(altMods, m)
}
}
@@ -360,7 +375,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
mods[i], mods[j] = mods[j], mods[i]
dirs[i], dirs[j] = dirs[j], dirs[i]
}
- return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
+ return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
}
if len(sumErrMods) > 0 {
@@ -368,7 +383,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
j := len(sumErrMods) - 1 - i
sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
}
- return module.Version{}, "", &ImportMissingSumError{
+ return module.Version{}, "", nil, &ImportMissingSumError{
importPath: path,
mods: sumErrMods,
found: len(mods) > 0,
@@ -376,7 +391,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
}
if len(mods) == 1 {
- return mods[0], dirs[0], nil
+ return mods[0], dirs[0], altMods, nil
}
if mg != nil {
@@ -386,7 +401,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
if !HasModRoot() {
queryErr = ErrNoModRoot
}
- return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
+ return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
}
// So far we've checked the root dependencies.
@@ -397,7 +412,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
// the module graph, so we can't return an ImportMissingError here — one
// of the missing modules might actually contain the package in question,
// in which case we shouldn't go looking for it in some new dependency.
- return module.Version{}, "", err
+ return module.Version{}, "", nil, err
}
}
}
@@ -410,9 +425,9 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
func queryImport(ctx context.Context, path string, rs *Requirements) (module.Version, error) {
// To avoid spurious remote fetches, try the latest replacement for each
// module (golang.org/issue/26241).
- if index != nil {
- var mods []module.Version
- for mp, mv := range index.highestReplaced {
+ var mods []module.Version
+ 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
}
@@ -439,40 +454,41 @@ func queryImport(ctx context.Context, path string, rs *Requirements) (module.Ver
}
mods = append(mods, module.Version{Path: mp, Version: mv})
}
+ }
- // Every module path in mods is a prefix of the import path.
- // As in QueryPattern, prefer the longest prefix that satisfies the import.
- sort.Slice(mods, func(i, j int) bool {
- return len(mods[i].Path) > len(mods[j].Path)
- })
- for _, m := range mods {
- needSum := true
- root, isLocal, err := fetch(ctx, m, needSum)
- if err != nil {
- if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) {
- return module.Version{}, &ImportMissingSumError{importPath: path}
- }
- return module.Version{}, err
- }
- if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
- return m, err
- } else if ok {
- if cfg.BuildMod == "readonly" {
- return module.Version{}, &ImportMissingError{Path: path, replaced: m}
- }
- return m, nil
+ // Every module path in mods is a prefix of the import path.
+ // As in QueryPattern, prefer the longest prefix that satisfies the import.
+ sort.Slice(mods, func(i, j int) bool {
+ return len(mods[i].Path) > len(mods[j].Path)
+ })
+ for _, m := range mods {
+ needSum := true
+ root, isLocal, err := fetch(ctx, m, needSum)
+ if err != nil {
+ if sumErr := (*sumMissingError)(nil); errors.As(err, &sumErr) {
+ return module.Version{}, &ImportMissingSumError{importPath: path}
}
+ return module.Version{}, err
}
- if len(mods) > 0 && module.CheckPath(path) != nil {
- // 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.
- return module.Version{}, &PackageNotInModuleError{
- Mod: mods[0],
- Query: "latest",
- Pattern: path,
- Replacement: Replacement(mods[0]),
+ if _, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
+ return m, err
+ } else if ok {
+ if cfg.BuildMod == "readonly" {
+ return module.Version{}, &ImportMissingError{Path: path, replaced: m}
}
+ return m, nil
+ }
+ }
+ if len(mods) > 0 && module.CheckPath(path) != nil {
+ // 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])
+ return module.Version{}, &PackageNotInModuleError{
+ Mod: mods[0],
+ Query: "latest",
+ Pattern: path,
+ Replacement: replacement,
}
}
@@ -596,7 +612,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
// (the main module, and any directory trees pointed at by replace directives).
if isLocal {
for d := dir; d != mdir && len(d) > len(mdir); {
- haveGoMod := haveGoModCache.Do(d, func() interface{} {
+ haveGoMod := haveGoModCache.Do(d, func() any {
fi, err := fsys.Stat(filepath.Join(d, "go.mod"))
return err == nil && !fi.IsDir()
}).(bool)
@@ -619,7 +635,7 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
// Are there Go source files in the directory?
// We don't care about build tags, not even "+build ignore".
// We're just looking for a plausible directory.
- res := haveGoFilesCache.Do(dir, func() interface{} {
+ res := haveGoFilesCache.Do(dir, func() any {
ok, err := fsys.IsDirWithGoFiles(dir)
return goFilesEntry{haveGoFiles: ok, err: err}
}).(goFilesEntry)
@@ -638,14 +654,14 @@ func dirInModule(path, mpath, mdir string, isLocal bool) (dir string, haveGoFile
// The isLocal return value reports whether the replacement,
// if any, is local to the filesystem.
func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, isLocal bool, err error) {
- if mod == Target {
- return ModRoot(), true, nil
+ if modRoot := MainModules.ModRoot(mod); modRoot != "" {
+ return modRoot, true, nil
}
if r := Replacement(mod); r.Path != "" {
if r.Version == "" {
dir = r.Path
if !filepath.IsAbs(dir) {
- dir = filepath.Join(ModRoot(), dir)
+ dir = filepath.Join(replaceRelativeTo(), dir)
}
// Ensure that the replacement directory actually exists:
// dirInModule does not report errors for missing modules,
@@ -667,7 +683,7 @@ func fetch(ctx context.Context, mod module.Version, needSum bool) (dir string, i
mod = r
}
- if HasModRoot() && cfg.BuildMod == "readonly" && needSum && !modfetch.HaveSum(mod) {
+ if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && needSum && !modfetch.HaveSum(mod) {
return "", false, module.VersionError(mod, &sumMissingError{})
}
diff --git a/src/cmd/go/internal/modload/import_test.go b/src/cmd/go/internal/modload/import_test.go
index 98145887e9dcc5c6b18aa071f08c27ce7c4681ca..65a889ec52f11c8355085050833b82ccfee56ddd 100644
--- a/src/cmd/go/internal/modload/import_test.go
+++ b/src/cmd/go/internal/modload/import_test.go
@@ -69,7 +69,7 @@ func TestQueryImport(t *testing.T) {
RootMode = NoRoot
ctx := context.Background()
- rs := newRequirements(eager, nil, nil)
+ rs := LoadModFile(ctx)
for _, tt := range importTests {
t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) {
diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go
index 45f724d5e3658f5d21d757d7a955b24af6b89be9..df083e7fcca05475be6b676a767022f1aeca5b86 100644
--- a/src/cmd/go/internal/modload/init.go
+++ b/src/cmd/go/internal/modload/init.go
@@ -12,11 +12,13 @@ import (
"fmt"
"go/build"
"internal/lazyregexp"
+ "io/ioutil"
"os"
"path"
"path/filepath"
"strconv"
"strings"
+ "sync"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
@@ -43,28 +45,197 @@ var (
ForceUseModules bool
allowMissingModuleImports bool
+
+ // ExplicitWriteGoMod prevents LoadPackages, ListModules, and other functions
+ // from updating go.mod and go.sum or reporting errors when updates are
+ // needed. A package should set this if it would cause go.mod to be written
+ // multiple times (for example, 'go get' calls LoadPackages multiple times) or
+ // if it needs some other operation to be successful before go.mod and go.sum
+ // can be written (for example, 'go mod download' must download modules before
+ // adding sums to go.sum). Packages that set this are responsible for calling
+ // WriteGoMod explicitly.
+ ExplicitWriteGoMod bool
)
// Variables set in Init.
var (
initialized bool
- modRoot string
- gopath string
+
+ // These are primarily used to initialize the MainModules, and should be
+ // eventually superceded by them but are still used in cases where the module
+ // 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
)
-// Variables set in initTarget (during {Load,Create}ModFile).
+// EnterModule resets MainModules and requirements to refer to just this one module.
+func EnterModule(ctx context.Context, enterModroot string) {
+ MainModules = nil // reset MainModules
+ requirements = nil
+ workFilePath = "" // Force module mode
+
+ modRoots = []string{enterModroot}
+ LoadModFile(ctx)
+}
+
+// Variable set in InitWorkfile
var (
- Target module.Version
+ // Set to the path to the go.work file, or "" if workspace mode is disabled.
+ workFilePath string
+)
+
+type MainModuleSet struct {
+ // versions are the module.Version values of each of the main modules.
+ // For each of them, the Path fields are ordinary module paths and the Version
+ // fields are empty strings.
+ versions []module.Version
+
+ // modRoot maps each module in versions to its absolute filesystem path.
+ modRoot map[module.Version]string
- // targetPrefix is the path prefix for packages in Target, without a trailing
- // slash. For most modules, targetPrefix is just Target.Path, but the
+ // pathPrefix is the path prefix for packages in the module, without a trailing
+ // slash. For most modules, pathPrefix is just version.Path, but the
// standard-library module "std" has an empty prefix.
- targetPrefix string
+ pathPrefix map[module.Version]string
- // targetInGorootSrc caches whether modRoot is within GOROOT/src.
+ // inGorootSrc caches whether modRoot is within GOROOT/src.
// The "std" module is special within GOROOT/src, but not otherwise.
- targetInGorootSrc bool
-)
+ inGorootSrc map[module.Version]bool
+
+ modFiles map[module.Version]*modfile.File
+
+ modContainingCWD module.Version
+
+ 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
+}
+
+func (mms *MainModuleSet) PathPrefix(m module.Version) string {
+ return mms.pathPrefix[m]
+}
+
+// Versions returns the module.Version values of each of the main modules.
+// For each of them, the Path fields are ordinary module paths and the Version
+// fields are empty strings.
+// Callers should not modify the returned slice.
+func (mms *MainModuleSet) Versions() []module.Version {
+ if mms == nil {
+ return nil
+ }
+ return mms.versions
+}
+
+func (mms *MainModuleSet) Contains(path string) bool {
+ if mms == nil {
+ return false
+ }
+ for _, v := range mms.versions {
+ if v.Path == path {
+ return true
+ }
+ }
+ return false
+}
+
+func (mms *MainModuleSet) ModRoot(m module.Version) string {
+ if mms == nil {
+ return ""
+ }
+ return mms.modRoot[m]
+}
+
+func (mms *MainModuleSet) InGorootSrc(m module.Version) bool {
+ if mms == nil {
+ return false
+ }
+ return mms.inGorootSrc[m]
+}
+
+func (mms *MainModuleSet) mustGetSingleMainModule() module.Version {
+ if mms == nil || len(mms.versions) == 0 {
+ panic("internal error: mustGetSingleMainModule called in context with no main modules")
+ }
+ if len(mms.versions) != 1 {
+ if inWorkspaceMode() {
+ panic("internal error: mustGetSingleMainModule called in workspace mode")
+ } else {
+ panic("internal error: multiple main modules present outside of workspace mode")
+ }
+ }
+ return mms.versions[0]
+}
+
+func (mms *MainModuleSet) GetSingleIndexOrNil() *modFileIndex {
+ if mms == nil {
+ return nil
+ }
+ if len(mms.versions) == 0 {
+ return nil
+ }
+ return mms.indices[mms.mustGetSingleMainModule()]
+}
+
+func (mms *MainModuleSet) Index(m module.Version) *modFileIndex {
+ mms.indexMu.Lock()
+ defer mms.indexMu.Unlock()
+ return mms.indices[m]
+}
+
+func (mms *MainModuleSet) SetIndex(m module.Version, index *modFileIndex) {
+ mms.indexMu.Lock()
+ defer mms.indexMu.Unlock()
+ mms.indices[m] = index
+}
+
+func (mms *MainModuleSet) ModFile(m module.Version) *modfile.File {
+ return mms.modFiles[m]
+}
+
+func (mms *MainModuleSet) Len() int {
+ if mms == nil {
+ return 0
+ }
+ return len(mms.versions)
+}
+
+// ModContainingCWD returns the main module containing the working directory,
+// or module.Version{} if none of the main modules contain the working
+// directory.
+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 {
+ if !inWorkspaceMode() {
+ return modFileGoVersion(mms.ModFile(mms.mustGetSingleMainModule()))
+ }
+ v := mms.workFileGoVersion
+ if v == "" {
+ // Fall back to 1.18 for go.work files.
+ v = "1.18"
+ }
+ return v
+}
+
+func (mms *MainModuleSet) WorkFileReplaceMap() map[module.Version]module.Version {
+ return mms.workFileReplaceMap
+}
+
+var MainModules *MainModuleSet
type Root int
@@ -94,6 +265,7 @@ const (
// in go.mod, edit it before loading.
func ModFile() *modfile.File {
Init()
+ modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule())
if modFile == nil {
die()
}
@@ -102,9 +274,38 @@ func ModFile() *modfile.File {
func BinDir() string {
Init()
+ if cfg.GOBIN != "" {
+ return cfg.GOBIN
+ }
+ if gopath == "" {
+ return ""
+ }
return filepath.Join(gopath, "bin")
}
+// InitWorkfile initializes the workFilePath variable for commands that
+// operate in workspace mode. It should not be called by other commands,
+// for example 'go mod tidy', that don't operate in workspace mode.
+func InitWorkfile() {
+ switch cfg.WorkFile {
+ case "off":
+ workFilePath = ""
+ case "", "auto":
+ workFilePath = findWorkspaceFile(base.Cwd())
+ default:
+ if !filepath.IsAbs(cfg.WorkFile) {
+ base.Fatalf("the path provided to -workfile must be an absolute path")
+ }
+ workFilePath = cfg.WorkFile
+ }
+}
+
+// WorkFilePath returns the path of the go.work file, or "" if not in
+// workspace mode. WorkFilePath must be called after InitWorkfile.
+func WorkFilePath() string {
+ return workFilePath
+}
+
// Init determines whether module mode is enabled, locates the root of the
// current module (if any), sets environment variables for Git subprocesses, and
// configures the cfg, codehost, load, modfetch, and search packages for use
@@ -169,18 +370,18 @@ func Init() {
if os.Getenv("GCM_INTERACTIVE") == "" {
os.Setenv("GCM_INTERACTIVE", "never")
}
-
- if modRoot != "" {
+ if modRoots != nil {
// modRoot set before Init was called ("go mod init" does this).
// No need to search for go.mod.
} else if RootMode == NoRoot {
if cfg.ModFile != "" && !base.InGOFLAGS("-modfile") {
base.Fatalf("go: -modfile cannot be used with commands that ignore the current module")
}
- modRoot = ""
+ modRoots = nil
+ } else if inWorkspaceMode() {
+ // We're in workspace mode.
} else {
- modRoot = findModuleRoot(base.Cwd())
- if modRoot == "" {
+ if modRoot := findModuleRoot(base.Cwd()); modRoot == "" {
if cfg.ModFile != "" {
base.Fatalf("go: cannot find main module, but -modfile was set.\n\t-modfile cannot be used to set the module root directory.")
}
@@ -198,11 +399,12 @@ func Init() {
// will find it and get modules when they're not expecting them.
// It's a bit of a peculiar thing to disallow but quite mysterious
// when it happens. See golang.org/issue/26708.
- modRoot = ""
fmt.Fprintf(os.Stderr, "go: warning: ignoring go.mod in system temp root %v\n", os.TempDir())
if !mustUseModules {
return
}
+ } else {
+ modRoots = []string{modRoot}
}
}
if cfg.ModFile != "" && !strings.HasSuffix(cfg.ModFile, ".mod") {
@@ -213,35 +415,11 @@ func Init() {
cfg.ModulesEnabled = true
setDefaultBuildMod()
list := filepath.SplitList(cfg.BuildContext.GOPATH)
- if len(list) == 0 || list[0] == "" {
- base.Fatalf("missing $GOPATH")
- }
- gopath = list[0]
- if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
- base.Fatalf("$GOPATH/go.mod exists but should not")
- }
-
- if modRoot == "" {
- // 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(), ".mod") + ".sum"
- search.SetModRoot(modRoot)
+ if len(list) > 0 && list[0] != "" {
+ gopath = list[0]
+ if _, err := fsys.Stat(filepath.Join(gopath, "go.mod")); err == nil {
+ base.Fatalf("$GOPATH/go.mod exists but should not")
+ }
}
}
@@ -255,7 +433,7 @@ func Init() {
// be called until the command is installed and flags are parsed. Instead of
// calling Init and Enabled, the main package can call this function.
func WillBeEnabled() bool {
- if modRoot != "" || cfg.ModulesEnabled {
+ if modRoots != nil || cfg.ModulesEnabled {
// Already enabled.
return true
}
@@ -297,16 +475,18 @@ func WillBeEnabled() bool {
// (usually through MustModRoot).
func Enabled() bool {
Init()
- return modRoot != "" || cfg.ModulesEnabled
+ return modRoots != nil || cfg.ModulesEnabled
}
-// ModRoot returns the root of the main module.
-// It calls base.Fatalf if there is no main module.
-func ModRoot() string {
- if !HasModRoot() {
- die()
+func VendorDir() string {
+ return filepath.Join(MainModules.ModRoot(MainModules.mustGetSingleMainModule()), "vendor")
+}
+
+func inWorkspaceMode() bool {
+ if !initialized {
+ panic("inWorkspaceMode called before modload.Init called")
}
- return modRoot
+ return workFilePath != ""
}
// HasModRoot reports whether a main module is present.
@@ -314,17 +494,27 @@ func ModRoot() string {
// does not require a main module.
func HasModRoot() bool {
Init()
- return modRoot != ""
+ return modRoots != nil
}
-// ModFilePath returns the effective path of the go.mod file. Normally, this
-// "go.mod" in the directory returned by ModRoot, but the -modfile flag may
-// change its location. ModFilePath calls base.Fatalf if there is no main
-// module, even if -modfile is set.
-func ModFilePath() string {
+// MustHaveModRoot checks that a main module or main modules are present,
+// and calls base.Fatalf if there are no main modules.
+func MustHaveModRoot() {
+ Init()
if !HasModRoot() {
die()
}
+}
+
+// ModFilePath returns the path that would be used for the go.mod
+// file, if in module mode. ModFilePath calls base.Fatalf if there is no main
+// module, even if -modfile is set.
+func ModFilePath() string {
+ MustHaveModRoot()
+ return modFilePath(findModuleRoot(base.Cwd()))
+}
+
+func modFilePath(modRoot string) string {
if cfg.ModFile != "" {
return cfg.ModFile
}
@@ -335,6 +525,9 @@ func die() {
if cfg.Getenv("GO111MODULE") == "off" {
base.Fatalf("go: modules disabled by GO111MODULE=off; see 'go help modules'")
}
+ if inWorkspaceMode() {
+ base.Fatalf("go: no modules were found in the current workspace; see 'go help work'")
+ }
if dir, name := findAltConfig(base.Cwd()); dir != "" {
rel, err := filepath.Rel(base.Cwd(), dir)
if err != nil {
@@ -365,12 +558,81 @@ func (goModDirtyError) Error() string {
var errGoModDirty error = goModDirtyError{}
+func loadWorkFile(path string) (goVersion string, modRoots []string, replaces []*modfile.Replace, err error) {
+ workDir := filepath.Dir(path)
+ wf, err := ReadWorkFile(path)
+ if err != nil {
+ return "", nil, nil, err
+ }
+ if wf.Go != nil {
+ goVersion = wf.Go.Version
+ }
+ seen := map[string]bool{}
+ for _, d := range wf.Use {
+ modRoot := d.Path
+ if !filepath.IsAbs(modRoot) {
+ modRoot = filepath.Join(workDir, modRoot)
+ }
+
+ if seen[modRoot] {
+ return "", nil, nil, fmt.Errorf("path %s appears multiple times in workspace", modRoot)
+ }
+ seen[modRoot] = true
+ modRoots = append(modRoots, modRoot)
+ }
+
+ return goVersion, modRoots, wf.Replace, nil
+}
+
+// ReadWorkFile reads and parses the go.work file at the given path.
+func ReadWorkFile(path string) (*modfile.WorkFile, error) {
+ workData, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return modfile.ParseWork(path, workData, nil)
+}
+
+// WriteWorkFile cleans and writes out the go.work file to the given path.
+func WriteWorkFile(path string, wf *modfile.WorkFile) error {
+ wf.SortBlocks()
+ wf.Cleanup()
+ out := modfile.Format(wf.Syntax)
+
+ return ioutil.WriteFile(path, out, 0666)
+}
+
+// UpdateWorkFile updates comments on directory directives in the go.work
+// file to include the associated module path.
+func UpdateWorkFile(wf *modfile.WorkFile) {
+ missingModulePaths := map[string]string{} // module directory listed in file -> abspath modroot
+
+ for _, d := range wf.Use {
+ modRoot := d.Path
+ if d.ModulePath == "" {
+ missingModulePaths[d.Path] = modRoot
+ }
+ }
+
+ // Clean up and annotate directories.
+ // TODO(matloob): update x/mod to actually add module paths.
+ for moddir, absmodroot := range missingModulePaths {
+ _, f, err := ReadModFile(filepath.Join(absmodroot, "go.mod"), nil)
+ if err != nil {
+ continue // Error will be reported if modules are loaded.
+ }
+ wf.AddUse(moddir, f.Module.Mod.Path)
+ }
+}
+
// LoadModFile sets Target and, if there is a main module, parses the initial
// build list from its go.mod file.
//
// LoadModFile may make changes in memory, like adding a go directive and
-// ensuring requirements are consistent, and will write those changes back to
-// disk unless DisallowWriteGoMod is in effect.
+// ensuring requirements are consistent. The caller is responsible for ensuring
+// those changes are written to disk by calling LoadPackages or ListModules
+// (unless ExplicitWriteGoMod is set) or by calling WriteGoMod directly.
//
// As a side-effect, LoadModFile may change cfg.BuildMod to "vendor" if
// -mod wasn't set explicitly and automatic vendoring should be enabled.
@@ -383,111 +645,142 @@ var errGoModDirty error = goModDirtyError{}
// it for global consistency. Most callers outside of the modload package should
// use LoadModGraph instead.
func LoadModFile(ctx context.Context) *Requirements {
- rs, needCommit := loadModFile(ctx)
- if needCommit {
- commitRequirements(ctx, modFileGoVersion(), rs)
- }
- return rs
-}
-
-// loadModFile is like LoadModFile, but does not implicitly commit the
-// requirements back to disk after fixing inconsistencies.
-//
-// If needCommit is true, after the caller makes any other needed changes to the
-// returned requirements they should invoke commitRequirements to fix any
-// inconsistencies that may be present in the on-disk go.mod file.
-func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
if requirements != nil {
- return requirements, false
+ return requirements
}
Init()
- if modRoot == "" {
- Target = module.Version{Path: "command-line-arguments"}
- targetPrefix = "command-line-arguments"
- goVersion := LatestGoVersion()
- rawGoVersion.Store(Target, goVersion)
- requirements = newRequirements(modDepthFromGoVersion(goVersion), nil, nil)
- return requirements, false
- }
-
- gomod := ModFilePath()
- var data []byte
- var err error
- if gomodActual, ok := fsys.OverlayPath(gomod); ok {
- // Don't lock go.mod if it's part of the overlay.
- // On Plan 9, locking requires chmod, and we don't want to modify any file
- // in the overlay. See #44700.
- data, err = os.ReadFile(gomodActual)
+ 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)
+ }
+ for _, modRoot := range modRoots {
+ sumFile := strings.TrimSuffix(modFilePath(modRoot), ".mod") + ".sum"
+ modfetch.WorkspaceGoSumFiles = append(modfetch.WorkspaceGoSumFiles, sumFile)
+ }
+ 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 {
- data, err = lockedfile.Read(gomodActual)
- }
- if err != nil {
- base.Fatalf("go: %v", err)
+ modfetch.GoSumFile = strings.TrimSuffix(modFilePath(modRoots[0]), ".mod") + ".sum"
+ }
+ if len(modRoots) == 0 {
+ // TODO(#49228): 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}, "", nil)
+ goVersion := LatestGoVersion()
+ rawGoVersion.Store(mainModule, goVersion)
+ pruning := pruningForGoVersion(goVersion)
+ if inWorkspaceMode() {
+ pruning = workspace
+ }
+ requirements = newRequirements(pruning, nil, nil)
+ return requirements
}
- var fixed bool
- f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed))
- if err != nil {
- // Errors returned by modfile.Parse begin with file:line.
- base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
- }
- if f.Module == nil {
- // No module declaration. Must add module path.
- base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
- }
+ var modFiles []*modfile.File
+ var mainModules []module.Version
+ var indices []*modFileIndex
+ for _, modroot := range modRoots {
+ gomod := modFilePath(modroot)
+ var fixed bool
+ data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
- modFile = f
- initTarget(f.Module.Mod)
- index = indexModFile(data, f, fixed)
+ modFiles = append(modFiles, f)
+ mainModule := f.Module.Mod
+ mainModules = append(mainModules, mainModule)
+ indices = append(indices, indexModFile(data, f, mainModule, fixed))
- if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
- if pathErr, ok := err.(*module.InvalidPathError); ok {
- pathErr.Kind = "module"
+ if err := module.CheckImportPath(f.Module.Mod.Path); err != nil {
+ if pathErr, ok := err.(*module.InvalidPathError); ok {
+ pathErr.Kind = "module"
+ }
+ base.Fatalf("go: %v", err)
}
- base.Fatalf("go: %v", err)
}
+ MainModules = makeMainModules(mainModules, modRoots, modFiles, indices, workFileGoVersion, workFileReplaces)
setDefaultBuildMod() // possibly enable automatic vendoring
- rs = requirementsFromModFile()
+ rs := requirementsFromModFiles(ctx, modFiles)
+
+ if inWorkspaceMode() {
+ // We don't need to do anything for vendor or update the mod file so
+ // return early.
+ requirements = rs
+ return rs
+ }
+
+ mainModule := MainModules.mustGetSingleMainModule()
+
if cfg.BuildMod == "vendor" {
- readVendorList()
- checkVendorConsistency()
+ readVendorList(mainModule)
+ index := MainModules.Index(mainModule)
+ modFile := MainModules.ModFile(mainModule)
+ checkVendorConsistency(index, modFile)
rs.initVendor(vendorList)
}
+
if rs.hasRedundantRoot() {
// If any module path appears more than once in the roots, we know that the
// go.mod file needs to be updated even though we have not yet loaded any
// transitive dependencies.
+ var err error
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
if err != nil {
base.Fatalf("go: %v", err)
}
}
- if index.goVersionV == "" {
+ if MainModules.Index(mainModule).goVersionV == "" && rs.pruning != workspace {
// TODO(#45551): Do something more principled instead of checking
// cfg.CmdName directly here.
if cfg.BuildMod == "mod" && cfg.CmdName != "mod graph" && cfg.CmdName != "mod why" {
- addGoStmt(LatestGoVersion())
- if go117EnableLazyLoading {
- // We need to add a 'go' version to the go.mod file, but we must assume
- // that its existing contents match something between Go 1.11 and 1.16.
- // Go 1.11 through 1.16 have eager requirements, but the latest Go
- // version uses lazy requirements instead — so we need to cnvert the
- // requirements to be lazy.
- rs, err = convertDepth(ctx, rs, lazy)
- if err != nil {
- base.Fatalf("go: %v", err)
- }
+ addGoStmt(MainModules.ModFile(mainModule), mainModule, LatestGoVersion())
+
+ // We need to add a 'go' version to the go.mod file, but we must assume
+ // that its existing contents match something between Go 1.11 and 1.16.
+ // Go 1.11 through 1.16 do not support graph pruning, but the latest Go
+ // version uses a pruned module graph — so we need to convert the
+ // requirements to support pruning.
+ var err error
+ rs, err = convertPruning(ctx, rs, pruned)
+ if err != nil {
+ base.Fatalf("go: %v", err)
}
} else {
- rawGoVersion.Store(Target, modFileGoVersion())
+ rawGoVersion.Store(mainModule, modFileGoVersion(MainModules.ModFile(mainModule)))
}
}
requirements = rs
- return requirements, true
+ return requirements
}
// CreateModFile initializes a new module by creating a go.mod file.
@@ -500,9 +793,10 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
// exactly the same as in the legacy configuration (for example, we can't get
// packages at multiple versions from the same module).
func CreateModFile(ctx context.Context, modPath string) {
- modRoot = base.Cwd()
+ modRoot := base.Cwd()
+ modRoots = []string{modRoot}
Init()
- modFilePath := ModFilePath()
+ modFilePath := modFilePath(modRoot)
if _, err := fsys.Stat(modFilePath); err == nil {
base.Fatalf("go: %s already exists", modFilePath)
}
@@ -523,15 +817,22 @@ func CreateModFile(ctx context.Context, modPath string) {
}
}
base.Fatalf("go: %v", err)
+ } else if _, _, ok := module.SplitPathVersion(modPath); !ok {
+ if strings.HasPrefix(modPath, "gopkg.in/") {
+ invalidMajorVersionMsg := fmt.Errorf("module paths beginning with gopkg.in/ must always have a major version suffix in the form of .vN:\n\tgo mod init %s", suggestGopkgIn(modPath))
+ base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
+ }
+ invalidMajorVersionMsg := fmt.Errorf("major version suffixes must be in the form of /vN and are only allowed for v2 or later:\n\tgo mod init %s", suggestModulePath(modPath))
+ base.Fatalf(`go: invalid module path "%v": %v`, modPath, invalidMajorVersionMsg)
}
fmt.Fprintf(os.Stderr, "go: creating new go.mod: module %s\n", modPath)
- modFile = new(modfile.File)
+ modFile := new(modfile.File)
modFile.AddModuleStmt(modPath)
- initTarget(modFile.Module.Mod)
- addGoStmt(LatestGoVersion()) // Add the go directive before converted module requirements.
+ 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(modPath)
+ convertedFrom, err := convertLegacyConfig(modFile, modRoot)
if convertedFrom != "" {
fmt.Fprintf(os.Stderr, "go: copying requirements from %s\n", base.ShortPath(convertedFrom))
}
@@ -539,12 +840,15 @@ func CreateModFile(ctx context.Context, modPath string) {
base.Fatalf("go: %v", err)
}
- rs := requirementsFromModFile()
+ rs := requirementsFromModFiles(ctx, []*modfile.File{modFile})
rs, err = updateRoots(ctx, rs.direct, rs, nil, nil, false)
if err != nil {
base.Fatalf("go: %v", err)
}
- commitRequirements(ctx, modFileGoVersion(), rs)
+ requirements = rs
+ if err := commitRequirements(ctx); err != nil {
+ base.Fatalf("go: %v", err)
+ }
// Suggest running 'go mod tidy' unless the project is empty. Even if we
// imported all the correct requirements above, we're probably missing
@@ -570,6 +874,32 @@ func CreateModFile(ctx context.Context, modPath string) {
}
}
+// CreateWorkFile initializes a new workspace by creating a go.work file.
+func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) {
+ if _, err := fsys.Stat(workFile); err == nil {
+ base.Fatalf("go: %s already exists", workFile)
+ }
+
+ goV := LatestGoVersion() // Use current Go version by default
+ workF := new(modfile.WorkFile)
+ workF.Syntax = new(modfile.FileSyntax)
+ workF.AddGoStmt(goV)
+
+ for _, dir := range modDirs {
+ _, f, err := ReadModFile(filepath.Join(dir, "go.mod"), nil)
+ if err != nil {
+ if os.IsNotExist(err) {
+ base.Fatalf("go: creating workspace file: no go.mod file exists in directory %v", dir)
+ }
+ base.Fatalf("go: error parsing go.mod in directory %s: %v", dir, err)
+ }
+ workF.AddUse(ToDirectoryPath(dir), f.Module.Mod.Path)
+ }
+
+ UpdateWorkFile(workF)
+ WriteWorkFile(workFile, workF)
+}
+
// fixVersion returns a modfile.VersionFixer implemented using the Query function.
//
// It resolves commit hashes and branch names to versions,
@@ -632,49 +962,118 @@ func AllowMissingModuleImports() {
allowMissingModuleImports = true
}
-// initTarget sets Target and associated variables according to modFile,
-func initTarget(m module.Version) {
- Target = m
- targetPrefix = m.Path
-
- if rel := search.InDir(base.Cwd(), cfg.GOROOTsrc); rel != "" {
- targetInGorootSrc = true
- if m.Path == "std" {
- // The "std" module in GOROOT/src is the Go standard library. Unlike other
- // modules, the packages in the "std" module have no import-path prefix.
- //
- // Modules named "std" outside of GOROOT/src do not receive this special
- // treatment, so it is possible to run 'go test .' in other GOROOTs to
- // test individual packages using a combination of the modified package
- // and the ordinary standard library.
- // (See https://golang.org/issue/30756.)
- targetPrefix = ""
+// 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, 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))
+ }
+ }
+ 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,
+ 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
+ mainModules.modRoot[m] = rootDirs[i]
+ mainModules.modFiles[m] = modFiles[i]
+ mainModules.indices[m] = indices[i]
+
+ if mainModules.modRoot[m] == modRootContainingCWD {
+ mainModules.modContainingCWD = m
+ }
+
+ if rel := search.InDir(rootDirs[i], cfg.GOROOTsrc); rel != "" {
+ mainModules.inGorootSrc[m] = true
+ if m.Path == "std" {
+ // The "std" module in GOROOT/src is the Go standard library. Unlike other
+ // modules, the packages in the "std" module have no import-path prefix.
+ //
+ // Modules named "std" outside of GOROOT/src do not receive this special
+ // treatment, so it is possible to run 'go test .' in other GOROOTs to
+ // test individual packages using a combination of the modified package
+ // and the ordinary standard library.
+ // (See https://golang.org/issue/30756.)
+ 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] && prev != r.New {
+ base.Fatalf("go: conflicting replacements for %v:\n\t%v\n\t%v\nuse \"go work edit -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
}
-// requirementsFromModFile returns the set of non-excluded requirements from
+// requirementsFromModFiles returns the set of non-excluded requirements from
// the global modFile.
-func requirementsFromModFile() *Requirements {
- roots := make([]module.Version, 0, len(modFile.Require))
+func requirementsFromModFiles(ctx context.Context, modFiles []*modfile.File) *Requirements {
+ var roots []module.Version
direct := map[string]bool{}
- for _, r := range modFile.Require {
- if index != nil && index.exclude[r.Mod] {
- if cfg.BuildMod == "mod" {
- fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
- } else {
- fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
- }
- continue
+ var pruning modPruning
+ if inWorkspaceMode() {
+ pruning = workspace
+ roots = make([]module.Version, len(MainModules.Versions()))
+ copy(roots, MainModules.Versions())
+ } else {
+ pruning = pruningForGoVersion(MainModules.GoVersion())
+ if len(modFiles) != 1 {
+ panic(fmt.Errorf("requirementsFromModFiles called with %v modfiles outside workspace mode", len(modFiles)))
}
+ modFile := modFiles[0]
+ roots = make([]module.Version, 0, len(modFile.Require))
+ mm := MainModules.mustGetSingleMainModule()
+ for _, r := range modFile.Require {
+ if index := MainModules.Index(mm); index != nil && index.exclude[r.Mod] {
+ if cfg.BuildMod == "mod" {
+ fmt.Fprintf(os.Stderr, "go: dropping requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
+ } else {
+ fmt.Fprintf(os.Stderr, "go: ignoring requirement on excluded version %s %s\n", r.Mod.Path, r.Mod.Version)
+ }
+ continue
+ }
- roots = append(roots, r.Mod)
- if !r.Indirect {
- direct[r.Mod.Path] = true
+ roots = append(roots, r.Mod)
+ if !r.Indirect {
+ direct[r.Mod.Path] = true
+ }
}
}
module.Sort(roots)
- rs := newRequirements(modDepthFromGoVersion(modFileGoVersion()), roots, direct)
+ rs := newRequirements(pruning, roots, direct)
return rs
}
@@ -682,18 +1081,35 @@ func requirementsFromModFile() *Requirements {
// wasn't provided. setDefaultBuildMod may be called multiple times.
func setDefaultBuildMod() {
if cfg.BuildModExplicit {
+ if inWorkspaceMode() && cfg.BuildMod != "readonly" {
+ base.Fatalf("go: -mod may only be set to readonly when in workspace mode, but it is set to %q"+
+ "\n\tRemove the -mod flag to use the default readonly value,"+
+ "\n\tor set -workfile=off to disable workspace mode.", cfg.BuildMod)
+ }
// Don't override an explicit '-mod=' argument.
return
}
- if cfg.CmdName == "get" || strings.HasPrefix(cfg.CmdName, "mod ") {
- // 'get' and 'go mod' commands may update go.mod automatically.
- // TODO(jayconrod): should this narrower? Should 'go mod download' or
- // 'go mod graph' update go.mod by default?
+ // TODO(#40775): commands should pass in the module mode as an option
+ // to modload functions instead of relying on an implicit setting
+ // based on command name.
+ switch cfg.CmdName {
+ case "get", "mod download", "mod init", "mod tidy", "work sync":
+ // These commands are intended to update go.mod and go.sum.
+ cfg.BuildMod = "mod"
+ return
+ case "mod graph", "mod verify", "mod why":
+ // These commands should not update go.mod or go.sum, but they should be
+ // able to fetch modules not in go.sum and should not report errors if
+ // go.mod is inconsistent. They're useful for debugging, and they need
+ // to work in buggy situations.
cfg.BuildMod = "mod"
return
+ case "mod vendor":
+ cfg.BuildMod = "readonly"
+ return
}
- if modRoot == "" {
+ if modRoots == nil {
if allowMissingModuleImports {
cfg.BuildMod = "mod"
} else {
@@ -702,31 +1118,38 @@ func setDefaultBuildMod() {
return
}
- if fi, err := fsys.Stat(filepath.Join(modRoot, "vendor")); err == nil && fi.IsDir() {
- modGo := "unspecified"
- if index != nil && index.goVersionV != "" {
- if semver.Compare(index.goVersionV, "v1.14") >= 0 {
- // The Go version is at least 1.14, and a vendor directory exists.
- // Set -mod=vendor by default.
- cfg.BuildMod = "vendor"
- cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
- return
- } else {
- modGo = index.goVersionV[1:]
+ if len(modRoots) == 1 {
+ index := MainModules.GetSingleIndexOrNil()
+ if fi, err := fsys.Stat(filepath.Join(modRoots[0], "vendor")); err == nil && fi.IsDir() {
+ modGo := "unspecified"
+ if index != nil && index.goVersionV != "" {
+ if semver.Compare(index.goVersionV, "v1.14") >= 0 {
+ // The Go version is at least 1.14, and a vendor directory exists.
+ // Set -mod=vendor by default.
+ cfg.BuildMod = "vendor"
+ cfg.BuildModReason = "Go version in go.mod is at least 1.14 and vendor directory exists."
+ return
+ } else {
+ modGo = index.goVersionV[1:]
+ }
}
- }
- // Since a vendor directory exists, we should record why we didn't use it.
- // This message won't normally be shown, but it may appear with import errors.
- cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
+ // Since a vendor directory exists, we should record why we didn't use it.
+ // This message won't normally be shown, but it may appear with import errors.
+ cfg.BuildModReason = fmt.Sprintf("Go version in go.mod is %s, so vendor directory was not used.", modGo)
+ }
}
cfg.BuildMod = "readonly"
}
+func mustHaveCompleteRequirements() bool {
+ return cfg.BuildMod != "mod" && !inWorkspaceMode()
+}
+
// convertLegacyConfig imports module requirements from a legacy vendoring
// configuration file, if one is present.
-func convertLegacyConfig(modPath string) (from string, err error) {
+func convertLegacyConfig(modFile *modfile.File, modRoot string) (from string, err error) {
noneSelected := func(path string) (version string) { return "none" }
queryPackage := func(path, rev string) (module.Version, error) {
pkgMods, modOnly, err := QueryPattern(context.Background(), path, rev, noneSelected, nil)
@@ -757,14 +1180,14 @@ func convertLegacyConfig(modPath string) (from string, err error) {
// addGoStmt adds a go directive to the go.mod file if it does not already
// include one. The 'go' version added, if any, is the latest version supported
// by this toolchain.
-func addGoStmt(v string) {
+func addGoStmt(modFile *modfile.File, mod module.Version, v string) {
if modFile.Go != nil && modFile.Go.Version != "" {
return
}
if err := modFile.AddGoStmt(v); err != nil {
base.Fatalf("go: internal error: %v", err)
}
- rawGoVersion.Store(Target, v)
+ rawGoVersion.Store(mod, v)
}
// LatestGoVersion returns the latest version of the Go language supported by
@@ -815,7 +1238,7 @@ var altConfigs = []string{
".git/config",
}
-func findModuleRoot(dir string) (root string) {
+func findModuleRoot(dir string) (roots string) {
if dir == "" {
panic("dir not set")
}
@@ -835,6 +1258,33 @@ func findModuleRoot(dir string) (root string) {
return ""
}
+func findWorkspaceFile(dir string) (root string) {
+ if dir == "" {
+ panic("dir not set")
+ }
+ dir = filepath.Clean(dir)
+
+ // Look for enclosing go.mod.
+ for {
+ f := filepath.Join(dir, "go.work")
+ if fi, err := fsys.Stat(f); err == nil && !fi.IsDir() {
+ return f
+ }
+ d := filepath.Dir(dir)
+ if d == dir {
+ break
+ }
+ if d == cfg.GOROOT {
+ // As a special case, don't cross GOROOT to find a go.work file.
+ // The standard library and commands built in go always use the vendored
+ // dependencies, so avoid using a most likely irrelevant go.work file.
+ return ""
+ }
+ dir = d
+ }
+ return ""
+}
+
func findAltConfig(dir string) (root, name string) {
if dir == "" {
panic("dir not set")
@@ -960,66 +1410,62 @@ func findImportComment(file string) string {
return path
}
-var allowWriteGoMod = true
-
-// DisallowWriteGoMod causes future calls to WriteGoMod to do nothing at all.
-func DisallowWriteGoMod() {
- allowWriteGoMod = false
-}
-
-// AllowWriteGoMod undoes the effect of DisallowWriteGoMod:
-// future calls to WriteGoMod will update go.mod if needed.
-// Note that any past calls have been discarded, so typically
-// a call to AlowWriteGoMod should be followed by a call to WriteGoMod.
-func AllowWriteGoMod() {
- allowWriteGoMod = true
-}
-
// WriteGoMod writes the current build list back to go.mod.
-func WriteGoMod(ctx context.Context) {
- if !allowWriteGoMod {
- panic("WriteGoMod called while disallowed")
- }
- commitRequirements(ctx, modFileGoVersion(), LoadModFile(ctx))
+func WriteGoMod(ctx context.Context) error {
+ requirements = LoadModFile(ctx)
+ return commitRequirements(ctx)
}
-// commitRequirements writes sets the global requirements variable to rs and
-// writes its contents back to the go.mod file on disk.
-func commitRequirements(ctx context.Context, goVersion string, rs *Requirements) {
- requirements = rs
-
- if !allowWriteGoMod {
- // Some package outside of modload promised to update the go.mod file later.
- return
- }
-
- if modRoot == "" {
+// commitRequirements ensures go.mod and go.sum are up to date with the current
+// requirements.
+//
+// In "mod" mode, commitRequirements writes changes to go.mod and go.sum.
+//
+// In "readonly" and "vendor" modes, commitRequirements returns an error if
+// go.mod or go.sum are out of date in a semantically significant way.
+//
+// In workspace mode, commitRequirements only writes changes to go.work.sum.
+func commitRequirements(ctx context.Context) (err error) {
+ if inWorkspaceMode() {
+ // go.mod files aren't updated in workspace mode, but we still want to
+ // update the go.work.sum file.
+ return modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
+ }
+ if MainModules.Len() != 1 || MainModules.ModRoot(MainModules.Versions()[0]) == "" {
// We aren't in a module, so we don't have anywhere to write a go.mod file.
- return
+ return nil
+ }
+ mainModule := MainModules.mustGetSingleMainModule()
+ modFile := MainModules.ModFile(mainModule)
+ if modFile == nil {
+ // command-line-arguments has no .mod file to write.
+ return nil
}
+ modFilePath := modFilePath(MainModules.ModRoot(mainModule))
var list []*modfile.Require
- for _, m := range rs.rootModules {
+ for _, m := range requirements.rootModules {
list = append(list, &modfile.Require{
Mod: m,
- Indirect: !rs.direct[m.Path],
+ Indirect: !requirements.direct[m.Path],
})
}
- if goVersion != "" {
- modFile.AddGoStmt(goVersion)
+ if modFile.Go == nil || modFile.Go.Version == "" {
+ modFile.AddGoStmt(modFileGoVersion(modFile))
}
- if semver.Compare("v"+modFileGoVersion(), separateIndirectVersionV) < 0 {
+ if semver.Compare("v"+modFileGoVersion(modFile), separateIndirectVersionV) < 0 {
modFile.SetRequire(list)
} else {
modFile.SetRequireSeparateIndirect(list)
}
modFile.Cleanup()
+ index := MainModules.GetSingleIndexOrNil()
dirty := index.modFileIsDirty(modFile)
if dirty && cfg.BuildMod != "mod" {
// If we're about to fail due to -mod=readonly,
// prefer to report a dirty go.mod over a dirty go.sum
- base.Fatalf("go: %v", errGoModDirty)
+ return errGoModDirty
}
if !dirty && cfg.CmdName != "mod tidy" {
@@ -1028,30 +1474,33 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
// Don't write go.mod, but write go.sum in case we added or trimmed sums.
// 'go mod init' shouldn't write go.sum, since it will be incomplete.
if cfg.CmdName != "mod init" {
- modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
+ if err := modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements()); err != nil {
+ return err
+ }
}
- return
+ return nil
}
- gomod := ModFilePath()
- if _, ok := fsys.OverlayPath(gomod); ok {
+ if _, ok := fsys.OverlayPath(modFilePath); ok {
if dirty {
- base.Fatalf("go: updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
+ return errors.New("updates to go.mod needed, but go.mod is part of the overlay specified with -overlay")
}
- return
+ return nil
}
new, err := modFile.Format()
if err != nil {
- base.Fatalf("go: %v", err)
+ return err
}
defer func() {
// At this point we have determined to make the go.mod file on disk equal to new.
- index = indexModFile(new, modFile, false)
+ MainModules.SetIndex(mainModule, indexModFile(new, modFile, mainModule, false))
// Update go.sum after releasing the side lock and refreshing the index.
// 'go mod init' shouldn't write go.sum, since it will be incomplete.
if cfg.CmdName != "mod init" {
- modfetch.WriteGoSum(keepSums(ctx, loaded, rs, addBuildListZipSums))
+ if err == nil {
+ err = modfetch.WriteGoSum(keepSums(ctx, loaded, requirements, addBuildListZipSums), mustHaveCompleteRequirements())
+ }
}
}()
@@ -1063,7 +1512,7 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
errNoChange := errors.New("no update needed")
- err = lockedfile.Transform(ModFilePath(), func(old []byte) ([]byte, error) {
+ err = lockedfile.Transform(modFilePath, func(old []byte) ([]byte, error) {
if bytes.Equal(old, new) {
// The go.mod file is already equal to new, possibly as the result of some
// other process.
@@ -1084,8 +1533,9 @@ func commitRequirements(ctx context.Context, goVersion string, rs *Requirements)
})
if err != nil && err != errNoChange {
- base.Fatalf("go: updating go.mod: %v", err)
+ return fmt.Errorf("updating go.mod: %w", err)
}
+ return nil
}
// keepSums returns the set of modules (and go.mod file entries) for which
@@ -1113,16 +1563,18 @@ func keepSums(ctx context.Context, ld *loader, rs *Requirements, which whichSums
continue
}
- if rs.depth == lazy && pkg.mod.Path != "" {
+ if rs.pruning == pruned && pkg.mod.Path != "" {
if v, ok := rs.rootSelected(pkg.mod.Path); ok && v == pkg.mod.Version {
- // pkg was loaded from a root module, and because the main module is
- // lazy we do not check non-root modules for conflicts for packages
- // that can be found in roots. So we only need the checksums for the
- // root modules that may contain pkg, not all possible modules.
+ // pkg was loaded from a root module, and because the main module has
+ // a pruned module graph we do not check non-root modules for
+ // conflicts for packages that can be found in roots. So we only need
+ // the checksums for the root modules that may contain pkg, not all
+ // possible modules.
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}
- keep[resolveReplacement(m)] = true
+ r := resolveReplacement(m)
+ keep[r] = true
}
}
continue
@@ -1133,15 +1585,15 @@ 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}
- keep[resolveReplacement(m)] = true
+ r := resolveReplacement(m)
+ keep[r] = true
}
}
}
}
if rs.graph.Load() == nil {
- // The module graph was not loaded, possibly because the main module is lazy
- // or possibly because we haven't needed to load the graph yet.
+ // We haven't needed to load the module graph so far.
// 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 {
@@ -1158,13 +1610,15 @@ 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.
- keep[modkey(resolveReplacement(m))] = true
+ r := resolveReplacement(m)
+ keep[modkey(r)] = true
}
})
if which == addBuildListZipSums {
for _, m := range mg.BuildList() {
- keep[resolveReplacement(m)] = true
+ r := resolveReplacement(m)
+ keep[r] = true
}
}
}
@@ -1184,3 +1638,56 @@ const (
func modkey(m module.Version) module.Version {
return module.Version{Path: m.Path, Version: m.Version + "/go.mod"}
}
+
+func suggestModulePath(path string) string {
+ var m string
+
+ i := len(path)
+ for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') {
+ i--
+ }
+ url := path[:i]
+ url = strings.TrimSuffix(url, "/v")
+ url = strings.TrimSuffix(url, "/")
+
+ f := func(c rune) bool {
+ return c > '9' || c < '0'
+ }
+ s := strings.FieldsFunc(path[i:], f)
+ if len(s) > 0 {
+ m = s[0]
+ }
+ m = strings.TrimLeft(m, "0")
+ if m == "" || m == "1" {
+ return url + "/v2"
+ }
+
+ return url + "/v" + m
+}
+
+func suggestGopkgIn(path string) string {
+ var m string
+ i := len(path)
+ for i > 0 && (('0' <= path[i-1] && path[i-1] <= '9') || (path[i-1] == '.')) {
+ i--
+ }
+ url := path[:i]
+ url = strings.TrimSuffix(url, ".v")
+ url = strings.TrimSuffix(url, "/v")
+ url = strings.TrimSuffix(url, "/")
+
+ f := func(c rune) bool {
+ return c > '9' || c < '0'
+ }
+ s := strings.FieldsFunc(path, f)
+ if len(s) > 0 {
+ m = s[0]
+ }
+
+ m = strings.TrimLeft(m, "0")
+
+ if m == "" {
+ return url + ".v1"
+ }
+ return url + ".v" + m
+}
diff --git a/src/cmd/go/internal/modload/list.go b/src/cmd/go/internal/modload/list.go
index ccdeb9b1d11e8650ec420169723ba7d1aae4c2ba..f782cd93db3d25d2258303fc22239c09156ff1c8 100644
--- a/src/cmd/go/internal/modload/list.go
+++ b/src/cmd/go/internal/modload/list.go
@@ -72,14 +72,21 @@ func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo.
}
if err == nil {
- commitRequirements(ctx, modFileGoVersion(), rs)
+ requirements = rs
+ if !ExplicitWriteGoMod {
+ err = commitRequirements(ctx)
+ }
}
return mods, err
}
func listModules(ctx context.Context, rs *Requirements, args []string, mode ListMode) (_ *Requirements, mods []*modinfo.ModulePublic, mgErr error) {
if len(args) == 0 {
- return rs, []*modinfo.ModulePublic{moduleInfo(ctx, rs, Target, mode)}, nil
+ var ms []*modinfo.ModulePublic
+ for _, m := range MainModules.Versions() {
+ ms = append(ms, moduleInfo(ctx, rs, m, mode))
+ }
+ return rs, ms, nil
}
needFullGraph := false
@@ -101,7 +108,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List
path := arg[:i]
vers := arg[i+1:]
if vers == "upgrade" || vers == "patch" {
- if _, ok := rs.rootSelected(path); !ok || rs.depth == eager {
+ if _, ok := rs.rootSelected(path); !ok || rs.pruning == unpruned {
needFullGraph = true
if !HasModRoot() {
base.Fatalf("go: cannot match %q: %v", arg, ErrNoModRoot)
@@ -110,7 +117,7 @@ func listModules(ctx context.Context, rs *Requirements, args []string, mode List
}
continue
}
- if _, ok := rs.rootSelected(arg); !ok || rs.depth == eager {
+ if _, ok := rs.rootSelected(arg); !ok || rs.pruning == unpruned {
needFullGraph = true
if mode&ListVersions == 0 && !HasModRoot() {
base.Fatalf("go: cannot match %q without -versions or an explicit version: %v", arg, ErrNoModRoot)
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index bce9ad85f42e6c593724c6e05bbfb31db6b57521..617b634d263e335946ef51ee84a26eb0cb0dc4d8 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -40,9 +40,10 @@ package modload
// - the main module specifies a go version ≤ 1.15, and the package is imported
// by a *test of* another package in "all".
//
-// When we implement lazy loading, we will record the modules providing packages
-// in "all" even when we are only loading individual packages, so we set the
-// pkgInAll flag regardless of the whether the "all" pattern is a root.
+// When graph pruning is in effect, we want to spot-check the graph-pruning
+// invariants — which depend on which packages are known to be in "all" — even
+// when we are only loading individual packages, so we set the pkgInAll flag
+// regardless of the whether the "all" pattern is a root.
// (This is necessary to maintain the “import invariant” described in
// https://golang.org/design/36460-lazy-module-loading.)
//
@@ -230,6 +231,9 @@ type PackageOpts struct {
// SilenceUnmatchedWarnings suppresses the warnings normally emitted for
// patterns that did not match any packages.
SilenceUnmatchedWarnings bool
+
+ // Resolve the query against this module.
+ MainModule module.Version
}
// LoadPackages identifies the set of packages matching the given patterns and
@@ -255,7 +259,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
case m.IsLocal():
// Evaluate list of file system directories on first iteration.
if m.Dirs == nil {
- matchLocalDirs(ctx, m, rs)
+ matchModRoots := modRoots
+ if opts.MainModule != (module.Version{}) {
+ matchModRoots = []string{MainModules.ModRoot(opts.MainModule)}
+ }
+ matchLocalDirs(ctx, matchModRoots, m, rs)
}
// Make a copy of the directory list and translate to import paths.
@@ -274,7 +282,9 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
// If we're outside of a module, ensure that the failure mode
// indicates that.
- ModRoot()
+ if !HasModRoot() {
+ die()
+ }
if ld != nil {
m.AddError(err)
@@ -306,7 +316,11 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
// The initial roots are the packages in the main module.
// loadFromRoots will expand that to "all".
m.Errs = m.Errs[:0]
- matchPackages(ctx, m, opts.Tags, omitStd, []module.Version{Target})
+ matchModules := MainModules.Versions()
+ if opts.MainModule != (module.Version{}) {
+ matchModules = []module.Version{opts.MainModule}
+ }
+ matchPackages(ctx, m, opts.Tags, omitStd, matchModules)
} else {
// Starting with the packages in the main module,
// enumerate the full list of "all".
@@ -324,7 +338,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
}
}
- initialRS, _ := loadModFile(ctx) // Ignore needCommit — we're going to commit at the end regardless.
+ initialRS := LoadModFile(ctx)
ld := loadFromRoots(ctx, loaderParams{
PackageOpts: opts,
@@ -365,7 +379,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
for _, m := range initialRS.rootModules {
var unused bool
- if ld.requirements.depth == eager {
+ if ld.requirements.pruning == unpruned {
// m is unused if it was dropped from the module graph entirely. If it
// was only demoted from direct to indirect, it may still be in use via
// a transitive import.
@@ -384,7 +398,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
}
keep := keepSums(ctx, ld, ld.requirements, loadedZipSumsOnly)
- if compatDepth := modDepthFromGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.depth {
+ if compatDepth := pruningForGoVersion(ld.TidyCompatibleVersion); compatDepth != ld.requirements.pruning {
compatRS := newRequirements(compatDepth, ld.requirements.rootModules, ld.requirements.direct)
ld.checkTidyCompatibility(ctx, compatRS)
@@ -393,7 +407,7 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
}
}
- if allowWriteGoMod {
+ if !ExplicitWriteGoMod {
modfetch.TrimGoSum(keep)
// commitRequirements below will also call WriteGoSum, but the "keep" map
@@ -401,13 +415,24 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
// loaded.requirements, but here we may have also loaded (and want to
// preserve checksums for) additional entities from compatRS, which are
// only needed for compatibility with ld.TidyCompatibleVersion.
- modfetch.WriteGoSum(keep)
+ if err := modfetch.WriteGoSum(keep, mustHaveCompleteRequirements()); err != nil {
+ base.Fatalf("go: %v", err)
+ }
+ }
+
+ // Update the go.mod file's Go version if necessary.
+ modFile := MainModules.ModFile(MainModules.mustGetSingleMainModule())
+ if ld.GoVersion != "" {
+ modFile.AddGoStmt(ld.GoVersion)
}
}
// Success! Update go.mod and go.sum (if needed) and return the results.
+ // We'll skip updating if ExplicitWriteGoMod is true (the caller has opted
+ // to call WriteGoMod itself) or if ResolveMissingImports is false (the
+ // command wants to examine the package graph as-is).
loaded = ld
- commitRequirements(ctx, loaded.GoVersion, loaded.requirements)
+ requirements = loaded.requirements
for _, pkg := range ld.pkgs {
if !pkg.isTest() {
@@ -415,12 +440,19 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
}
}
sort.Strings(loadedPackages)
+
+ if !ExplicitWriteGoMod && opts.ResolveMissingImports {
+ if err := commitRequirements(ctx); err != nil {
+ base.Fatalf("go: %v", err)
+ }
+ }
+
return matches, loadedPackages
}
// matchLocalDirs is like m.MatchDirs, but tries to avoid scanning directories
// outside of the standard library and active modules.
-func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) {
+func matchLocalDirs(ctx context.Context, modRoots []string, m *search.Match, rs *Requirements) {
if !m.IsLocal() {
panic(fmt.Sprintf("internal error: resolveLocalDirs on non-local pattern %s", m.Pattern()))
}
@@ -436,14 +468,23 @@ func matchLocalDirs(ctx context.Context, m *search.Match, rs *Requirements) {
if !filepath.IsAbs(dir) {
absDir = filepath.Join(base.Cwd(), dir)
}
- if search.InDir(absDir, cfg.GOROOTsrc) == "" && search.InDir(absDir, ModRoot()) == "" && pathInModuleCache(ctx, absDir, rs) == "" {
+
+ modRoot := findModuleRoot(absDir)
+ found := false
+ for _, mainModuleRoot := range modRoots {
+ if mainModuleRoot == modRoot {
+ found = true
+ break
+ }
+ }
+ if !found && search.InDir(absDir, cfg.GOROOTsrc) == "" && pathInModuleCache(ctx, absDir, rs) == "" {
m.Dirs = []string{}
m.AddError(fmt.Errorf("directory prefix %s outside available modules", base.ShortPath(absDir)))
return
}
}
- m.MatchDirs()
+ m.MatchDirs(modRoots)
}
// resolveLocalPackage resolves a filesystem path to a package path.
@@ -485,49 +526,69 @@ func resolveLocalPackage(ctx context.Context, dir string, rs *Requirements) (str
}
}
- if modRoot != "" && absDir == modRoot {
- if absDir == cfg.GOROOTsrc {
- return "", errPkgIsGorootSrc
+ for _, mod := range MainModules.Versions() {
+ modRoot := MainModules.ModRoot(mod)
+ if modRoot != "" && absDir == modRoot {
+ if absDir == cfg.GOROOTsrc {
+ return "", errPkgIsGorootSrc
+ }
+ return MainModules.PathPrefix(mod), nil
}
- return targetPrefix, nil
}
// Note: The checks for @ here are just to avoid misinterpreting
// the module cache directories (formerly GOPATH/src/mod/foo@v1.5.2/bar).
// It's not strictly necessary but helpful to keep the checks.
- if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
- suffix := filepath.ToSlash(absDir[len(modRoot):])
- if strings.HasPrefix(suffix, "/vendor/") {
- if cfg.BuildMod != "vendor" {
- return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
+ var pkgNotFoundErr error
+ pkgNotFoundLongestPrefix := ""
+ for _, mainModule := range MainModules.Versions() {
+ modRoot := MainModules.ModRoot(mainModule)
+ if modRoot != "" && strings.HasPrefix(absDir, modRoot+string(filepath.Separator)) && !strings.Contains(absDir[len(modRoot):], "@") {
+ suffix := filepath.ToSlash(absDir[len(modRoot):])
+ if strings.HasPrefix(suffix, "/vendor/") {
+ if cfg.BuildMod != "vendor" {
+ return "", fmt.Errorf("without -mod=vendor, directory %s has no package path", absDir)
+ }
+
+ readVendorList(mainModule)
+ pkg := strings.TrimPrefix(suffix, "/vendor/")
+ if _, ok := vendorPkgModule[pkg]; !ok {
+ return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir)
+ }
+ return pkg, nil
}
- readVendorList()
- pkg := strings.TrimPrefix(suffix, "/vendor/")
- if _, ok := vendorPkgModule[pkg]; !ok {
- return "", fmt.Errorf("directory %s is not a package listed in vendor/modules.txt", absDir)
+ mainModulePrefix := MainModules.PathPrefix(mainModule)
+ if mainModulePrefix == "" {
+ pkg := strings.TrimPrefix(suffix, "/")
+ if pkg == "builtin" {
+ // "builtin" is a pseudo-package with a real source file.
+ // It's not included in "std", so it shouldn't resolve from "."
+ // within module "std" either.
+ return "", errPkgIsBuiltin
+ }
+ return pkg, nil
}
- return pkg, nil
- }
- if targetPrefix == "" {
- pkg := strings.TrimPrefix(suffix, "/")
- if pkg == "builtin" {
- // "builtin" is a pseudo-package with a real source file.
- // It's not included in "std", so it shouldn't resolve from "."
- // within module "std" either.
- return "", errPkgIsBuiltin
+ pkg := mainModulePrefix + suffix
+ if _, ok, err := dirInModule(pkg, mainModulePrefix, modRoot, true); err != nil {
+ return "", err
+ } else if !ok {
+ // This main module could contain the directory but doesn't. Other main
+ // modules might contain the directory, so wait till we finish the loop
+ // to see if another main module contains directory. But if not,
+ // return an error.
+ if len(mainModulePrefix) > len(pkgNotFoundLongestPrefix) {
+ pkgNotFoundLongestPrefix = mainModulePrefix
+ pkgNotFoundErr = &PackageNotInModuleError{MainModules: []module.Version{mainModule}, Pattern: pkg}
+ }
+ continue
}
return pkg, nil
}
-
- pkg := targetPrefix + suffix
- if _, ok, err := dirInModule(pkg, targetPrefix, modRoot, true); err != nil {
- return "", err
- } else if !ok {
- return "", &PackageNotInModuleError{Mod: Target, Pattern: pkg}
- }
- return pkg, nil
+ }
+ if pkgNotFoundErr != nil {
+ return "", pkgNotFoundErr
}
if sub := search.InDir(absDir, cfg.GOROOTsrc); sub != "" && sub != "." && !strings.Contains(sub, "@") {
@@ -560,7 +621,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string
if repl := Replacement(m); repl.Path != "" && repl.Version == "" {
root = repl.Path
if !filepath.IsAbs(root) {
- root = filepath.Join(ModRoot(), root)
+ root = filepath.Join(replaceRelativeTo(), root)
}
} else if repl.Path != "" {
root, err = modfetch.DownloadDir(repl)
@@ -583,7 +644,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string
return path.Join(m.Path, filepath.ToSlash(sub)), true
}
- if rs.depth == lazy {
+ if rs.pruning == pruned {
for _, m := range rs.rootModules {
if v, _ := rs.rootSelected(m.Path); v != m.Version {
continue // m is a root, but we have a higher root for the same path.
@@ -596,9 +657,9 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string
}
}
- // None of the roots contained dir, or we're in eager mode and want to load
- // the full module graph more aggressively. Either way, check the full graph
- // to see if the directory is a non-root dependency.
+ // None of the roots contained dir, or the graph is unpruned (so we don't want
+ // to distinguish between roots and transitive dependencies). Either way,
+ // check the full graph to see if the directory is a non-root dependency.
//
// If the roots are not consistent with the full module graph, the selected
// versions of root modules may differ from what we already checked above.
@@ -645,14 +706,14 @@ func ImportFromFiles(ctx context.Context, gofiles []string) {
return roots
},
})
- commitRequirements(ctx, loaded.GoVersion, loaded.requirements)
+ requirements = loaded.requirements
}
// DirImportPath returns the effective import path for dir,
-// provided it is within the main module, or else returns ".".
-func DirImportPath(ctx context.Context, dir string) string {
+// provided it is within a main module, or else returns ".".
+func (mms *MainModuleSet) DirImportPath(ctx context.Context, dir string) (path string, m module.Version) {
if !HasModRoot() {
- return "."
+ return ".", module.Version{}
}
LoadModFile(ctx) // Sets targetPrefix.
@@ -662,17 +723,32 @@ func DirImportPath(ctx context.Context, dir string) string {
dir = filepath.Clean(dir)
}
- if dir == modRoot {
- return targetPrefix
- }
- if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
- suffix := filepath.ToSlash(dir[len(modRoot):])
- if strings.HasPrefix(suffix, "/vendor/") {
- return strings.TrimPrefix(suffix, "/vendor/")
+ var longestPrefix string
+ var longestPrefixPath string
+ var longestPrefixVersion module.Version
+ for _, v := range mms.Versions() {
+ modRoot := mms.ModRoot(v)
+ if dir == modRoot {
+ return mms.PathPrefix(v), v
+ }
+ if strings.HasPrefix(dir, modRoot+string(filepath.Separator)) {
+ pathPrefix := MainModules.PathPrefix(v)
+ if pathPrefix > longestPrefix {
+ longestPrefix = pathPrefix
+ longestPrefixVersion = v
+ suffix := filepath.ToSlash(dir[len(modRoot):])
+ if strings.HasPrefix(suffix, "/vendor/") {
+ longestPrefixPath = strings.TrimPrefix(suffix, "/vendor/")
+ }
+ longestPrefixPath = mms.PathPrefix(v) + suffix
+ }
}
- return targetPrefix + suffix
}
- return "."
+ if len(longestPrefix) > 0 {
+ return longestPrefixPath, longestPrefixVersion
+ }
+
+ return ".", module.Version{}
}
// ImportMap returns the actual package import path
@@ -783,7 +859,7 @@ func (ld *loader) reset() {
// errorf reports an error via either os.Stderr or base.Errorf,
// according to whether ld.AllowErrors is set.
-func (ld *loader) errorf(format string, args ...interface{}) {
+func (ld *loader) errorf(format string, args ...any) {
if ld.AllowErrors {
fmt.Fprintf(os.Stderr, format, args...)
} else {
@@ -807,6 +883,7 @@ type loadPkg struct {
imports []*loadPkg // packages imported by this one
testImports []string // test-only imports, saved for use by pkg.test.
inStd bool
+ altMods []module.Version // modules that could have contained the package but did not
// Populated by (*loader).pkgTest:
testOnce sync.Once
@@ -894,10 +971,7 @@ func (pkg *loadPkg) fromExternalModule() bool {
if pkg.mod.Path == "" {
return false // loaded from the standard library, not a module
}
- if pkg.mod.Path == Target.Path {
- return false // loaded from the main module.
- }
- return true
+ return !MainModules.Contains(pkg.mod.Path)
}
var errMissing = errors.New("cannot find package")
@@ -915,10 +989,10 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
}
if ld.GoVersion == "" {
- ld.GoVersion = modFileGoVersion()
+ ld.GoVersion = MainModules.GoVersion()
if ld.Tidy && semver.Compare("v"+ld.GoVersion, "v"+LatestGoVersion()) > 0 {
- ld.errorf("go mod tidy: go.mod file indicates go %s, but maximum supported version is %s\n", ld.GoVersion, LatestGoVersion())
+ ld.errorf("go: go.mod file indicates go %s, but maximum version supported by tidy is %s\n", ld.GoVersion, LatestGoVersion())
base.ExitIfErrors()
}
}
@@ -935,18 +1009,30 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
}
if semver.Compare("v"+ld.GoVersion, narrowAllVersionV) < 0 && !ld.UseVendorAll {
- // The module's go version explicitly predates the change in "all" for lazy
- // loading, so continue to use the older interpretation.
+ // The module's go version explicitly predates the change in "all" for graph
+ // pruning, so continue to use the older interpretation.
ld.allClosesOverTests = true
}
var err error
- ld.requirements, err = convertDepth(ctx, ld.requirements, modDepthFromGoVersion(ld.GoVersion))
+ desiredPruning := pruningForGoVersion(ld.GoVersion)
+ if ld.requirements.pruning == workspace {
+ desiredPruning = workspace
+ }
+ ld.requirements, err = convertPruning(ctx, ld.requirements, desiredPruning)
if err != nil {
ld.errorf("go: %v\n", err)
}
- if ld.requirements.depth == eager {
+ if ld.requirements.pruning == unpruned {
+ // If the module graph does not support pruning, we assume that we will need
+ // the full module graph in order to load package dependencies.
+ //
+ // This might not be strictly necessary, but it matches the historical
+ // behavior of the 'go' command and keeps the go.mod file more consistent in
+ // case of erroneous hand-edits — which are less likely to be detected by
+ // spot-checks in modules that do not maintain the expanded go.mod
+ // requirements needed for graph pruning.
var err error
ld.requirements, _, err = expandGraph(ctx, ld.requirements)
if err != nil {
@@ -963,7 +1049,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
// build list we're using.
rootPkgs := ld.listRoots(ld.requirements)
- if ld.requirements.depth == lazy && cfg.BuildMod == "mod" {
+ if ld.requirements.pruning == pruned && cfg.BuildMod == "mod" {
// Before we start loading transitive imports of packages, locate all of
// the root packages and promote their containing modules to root modules
// dependencies. If their go.mod files are tidy (the common case) and the
@@ -1005,7 +1091,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
break
}
if changed {
- // Don't resolve missing imports until the module graph have stabilized.
+ // Don't resolve missing imports until the module graph has stabilized.
// If the roots are still changing, they may turn out to specify a
// requirement on the missing package(s), and we would rather use a
// version specified by a new root than add a new dependency on an
@@ -1074,15 +1160,15 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
ld.errorf("go: %v\n", err)
}
- if ld.requirements.depth == lazy {
+ if ld.requirements.pruning == pruned {
// We continuously add tidy roots to ld.requirements during loading, so at
// this point the tidy roots should be a subset of the roots of
// ld.requirements, ensuring that no new dependencies are brought inside
- // the lazy-loading horizon.
+ // the graph-pruning horizon.
// If that is not the case, there is a bug in the loading loop above.
for _, m := range rs.rootModules {
if v, ok := ld.requirements.rootSelected(m.Path); !ok || v != m.Version {
- ld.errorf("go mod tidy: internal error: a requirement on %v is needed but was not added during package loading\n", m)
+ ld.errorf("go: internal error: a requirement on %v is needed but was not added during package loading\n", m)
base.ExitIfErrors()
}
}
@@ -1124,8 +1210,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
}
// updateRequirements ensures that ld.requirements is consistent with the
-// information gained from ld.pkgs and includes the modules in add as roots at
-// at least the given versions.
+// information gained from ld.pkgs.
//
// In particular:
//
@@ -1168,7 +1253,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
}
for _, pkg := range ld.pkgs {
- if pkg.mod != Target {
+ if pkg.mod.Version != "" || !MainModules.Contains(pkg.mod.Path) {
continue
}
for _, dep := range pkg.imports {
@@ -1176,6 +1261,24 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
continue
}
+ if inWorkspaceMode() {
+ // In workspace mode / workspace pruning mode, the roots are the main modules
+ // rather than the main module's direct dependencies. The check below on the selected
+ // roots does not apply.
+ if mg, err := rs.Graph(ctx); err != nil {
+ return false, err
+ } else if _, ok := mg.RequiredBy(dep.mod); !ok {
+ // dep.mod is not an explicit dependency, but needs to be.
+ // See comment on error returned below.
+ pkg.err = &DirectImportFromImplicitDependencyError{
+ ImporterPath: pkg.path,
+ ImportedPath: dep.path,
+ Module: dep.mod,
+ }
+ }
+ continue
+ }
+
if pkg.err == nil && cfg.BuildMod != "mod" {
if v, ok := rs.rootSelected(dep.mod.Path); !ok || v != dep.mod.Version {
// dep.mod is not an explicit dependency, but needs to be.
@@ -1206,14 +1309,14 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
var addRoots []module.Version
if ld.Tidy {
- // When we are tidying a lazy module, we may need to add roots to preserve
- // the versions of indirect, test-only dependencies that are upgraded
- // above or otherwise missing from the go.mod files of direct
- // dependencies. (For example, the direct dependency might be a very
+ // When we are tidying a module with a pruned dependency graph, we may need
+ // to add roots to preserve the versions of indirect, test-only dependencies
+ // that are upgraded above or otherwise missing from the go.mod files of
+ // direct dependencies. (For example, the direct dependency might be a very
// stable codebase that predates modules and thus lacks a go.mod file, or
- // the author of the direct dependency may have forgotten to commit a
- // change to the go.mod file, or may have made an erroneous hand-edit that
- // causes it to be untidy.)
+ // the author of the direct dependency may have forgotten to commit a change
+ // to the go.mod file, or may have made an erroneous hand-edit that causes
+ // it to be untidy.)
//
// Promoting an indirect dependency to a root adds the next layer of its
// dependencies to the module graph, which may increase the selected
@@ -1283,7 +1386,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
//
// In some sense, we can think of this as ‘upgraded the module providing
// pkg.path from "none" to a version higher than "none"’.
- if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
+ if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
changed = true
break
}
@@ -1327,6 +1430,15 @@ func (ld *loader) resolveMissingImports(ctx context.Context) (modAddedBy map[mod
var err error
mod, err = queryImport(ctx, pkg.path, ld.requirements)
if err != nil {
+ var ime *ImportMissingError
+ if errors.As(err, &ime) {
+ for curstack := pkg.stack; curstack != nil; curstack = curstack.stack {
+ if MainModules.Contains(curstack.mod.Path) {
+ ime.ImportingMainModule = curstack.mod
+ break
+ }
+ }
+ }
// pkg.err was already non-nil, so we can reasonably attribute the error
// for pkg to either the original error or the one returned by
// queryImport. The existing error indicates only that we couldn't find
@@ -1380,7 +1492,7 @@ func (ld *loader) pkg(ctx context.Context, path string, flags loadPkgFlags) *loa
panic("internal error: (*loader).pkg called with pkgImportsLoaded flag set")
}
- pkg := ld.pkgCache.Do(path, func() interface{} {
+ pkg := ld.pkgCache.Do(path, func() any {
pkg := &loadPkg{
path: path,
}
@@ -1425,7 +1537,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg
// so it's ok if we call it more than is strictly necessary.
wantTest := false
switch {
- case ld.allPatternIsRoot && pkg.mod == Target:
+ case ld.allPatternIsRoot && MainModules.Contains(pkg.mod.Path):
// We are loading the "all" pattern, which includes packages imported by
// tests in the main module. This package is in the main module, so we
// need to identify the imports of its test even if LoadTests is not set.
@@ -1446,7 +1558,7 @@ func (ld *loader) applyPkgFlags(ctx context.Context, pkg *loadPkg, flags loadPkg
if wantTest {
var testFlags loadPkgFlags
- if pkg.mod == Target || (ld.allClosesOverTests && new.has(pkgInAll)) {
+ if MainModules.Contains(pkg.mod.Path) || (ld.allClosesOverTests && new.has(pkgInAll)) {
// Tests of packages in the main module are in "all", in the sense that
// they cause the packages they import to also be in "all". So are tests
// of packages in "all" if "all" closes over test dependencies.
@@ -1485,7 +1597,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
// If the main module is tidy and the package is in "all" — or if we're
// lucky — we can identify all of its imports without actually loading the
// full module graph.
- m, _, err := importFromModules(ctx, path, ld.requirements, nil)
+ m, _, _, err := importFromModules(ctx, path, ld.requirements, nil)
if err != nil {
var missing *ImportMissingError
if errors.As(err, &missing) && ld.ResolveMissingImports {
@@ -1511,7 +1623,8 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
// module to a root to ensure that any other packages this package
// imports are resolved from correct dependency versions.
//
- // (This is the “argument invariant” from the lazy loading design.)
+ // (This is the “argument invariant” from
+ // https://golang.org/design/36460-lazy-module-loading.)
need := <-needc
need[m] = true
needc <- need
@@ -1573,7 +1686,7 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
}
var mg *ModuleGraph
- if ld.requirements.depth == eager {
+ if ld.requirements.pruning == unpruned {
var err error
mg, err = ld.requirements.Graph(ctx)
if err != nil {
@@ -1589,11 +1702,11 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
}
}
- pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
+ pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
if pkg.dir == "" {
return
}
- if pkg.mod == Target {
+ if MainModules.Contains(pkg.mod.Path) {
// Go ahead and mark pkg as in "all". This provides the invariant that a
// package that is *only* imported by other packages in "all" is always
// marked as such before loading its imports.
@@ -1698,13 +1811,14 @@ func (ld *loader) stdVendor(parentPath, path string) string {
}
if str.HasPathPrefix(parentPath, "cmd") {
- if !ld.VendorModulesInGOROOTSrc || Target.Path != "cmd" {
+ if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("cmd") {
vendorPath := pathpkg.Join("cmd", "vendor", path)
+
if _, err := os.Stat(filepath.Join(cfg.GOROOTsrc, filepath.FromSlash(vendorPath))); err == nil {
return vendorPath
}
}
- } else if !ld.VendorModulesInGOROOTSrc || Target.Path != "std" || str.HasPathPrefix(parentPath, "vendor") {
+ } else if !ld.VendorModulesInGOROOTSrc || !MainModules.Contains("std") || str.HasPathPrefix(parentPath, "vendor") {
// If we are outside of the 'std' module, resolve imports from within 'std'
// to the vendor directory.
//
@@ -1781,7 +1895,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
fmt.Fprintln(os.Stderr)
goFlag := ""
- if ld.GoVersion != modFileGoVersion() {
+ if ld.GoVersion != MainModules.GoVersion() {
goFlag = " -go=" + ld.GoVersion
}
@@ -1813,7 +1927,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
mg, err := rs.Graph(ctx)
if err != nil {
- ld.errorf("go mod tidy: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err)
+ ld.errorf("go: error loading go %s module graph: %v\n", ld.TidyCompatibleVersion, err)
suggestFixes()
return
}
@@ -1847,7 +1961,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
pkg := pkg
ld.work.Add(func() {
- mod, _, err := importFromModules(ctx, pkg.path, rs, mg)
+ mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg)
if mod != pkg.mod {
mismatches := <-mismatchMu
mismatches[pkg] = mismatch{mod: mod, err: err}
@@ -1900,9 +2014,10 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
case mismatch.err != nil:
// pkg resolved successfully, but errors out using the requirements in rs.
//
- // This could occur because the import is provided by a single lazy root
- // (and is thus unambiguous in lazy mode) and also one or more
- // transitive dependencies (and is ambiguous in eager mode).
+ // This could occur because the import is provided by a single root (and
+ // is thus unambiguous in a main module with a pruned module graph) and
+ // also one or more transitive dependencies (and is ambiguous with an
+ // unpruned graph).
//
// It could also occur because some transitive dependency upgrades the
// module that previously provided the package to a version that no
@@ -1940,18 +2055,18 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
}
case pkg.err != nil:
- // pkg had an error in lazy mode (presumably suppressed with the -e flag),
- // but not in eager mode.
+ // pkg had an error in with a pruned module graph (presumably suppressed
+ // with the -e flag), but the error went away using an unpruned graph.
//
- // This is possible, if, say, the import is unresolved in lazy mode
+ // This is possible, if, say, the import is unresolved in the pruned graph
// (because the "latest" version of each candidate module either is
- // unavailable or does not contain the package), but is resolved in
- // eager mode due to a newer-than-latest dependency that is normally
- // runed out of the module graph.
+ // unavailable or does not contain the package), but is resolved in the
+ // unpruned graph due to a newer-than-latest dependency that is normally
+ // pruned out.
//
// This could also occur if the source code for the module providing the
- // package in lazy mode has a checksum error, but eager mode upgrades
- // that module to a version with a correct checksum.
+ // package in the pruned graph has a checksum error, but the unpruned
+ // graph upgrades that module to a version with a correct checksum.
//
// pkg.err should have already been logged elsewhere — along with a
// stack trace — so log only the import path and non-error info here.
diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go
index 03e02e73b63f659f0ec6e3eff89299f691763c6d..ec3f57ae3e1da513ae6a3832489beb8f49d0460c 100644
--- a/src/cmd/go/internal/modload/modfile.go
+++ b/src/cmd/go/internal/modload/modfile.go
@@ -33,10 +33,13 @@ const (
// tests outside of the main module.
narrowAllVersionV = "v1.16"
- // lazyLoadingVersionV 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.
- lazyLoadingVersionV = "v1.17"
+ //
+ // 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"
// separateIndirectVersionV is the Go version (plus leading "v") at which
// "// indirect" dependencies are added in a block separate from the direct
@@ -44,23 +47,37 @@ const (
separateIndirectVersionV = "v1.17"
)
-const (
- // go117EnableLazyLoading toggles whether lazy-loading code paths should be
- // active. It will be removed once the lazy loading implementation is stable
- // and well-tested.
- go117EnableLazyLoading = true
-
- // go1117LazyTODO is a constant that exists only until lazy loading is
- // implemented. Its use indicates a condition that will need to change if the
- // main module is lazy.
- go117LazyTODO = false
-)
+// ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the
+// overlay, locks the file while reading, and applies fix, if applicable.
+func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) {
+ if gomodActual, ok := fsys.OverlayPath(gomod); ok {
+ // Don't lock go.mod if it's part of the overlay.
+ // On Plan 9, locking requires chmod, and we don't want to modify any file
+ // in the overlay. See #44700.
+ data, err = os.ReadFile(gomodActual)
+ } else {
+ data, err = lockedfile.Read(gomodActual)
+ }
+ if err != nil {
+ return nil, nil, err
+ }
-var modFile *modfile.File
+ f, err = modfile.Parse(gomod, data, fix)
+ if err != nil {
+ // Errors returned by modfile.Parse begin with file:line.
+ return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err)
+ }
+ if f.Module == nil {
+ // No module declaration. Must add module path.
+ return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
+ }
+
+ return data, f, err
+}
// modFileGoVersion returns the (non-empty) Go version at which the requirements
-// in modFile are intepreted, or the latest Go version if modFile is nil.
-func modFileGoVersion() string {
+// in modFile are interpreted, or the latest Go version if modFile is nil.
+func modFileGoVersion(modFile *modfile.File) string {
if modFile == nil {
return LatestGoVersion()
}
@@ -71,9 +88,9 @@ func modFileGoVersion() string {
// has been erroneously hand-edited.
//
// The semantics of the go.mod file are more-or-less the same from Go 1.11
- // through Go 1.16, changing at 1.17 for lazy loading. So even though a
- // go.mod file without a 'go' directive is theoretically a Go 1.11 file,
- // scripts may assume that it ends up as a Go 1.16 module.
+ // through Go 1.16, changing at 1.17 to support module graph pruning.
+ // So even though a go.mod file without a 'go' directive is theoretically a
+ // Go 1.11 file, scripts may assume that it ends up as a Go 1.16 module.
return "1.16"
}
return modFile.Go.Version
@@ -82,39 +99,37 @@ func modFileGoVersion() 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
}
-// index is the index of the go.mod file as of when it was last read or written.
-var index *modFileIndex
-
type requireMeta struct {
indirect bool
}
-// A modDepth indicates which dependencies should be loaded for a go.mod file.
-type modDepth uint8
+// A modPruning indicates whether transitive dependencies of Go 1.17 dependencies
+// are pruned out of the module subgraph rooted at a given module.
+// (See https://golang.org/ref/mod#graph-pruning.)
+type modPruning uint8
const (
- lazy modDepth = iota // load dependencies only as needed
- eager // load all transitive dependencies eagerly
+ pruned modPruning = iota // transitive dependencies of modules at go 1.17 and higher are pruned out
+ unpruned // no transitive dependencies are pruned out
+ workspace // pruned to the union of modules in the workspace
)
-func modDepthFromGoVersion(goVersion string) modDepth {
- if !go117EnableLazyLoading {
- return eager
- }
- if semver.Compare("v"+goVersion, lazyLoadingVersionV) < 0 {
- return eager
+func pruningForGoVersion(goVersion string) modPruning {
+ 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
}
- return lazy
+ return pruned
}
// CheckAllowed returns an error equivalent to ErrDisallowed if m is excluded by
@@ -137,8 +152,10 @@ var ErrDisallowed = errors.New("disallowed module version")
// CheckExclusions returns an error equivalent to ErrDisallowed if module m is
// excluded by the main module's go.mod file.
func CheckExclusions(ctx context.Context, m module.Version) error {
- if index != nil && index.exclude[m] {
- return module.VersionError(m, errExcluded)
+ for _, mainModule := range MainModules.Versions() {
+ if index := MainModules.Index(mainModule); index != nil && index.exclude[m] {
+ return module.VersionError(m, errExcluded)
+ }
}
return nil
}
@@ -306,23 +323,74 @@ func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string
return summary.deprecated, nil
}
-// Replacement returns the replacement for mod, if any, from go.mod.
-// If there is no replacement for mod, Replacement returns
-// a module.Version with Path == "".
+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 := replace[module.Version{Path: mod.Path}]; ok {
+ return "", r, true
+ }
+ return "", module.Version{}, false
+}
+
+// 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 {
- if index != nil {
- if r, ok := index.replace[mod]; ok {
- return r
- }
- if r, ok := index.replace[module.Version{Path: mod.Path}]; ok {
- return r
+ 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.replace); ok {
+ modRoot := MainModules.ModRoot(v)
+ if foundModRoot != "" && foundFrom != from && found != r {
+ base.Errorf("conflicting replacements found for %v in workspace modules defined by %v and %v",
+ mod, modFilePath(foundModRoot), modFilePath(modRoot))
+ return canonicalizeReplacePath(found, foundModRoot)
+ }
+ found, foundModRoot = r, modRoot
+ }
}
}
- return module.Version{}
+ 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(filepath.Dir(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 {
if r := Replacement(m); r.Path != "" {
return r
@@ -330,10 +398,21 @@ func resolveReplacement(m module.Version) module.Version {
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.
// If modFile has been changed since it was first read,
// modFile.Cleanup must be called before indexModFile.
-func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileIndex {
+func indexModFile(data []byte, modFile *modfile.File, mod module.Version, needsFix bool) *modFileIndex {
i := new(modFileIndex)
i.data = data
i.dataNeedsFix = needsFix
@@ -345,12 +424,12 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
i.goVersionV = ""
if modFile.Go == nil {
- rawGoVersion.Store(Target, "")
+ rawGoVersion.Store(mod, "")
} else {
// We're going to use the semver package to compare Go versions, so go ahead
// and add the "v" prefix it expects once instead of every time.
i.goVersionV = "v" + modFile.Go.Version
- rawGoVersion.Store(Target, modFile.Go.Version)
+ rawGoVersion.Store(mod, modFile.Go.Version)
}
i.require = make(map[module.Version]requireMeta, len(modFile.Require))
@@ -358,21 +437,7 @@ func indexModFile(data []byte, modFile *modfile.File, needsFix bool) *modFileInd
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 {
@@ -465,7 +530,7 @@ var rawGoVersion sync.Map // map[module.Version]string
type modFileSummary struct {
module module.Version
goVersion string
- depth modDepth
+ pruning modPruning
require []module.Version
retract []retraction
deprecated string
@@ -490,8 +555,8 @@ type retraction struct {
//
// The caller must not modify the returned summary.
func goModSummary(m module.Version) (*modFileSummary, error) {
- if m == Target {
- panic("internal error: goModSummary called on the Target module")
+ if m.Version == "" && !inWorkspaceMode() && MainModules.Contains(m.Path) {
+ panic("internal error: goModSummary called on a main module")
}
if cfg.BuildMod == "vendor" {
@@ -506,7 +571,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
// For every module other than the target,
// return the full list of modules from modules.txt.
- readVendorList()
+ readVendorList(MainModules.mustGetSingleMainModule())
// We don't know what versions the vendored module actually relies on,
// so assume that it requires everything.
@@ -515,7 +580,7 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
}
actual := resolveReplacement(m)
- if HasModRoot() && cfg.BuildMod == "readonly" && actual.Version != "" {
+ if HasModRoot() && cfg.BuildMod == "readonly" && !inWorkspaceMode() && actual.Version != "" {
key := module.Version{Path: actual.Path, Version: actual.Version + "/go.mod"}
if !modfetch.HaveSum(key) {
suggestion := fmt.Sprintf("; to add it:\n\tgo mod download %s", m.Path)
@@ -553,27 +618,29 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
}
}
- if index != nil && len(index.exclude) > 0 {
- // Drop any requirements on excluded versions.
- // Don't modify the cached summary though, since we might need the raw
- // summary separately.
- haveExcludedReqs := false
- for _, r := range summary.require {
- if index.exclude[r] {
- haveExcludedReqs = true
- break
- }
- }
- if haveExcludedReqs {
- s := new(modFileSummary)
- *s = *summary
- s.require = make([]module.Version, 0, len(summary.require))
+ for _, mainModule := range MainModules.Versions() {
+ if index := MainModules.Index(mainModule); index != nil && len(index.exclude) > 0 {
+ // Drop any requirements on excluded versions.
+ // Don't modify the cached summary though, since we might need the raw
+ // summary separately.
+ haveExcludedReqs := false
for _, r := range summary.require {
- if !index.exclude[r] {
- s.require = append(s.require, r)
+ if index.exclude[r] {
+ haveExcludedReqs = true
+ break
+ }
+ }
+ if haveExcludedReqs {
+ s := new(modFileSummary)
+ *s = *summary
+ s.require = make([]module.Version, 0, len(summary.require))
+ for _, r := range summary.require {
+ if !index.exclude[r] {
+ s.require = append(s.require, r)
+ }
}
+ summary = s
}
- summary = s
}
}
return summary, nil
@@ -584,16 +651,20 @@ func goModSummary(m module.Version) (*modFileSummary, error) {
// its dependencies.
//
// rawGoModSummary cannot be used on the Target module.
+
func rawGoModSummary(m module.Version) (*modFileSummary, error) {
- if m == Target {
+ if m.Path == "" && MainModules.Contains(m.Path) {
panic("internal error: rawGoModSummary called on the Target module")
}
+ type key struct {
+ m module.Version
+ }
type cached struct {
summary *modFileSummary
err error
}
- c := rawGoModSummaryCache.Do(m, func() interface{} {
+ c := rawGoModSummaryCache.Do(key{m}, func() any {
summary := new(modFileSummary)
name, data, err := rawGoModData(m)
if err != nil {
@@ -610,9 +681,9 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
if f.Go != nil && f.Go.Version != "" {
rawGoVersion.LoadOrStore(m, f.Go.Version)
summary.goVersion = f.Go.Version
- summary.depth = modDepthFromGoVersion(f.Go.Version)
+ summary.pruning = pruningForGoVersion(f.Go.Version)
} else {
- summary.depth = eager
+ summary.pruning = unpruned
}
if len(f.Require) > 0 {
summary.require = make([]module.Version, 0, len(f.Require))
@@ -648,9 +719,14 @@ var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
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) {
- dir = filepath.Join(ModRoot(), dir)
+ if inWorkspaceMode() && MainModules.Contains(m.Path) {
+ dir = MainModules.ModRoot(m)
+ } else {
+ dir = filepath.Join(replaceRelativeTo(), dir)
+ }
}
name = filepath.Join(dir, "go.mod")
if gomodActual, ok := fsys.OverlayPath(name); ok {
@@ -690,7 +766,7 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la
latest module.Version
err error
}
- e := latestVersionIgnoringRetractionsCache.Do(path, func() interface{} {
+ e := latestVersionIgnoringRetractionsCache.Do(path, func() any {
ctx, span := trace.StartSpan(ctx, "queryLatestVersionIgnoringRetractions "+path)
defer span.Done()
@@ -718,3 +794,15 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la
}
var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
+
+// ToDirectoryPath adds a prefix if necessary so that path in unambiguously
+// an absolute path or a relative path starting with a '.' or '..'
+// path component.
+func ToDirectoryPath(path string) string {
+ if modfile.IsDirectoryPath(path) {
+ return path
+ }
+ // The path is not a relative path or an absolute path, so make it relative
+ // to the current directory.
+ return "./" + filepath.ToSlash(filepath.Clean(path))
+}
diff --git a/src/cmd/go/internal/modload/mvs.go b/src/cmd/go/internal/modload/mvs.go
index 87619b4ace68e28d40621054ab4d437a4b97cf83..588bcf4bdc26453b41e53d2562ecbe66088ee0f3 100644
--- a/src/cmd/go/internal/modload/mvs.go
+++ b/src/cmd/go/internal/modload/mvs.go
@@ -42,7 +42,7 @@ type mvsReqs struct {
}
func (r *mvsReqs) Required(mod module.Version) ([]module.Version, error) {
- if mod == Target {
+ if mod.Version == "" && MainModules.Contains(mod.Path) {
// Use the build list as it existed when r was constructed, not the current
// global build list.
return r.roots, nil
@@ -108,12 +108,12 @@ func versions(ctx context.Context, path string, allowed AllowedFunc) ([]string,
// previousVersion returns the tagged version of m.Path immediately prior to
// m.Version, or version "none" if no prior version is tagged.
//
-// Since the version of Target is not found in the version list,
+// Since the version of a main module is not found in the version list,
// it has no previous version.
func previousVersion(m module.Version) (module.Version, error) {
// TODO(golang.org/issue/38714): thread tracing context through MVS.
- if m == Target {
+ if m.Version == "" && MainModules.Contains(m.Path) {
return module.Version{Path: m.Path, Version: "none"}, nil
}
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index e737ca90fcd79d911f741b7fc43e1aa663b97d4b..33808ea1097a7f3dc0f1b06a197fde3a09900eb6 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -110,11 +110,12 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
allowed = func(context.Context, module.Version) error { return nil }
}
- if path == Target.Path && (query == "upgrade" || query == "patch") {
- if err := allowed(ctx, Target); err != nil {
+ if MainModules.Contains(path) && (query == "upgrade" || query == "patch") {
+ m := module.Version{Path: path}
+ if err := allowed(ctx, m); err != nil {
return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
}
- return &modfetch.RevInfo{Version: Target.Version}, nil
+ return &modfetch.RevInfo{Version: m.Version}, nil
}
if path == "std" || path == "cmd" {
@@ -512,9 +513,10 @@ 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)
return nil, &PackageNotInModuleError{
Mod: modOnly.Mod,
- Replacement: Replacement(modOnly.Mod),
+ Replacement: replacement,
Query: query,
Pattern: pattern,
}
@@ -551,7 +553,7 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
return m.Errs[0]
}
- var match func(mod module.Version, root string, isLocal bool) *search.Match
+ var match func(mod module.Version, roots []string, isLocal bool) *search.Match
matchPattern := search.MatchPattern(pattern)
if i := strings.Index(pattern, "..."); i >= 0 {
@@ -559,30 +561,32 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
if base == "." {
return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
}
- match = func(mod module.Version, root string, isLocal bool) *search.Match {
+ match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
m := search.NewMatch(pattern)
matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
return m
}
} else {
- match = func(mod module.Version, root string, isLocal bool) *search.Match {
+ match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
m := search.NewMatch(pattern)
prefix := mod.Path
- if mod == Target {
- prefix = targetPrefix
+ if MainModules.Contains(mod.Path) {
+ prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
}
- if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
- m.AddError(err)
- } else if ok {
- m.Pkgs = []string{pattern}
+ for _, root := range roots {
+ if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
+ m.AddError(err)
+ } else if ok {
+ m.Pkgs = []string{pattern}
+ }
}
return m
}
}
- var queryMatchesMainModule bool
- if HasModRoot() {
- m := match(Target, modRoot, true)
+ var mainModuleMatches []module.Version
+ for _, mainModule := range MainModules.Versions() {
+ m := match(mainModule, modRoots, true)
if len(m.Pkgs) > 0 {
if query != "upgrade" && query != "patch" {
return nil, nil, &QueryMatchesPackagesInMainModuleError{
@@ -591,12 +595,12 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
Packages: m.Pkgs,
}
}
- if err := allowed(ctx, Target); err != nil {
- return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
+ if err := allowed(ctx, mainModule); err != nil {
+ return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err)
}
return []QueryResult{{
- Mod: Target,
- Rev: &modfetch.RevInfo{Version: Target.Version},
+ Mod: mainModule,
+ Rev: &modfetch.RevInfo{Version: mainModule.Version},
Packages: m.Pkgs,
}}, nil, nil
}
@@ -604,15 +608,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
return nil, nil, err
}
- if matchPattern(Target.Path) {
- queryMatchesMainModule = true
+ var matchesMainModule bool
+ if matchPattern(mainModule.Path) {
+ mainModuleMatches = append(mainModuleMatches, mainModule)
+ matchesMainModule = true
}
- if (query == "upgrade" || query == "patch") && queryMatchesMainModule {
- if err := allowed(ctx, Target); err == nil {
+ if (query == "upgrade" || query == "patch") && matchesMainModule {
+ if err := allowed(ctx, mainModule); err == nil {
modOnly = &QueryResult{
- Mod: Target,
- Rev: &modfetch.RevInfo{Version: Target.Version},
+ Mod: mainModule,
+ Rev: &modfetch.RevInfo{Version: mainModule.Version},
}
}
}
@@ -625,16 +631,17 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
if len(candidateModules) == 0 {
if modOnly != nil {
return nil, modOnly, nil
- } else if queryMatchesMainModule {
- return nil, nil, &QueryMatchesMainModuleError{
- Pattern: pattern,
- Query: query,
+ } else if len(mainModuleMatches) != 0 {
+ return nil, nil, &QueryMatchesMainModulesError{
+ MainModules: mainModuleMatches,
+ Pattern: pattern,
+ Query: query,
}
} else {
return nil, nil, &PackageNotInModuleError{
- Mod: Target,
- Query: query,
- Pattern: pattern,
+ MainModules: mainModuleMatches,
+ Query: query,
+ Pattern: pattern,
}
}
}
@@ -656,15 +663,16 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
if err != nil {
return r, err
}
- m := match(r.Mod, root, isLocal)
+ m := match(r.Mod, []string{root}, isLocal)
r.Packages = m.Pkgs
if len(r.Packages) == 0 && !matchPattern(path) {
if err := firstError(m); err != nil {
return r, err
}
+ replacement := Replacement(r.Mod)
return r, &PackageNotInModuleError{
Mod: r.Mod,
- Replacement: Replacement(r.Mod),
+ Replacement: replacement,
Query: query,
Pattern: pattern,
}
@@ -684,8 +692,8 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
return err
})
- if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
- return nil, nil, &QueryMatchesMainModuleError{
+ if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
+ return nil, nil, &QueryMatchesMainModulesError{
Pattern: pattern,
Query: query,
}
@@ -701,8 +709,13 @@ func QueryPattern(ctx context.Context, pattern, query string, current func(strin
func modulePrefixesExcludingTarget(path string) []string {
prefixes := make([]string, 0, strings.Count(path, "/")+1)
+ mainModulePrefixes := make(map[string]bool)
+ for _, m := range MainModules.Versions() {
+ mainModulePrefixes[m.Path] = true
+ }
+
for {
- if path != targetPrefix {
+ if !mainModulePrefixes[path] {
if _, _, ok := module.SplitPathVersion(path); ok {
prefixes = append(prefixes, path)
}
@@ -759,7 +772,7 @@ func queryPrefixModules(ctx context.Context, candidateModules []string, queryMod
case *PackageNotInModuleError:
// Given the option, prefer to attribute “package not in module”
// to modules other than the main one.
- if noPackage == nil || noPackage.Mod == Target {
+ if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) {
noPackage = rErr
}
case *NoMatchingVersionError:
@@ -878,6 +891,7 @@ func (e *WildcardInFirstElementError) Error() string {
// code for the versions it knows about, and thus did not have the opportunity
// to return a non-400 status code to suppress fallback.
type PackageNotInModuleError struct {
+ MainModules []module.Version
Mod module.Version
Replacement module.Version
Query string
@@ -885,11 +899,15 @@ type PackageNotInModuleError struct {
}
func (e *PackageNotInModuleError) Error() string {
- if e.Mod == Target {
+ if len(e.MainModules) > 0 {
+ prefix := "workspace modules do"
+ if len(e.MainModules) == 1 {
+ prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0])
+ }
if strings.Contains(e.Pattern, "...") {
- return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern)
+ return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
}
- return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern)
+ return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
}
found := ""
@@ -978,14 +996,13 @@ func lookupRepo(proxy, path string) (repo versionRepo, err error) {
repo = emptyRepo{path: path, err: err}
}
- if index == nil {
- return repo, err
- }
- if _, ok := index.highestReplaced[path]; !ok {
+ if MainModules == nil {
return repo, err
+ } else if _, ok := MainModules.HighestReplaced()[path]; ok {
+ return &replacementRepo{repo: repo}, nil
}
- return &replacementRepo{repo: repo}, nil
+ return repo, err
}
// An emptyRepo is a versionRepo that contains no versions.
@@ -1024,11 +1041,13 @@ func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
}
versions := repoVersions
- if index != nil && len(index.replace) > 0 {
- path := rr.ModulePath()
- for m, _ := range index.replace {
- if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
- versions = append(versions, m.Version)
+ for _, mm := range MainModules.Versions() {
+ if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 {
+ path := rr.ModulePath()
+ for m, _ := range index.replace {
+ if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
+ versions = append(versions, m.Version)
+ }
}
}
}
@@ -1046,7 +1065,16 @@ func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
info, err := rr.repo.Stat(rev)
- if err == nil || index == nil || len(index.replace) == 0 {
+ if err == nil {
+ return info, err
+ }
+ var hasReplacements bool
+ for _, v := range MainModules.Versions() {
+ if index := MainModules.Index(v); index != nil && len(index.replace) > 0 {
+ hasReplacements = true
+ }
+ }
+ if !hasReplacements {
return info, err
}
@@ -1073,26 +1101,24 @@ func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
info, err := rr.repo.Latest()
+ path := rr.ModulePath()
- if index != nil {
- path := rr.ModulePath()
- if v, ok := index.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
- // 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(path); ok && len(pathMajor) > 0 {
- v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
- } else {
- v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
- }
+ 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
+ // 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(path); ok && len(pathMajor) > 0 {
+ v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
+ } else {
+ v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
}
+ }
- if err != nil || semver.Compare(v, info.Version) > 0 {
- return rr.replacementStat(v)
- }
+ if err != nil || semver.Compare(v, info.Version) > 0 {
+ return rr.replacementStat(v)
}
}
@@ -1108,20 +1134,46 @@ func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error)
return rev, nil
}
-// A QueryMatchesMainModuleError indicates that a query requests
+// A QueryMatchesMainModulesError indicates that a query requests
// a version of the main module that cannot be satisfied.
// (The main module's version cannot be changed.)
-type QueryMatchesMainModuleError struct {
- Pattern string
- Query string
+type QueryMatchesMainModulesError struct {
+ MainModules []module.Version
+ Pattern string
+ Query string
}
-func (e *QueryMatchesMainModuleError) Error() string {
- if e.Pattern == Target.Path {
+func (e *QueryMatchesMainModulesError) Error() string {
+ if MainModules.Contains(e.Pattern) {
return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
}
- return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path)
+ plural := ""
+ mainModulePaths := make([]string, len(e.MainModules))
+ for i := range e.MainModules {
+ mainModulePaths[i] = e.MainModules[i].Path
+ }
+ if len(e.MainModules) > 1 {
+ plural = "s"
+ }
+ return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", "))
+}
+
+// A QueryUpgradesAllError indicates that a query requests
+// an upgrade on the all pattern.
+// (The main module's version cannot be changed.)
+type QueryUpgradesAllError struct {
+ MainModules []module.Version
+ Query string
+}
+
+func (e *QueryUpgradesAllError) Error() string {
+ var plural string = ""
+ if len(e.MainModules) != 1 {
+ plural = "s"
+ }
+
+ return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
}
// A QueryMatchesPackagesInMainModuleError indicates that a query cannot be
diff --git a/src/cmd/go/internal/modload/search.go b/src/cmd/go/internal/modload/search.go
index 658fc6f55a9c9bb127e600f3d5ef51ffeb8c9773..799c48e50a8caca851b197c1ee584411bf2ad55c 100644
--- a/src/cmd/go/internal/modload/search.go
+++ b/src/cmd/go/internal/modload/search.go
@@ -131,9 +131,10 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
}
if cfg.BuildMod == "vendor" {
- if HasModRoot() {
- walkPkgs(ModRoot(), targetPrefix, pruneGoMod|pruneVendor)
- walkPkgs(filepath.Join(ModRoot(), "vendor"), "", pruneVendor)
+ mod := MainModules.mustGetSingleMainModule()
+ if modRoot := MainModules.ModRoot(mod); modRoot != "" {
+ walkPkgs(modRoot, MainModules.PathPrefix(mod), pruneGoMod|pruneVendor)
+ walkPkgs(filepath.Join(modRoot, "vendor"), "", pruneVendor)
}
return
}
@@ -147,12 +148,12 @@ func matchPackages(ctx context.Context, m *search.Match, tags map[string]bool, f
root, modPrefix string
isLocal bool
)
- if mod == Target {
- if !HasModRoot() {
+ if MainModules.Contains(mod.Path) {
+ if MainModules.ModRoot(mod) == "" {
continue // If there is no main module, we can't search in it.
}
- root = ModRoot()
- modPrefix = targetPrefix
+ root = MainModules.ModRoot(mod)
+ modPrefix = MainModules.PathPrefix(mod)
isLocal = true
} else {
var err error
diff --git a/src/cmd/go/internal/modload/stat_openfile.go b/src/cmd/go/internal/modload/stat_openfile.go
index 368f893198492984b0cc1efaffe988d5f172c5d5..ff7c124af58a28c6da3ddfe48d144f935a1afdfb 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 e079d7399026feb44091d94973cfe0eeb6da9f05..8a3653ba80b019ae0394540b2291cffe21699850 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 825e60b27af89406122aa400b92df706aba508d5..f29a99165e53001faec5e8553e670a82803522dc 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 80713b0812eacce928f0a39b26eba024e655a56b..5ea82a862083fdd76ed81f0b63ccac63abb88ef9 100644
--- a/src/cmd/go/internal/modload/vendor.go
+++ b/src/cmd/go/internal/modload/vendor.go
@@ -15,6 +15,7 @@ import (
"cmd/go/internal/base"
+ "golang.org/x/mod/modfile"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
@@ -35,13 +36,13 @@ type vendorMetadata struct {
}
// readVendorList reads the list of vendored modules from vendor/modules.txt.
-func readVendorList() {
+func readVendorList(mainModule module.Version) {
vendorOnce.Do(func() {
vendorList = nil
vendorPkgModule = make(map[string]module.Version)
vendorVersion = make(map[string]string)
vendorMeta = make(map[module.Version]vendorMetadata)
- data, err := os.ReadFile(filepath.Join(ModRoot(), "vendor/modules.txt"))
+ data, err := os.ReadFile(filepath.Join(MainModules.ModRoot(mainModule), "vendor/modules.txt"))
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
base.Fatalf("go: %s", err)
@@ -134,8 +135,8 @@ func readVendorList() {
// checkVendorConsistency verifies that the vendor/modules.txt file matches (if
// go 1.14) or at least does not contradict (go 1.13 or earlier) the
// requirements and replacements listed in the main module's go.mod file.
-func checkVendorConsistency() {
- readVendorList()
+func checkVendorConsistency(index *modFileIndex, modFile *modfile.File) {
+ readVendorList(MainModules.mustGetSingleMainModule())
pre114 := false
if semver.Compare(index.goVersionV, "v1.14") < 0 {
@@ -146,7 +147,7 @@ func checkVendorConsistency() {
}
vendErrors := new(strings.Builder)
- vendErrorf := func(mod module.Version, format string, args ...interface{}) {
+ vendErrorf := func(mod module.Version, format string, args ...any) {
detail := fmt.Sprintf(format, args...)
if mod.Version == "" {
fmt.Fprintf(vendErrors, "\n\t%s: %s", mod.Path, detail)
@@ -219,6 +220,7 @@ func checkVendorConsistency() {
}
if vendErrors.Len() > 0 {
+ modRoot := MainModules.ModRoot(MainModules.mustGetSingleMainModule())
base.Fatalf("go: inconsistent vendoring in %s:%s\n\n\tTo ignore the vendor directory, use -mod=readonly or -mod=mod.\n\tTo sync the vendor directory, run:\n\t\tgo mod vendor", modRoot, vendErrors)
}
}
diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go
index 6969f90f2e681abf23495ab7aab8ba4432453d17..d25d447b0ee397fb603280be92e8b245a402f0d0 100644
--- a/src/cmd/go/internal/mvs/mvs.go
+++ b/src/cmd/go/internal/mvs/mvs.go
@@ -8,6 +8,7 @@ package mvs
import (
"fmt"
+ "reflect"
"sort"
"sync"
@@ -85,11 +86,11 @@ type DowngradeReqs interface {
// of the list are sorted by path.
//
// See https://research.swtch.com/vgo-mvs for details.
-func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
- return buildList(target, reqs, nil)
+func BuildList(targets []module.Version, reqs Reqs) ([]module.Version, error) {
+ return buildList(targets, reqs, nil)
}
-func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) {
+func buildList(targets []module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) {
cmp := func(v1, v2 string) int {
if reqs.Max(v1, v2) != v1 {
return -1
@@ -102,7 +103,7 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
var (
mu sync.Mutex
- g = NewGraph(cmp, []module.Version{target})
+ g = NewGraph(cmp, targets)
upgrades = map[module.Version]module.Version{}
errs = map[module.Version]error{} // (non-nil errors only)
)
@@ -110,8 +111,10 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
// Explore work graph in parallel in case reqs.Required
// does high-latency network operations.
var work par.Work
- work.Add(target)
- work.Do(10, func(item interface{}) {
+ for _, target := range targets {
+ work.Add(target)
+ }
+ work.Do(10, func(item any) {
m := item.(module.Version)
var required []module.Version
@@ -168,12 +171,12 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
// The final list is the minimum version of each module found in the graph.
list := g.BuildList()
- if v := list[0]; v != target {
+ if vs := list[:len(targets)]; !reflect.DeepEqual(vs, targets) {
// target.Version will be "" for modload, the main client of MVS.
// "" denotes the main module, which has no version. However, MVS treats
// version strings as opaque, so "" is not a special value here.
// See golang.org/issue/31491, golang.org/issue/29773.
- panic(fmt.Sprintf("mistake: chose version %q instead of target %+v", v, target))
+ panic(fmt.Sprintf("mistake: chose versions %+v instead of targets %+v", vs, targets))
}
return list, nil
}
@@ -181,8 +184,8 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
// Req returns the minimal requirement list for the target module,
// with the constraint that all module paths listed in base must
// appear in the returned list.
-func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, error) {
- list, err := BuildList(target, reqs)
+func Req(mainModule module.Version, base []string, reqs Reqs) ([]module.Version, error) {
+ list, err := BuildList([]module.Version{mainModule}, reqs)
if err != nil {
return nil, err
}
@@ -194,7 +197,8 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err
// Compute postorder, cache requirements.
var postorder []module.Version
reqCache := map[module.Version][]module.Version{}
- reqCache[target] = nil
+ reqCache[mainModule] = nil
+
var walk func(module.Version) error
walk = func(m module.Version) error {
_, ok := reqCache[m]
@@ -273,7 +277,7 @@ func Req(target module.Version, base []string, reqs Reqs) ([]module.Version, err
// UpgradeAll returns a build list for the target module
// in which every module is upgraded to its latest version.
func UpgradeAll(target module.Version, reqs UpgradeReqs) ([]module.Version, error) {
- return buildList(target, reqs, func(m module.Version) (module.Version, error) {
+ return buildList([]module.Version{target}, reqs, func(m module.Version) (module.Version, error) {
if m.Path == target.Path {
return target, nil
}
@@ -308,7 +312,7 @@ func Upgrade(target module.Version, reqs UpgradeReqs, upgrade ...module.Version)
}
}
- return buildList(target, &override{target, list, reqs}, func(m module.Version) (module.Version, error) {
+ return buildList([]module.Version{target}, &override{target, list, reqs}, func(m module.Version) (module.Version, error) {
if v, ok := upgradeTo[m.Path]; ok {
return module.Version{Path: m.Path, Version: v}, nil
}
@@ -331,7 +335,7 @@ func Downgrade(target module.Version, reqs DowngradeReqs, downgrade ...module.Ve
//
// In order to generate those new requirements, we need to identify versions
// for every module in the build list — not just reqs.Required(target).
- list, err := BuildList(target, reqs)
+ list, err := BuildList([]module.Version{target}, reqs)
if err != nil {
return nil, err
}
@@ -446,7 +450,7 @@ List:
// list with the actual versions of the downgraded modules as selected by MVS,
// instead of our initial downgrades.
// (See the downhiddenartifact and downhiddencross test cases).
- actual, err := BuildList(target, &override{
+ actual, err := BuildList([]module.Version{target}, &override{
target: target,
list: downgraded,
Reqs: reqs,
@@ -466,7 +470,7 @@ List:
}
}
- return BuildList(target, &override{
+ return BuildList([]module.Version{target}, &override{
target: target,
list: downgraded,
Reqs: reqs,
diff --git a/src/cmd/go/internal/mvs/mvs_test.go b/src/cmd/go/internal/mvs/mvs_test.go
index 598ed666889517a112c01e78d5b6fcb95101fcd2..26d004fee2856ebb2a19cf27033d753b01e11c8d 100644
--- a/src/cmd/go/internal/mvs/mvs_test.go
+++ b/src/cmd/go/internal/mvs/mvs_test.go
@@ -507,7 +507,7 @@ func Test(t *testing.T) {
t.Fatalf("build takes one argument: %q", line)
}
fns = append(fns, func(t *testing.T) {
- list, err := BuildList(m(kf[1]), reqs)
+ list, err := BuildList([]module.Version{m(kf[1])}, reqs)
checkList(t, key, list, err, val)
})
continue
diff --git a/src/cmd/go/internal/par/work.go b/src/cmd/go/internal/par/work.go
index 960cec6fb16994ea9ef028b9e1b9d2b797f35a19..496c41b1509281bcea9b1ba5abfda1effe605e4f 100644
--- a/src/cmd/go/internal/par/work.go
+++ b/src/cmd/go/internal/par/work.go
@@ -14,24 +14,24 @@ import (
// Work manages a set of work items to be executed in parallel, at most once each.
// The items in the set must all be valid map keys.
type Work struct {
- f func(interface{}) // function to run for each item
- running int // total number of runners
+ f func(any) // function to run for each item
+ running int // total number of runners
mu sync.Mutex
- added map[interface{}]bool // items added to set
- todo []interface{} // items yet to be run
- wait sync.Cond // wait when todo is empty
- waiting int // number of runners waiting for todo
+ added map[any]bool // items added to set
+ todo []any // items yet to be run
+ wait sync.Cond // wait when todo is empty
+ waiting int // number of runners waiting for todo
}
func (w *Work) init() {
if w.added == nil {
- w.added = make(map[interface{}]bool)
+ w.added = make(map[any]bool)
}
}
// Add adds item to the work set, if it hasn't already been added.
-func (w *Work) Add(item interface{}) {
+func (w *Work) Add(item any) {
w.mu.Lock()
w.init()
if !w.added[item] {
@@ -51,7 +51,7 @@ func (w *Work) Add(item interface{}) {
// before calling Do (or else Do returns immediately),
// but it is allowed for f(item) to add new items to the set.
// Do should only be used once on a given Work.
-func (w *Work) Do(n int, f func(item interface{})) {
+func (w *Work) Do(n int, f func(item any)) {
if n < 1 {
panic("par.Work.Do: n < 1")
}
@@ -110,13 +110,13 @@ type Cache struct {
type cacheEntry struct {
done uint32
mu sync.Mutex
- result interface{}
+ result any
}
// Do calls the function f if and only if Do is being called for the first time with this key.
// No call to Do with a given key returns until the one call to f returns.
// Do returns the value returned by the one call to f.
-func (c *Cache) Do(key interface{}, f func() interface{}) interface{} {
+func (c *Cache) Do(key any, f func() any) any {
entryIface, ok := c.m.Load(key)
if !ok {
entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry))
@@ -136,7 +136,7 @@ func (c *Cache) Do(key interface{}, f func() interface{}) interface{} {
// Get returns the cached result associated with key.
// It returns nil if there is no such result.
// If the result for key is being computed, Get does not wait for the computation to finish.
-func (c *Cache) Get(key interface{}) interface{} {
+func (c *Cache) Get(key any) any {
entryIface, ok := c.m.Load(key)
if !ok {
return nil
@@ -156,7 +156,7 @@ func (c *Cache) Get(key interface{}) interface{} {
// TODO(jayconrod): Delete this after the package cache clearing functions
// in internal/load have been removed.
func (c *Cache) Clear() {
- c.m.Range(func(key, value interface{}) bool {
+ c.m.Range(func(key, value any) bool {
c.m.Delete(key)
return true
})
@@ -169,7 +169,7 @@ func (c *Cache) Clear() {
//
// TODO(jayconrod): Delete this after the package cache clearing functions
// in internal/load have been removed.
-func (c *Cache) Delete(key interface{}) {
+func (c *Cache) Delete(key any) {
c.m.Delete(key)
}
@@ -180,8 +180,8 @@ func (c *Cache) Delete(key interface{}) {
//
// TODO(jayconrod): Delete this after the package cache clearing functions
// in internal/load have been removed.
-func (c *Cache) DeleteIf(pred func(key interface{}) bool) {
- c.m.Range(func(key, _ interface{}) bool {
+func (c *Cache) DeleteIf(pred func(key any) bool) {
+ c.m.Range(func(key, _ any) bool {
if pred(key) {
c.Delete(key)
}
diff --git a/src/cmd/go/internal/par/work_test.go b/src/cmd/go/internal/par/work_test.go
index f104bc4106f208bc2fac5ce36955a971e5e9f064..add0e640d8c093423e16c1a3fae1cd2b208bd137 100644
--- a/src/cmd/go/internal/par/work_test.go
+++ b/src/cmd/go/internal/par/work_test.go
@@ -16,7 +16,7 @@ func TestWork(t *testing.T) {
const N = 10000
n := int32(0)
w.Add(N)
- w.Do(100, func(x interface{}) {
+ w.Do(100, func(x any) {
atomic.AddInt32(&n, 1)
i := x.(int)
if i >= 2 {
@@ -40,7 +40,7 @@ func TestWorkParallel(t *testing.T) {
}
start := time.Now()
var n int32
- w.Do(N, func(x interface{}) {
+ w.Do(N, func(x any) {
time.Sleep(1 * time.Millisecond)
atomic.AddInt32(&n, +1)
})
@@ -58,19 +58,19 @@ func TestCache(t *testing.T) {
var cache Cache
n := 1
- v := cache.Do(1, func() interface{} { n++; return n })
+ v := cache.Do(1, func() any { n++; return n })
if v != 2 {
t.Fatalf("cache.Do(1) did not run f")
}
- v = cache.Do(1, func() interface{} { n++; return n })
+ v = cache.Do(1, func() any { n++; return n })
if v != 2 {
t.Fatalf("cache.Do(1) ran f again!")
}
- v = cache.Do(2, func() interface{} { n++; return n })
+ v = cache.Do(2, func() any { n++; return n })
if v != 3 {
t.Fatalf("cache.Do(2) did not run f")
}
- v = cache.Do(1, func() interface{} { n++; return n })
+ v = cache.Do(1, func() any { n++; return n })
if v != 2 {
t.Fatalf("cache.Do(1) did not returned saved value from original cache.Do(1)")
}
diff --git a/src/cmd/go/internal/robustio/robustio_flaky.go b/src/cmd/go/internal/robustio/robustio_flaky.go
index d5c241857b476c47cfed2c9bb1c5b1f739426ee3..c56e36ca62412aae46a98f41f5a2d9ad5191fe8e 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 3a20cac6cf88aef9882a484439c2298489a80e2f..da9a46e4face362eb5d95872bccc2ecd4a7a5424 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 784f7162dfd3f5b01bbd7c60a89975a9e3e5f1aa..c4b70b64fe651b6bed5a3b8b5bcc8a6d0d33c0a1 100644
--- a/src/cmd/go/internal/run/run.go
+++ b/src/cmd/go/internal/run/run.go
@@ -65,14 +65,17 @@ func init() {
CmdRun.Run = runRun // break init loop
work.AddBuildFlags(CmdRun, work.DefaultBuildFlags)
+ base.AddWorkfileFlag(&CmdRun.Flag)
CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
}
-func printStderr(args ...interface{}) (int, error) {
+func printStderr(args ...any) (int, error) {
return fmt.Fprint(os.Stderr, args...)
}
func runRun(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
if shouldUseOutsideModuleMode(args) {
// Set global module flags for 'go run cmd@version'.
// This must be done before modload.Init, but we need to call work.BuildInit
@@ -100,7 +103,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
if strings.HasSuffix(file, "_test.go") {
// GoFilesPackage is going to assign this to TestGoFiles.
// Reject since it won't be part of the build.
- base.Fatalf("go run: cannot run *_test.go files (%s)", file)
+ base.Fatalf("go: cannot run *_test.go files (%s)", file)
}
}
p = load.GoFilesPackage(ctx, pkgOpts, files)
@@ -111,26 +114,26 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
var err error
pkgs, err = load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args[:1])
if err != nil {
- base.Fatalf("go run: %v", err)
+ base.Fatalf("go: %v", err)
}
} else {
pkgs = load.PackagesAndErrors(ctx, pkgOpts, args[:1])
}
if len(pkgs) == 0 {
- base.Fatalf("go run: no packages loaded from %s", arg)
+ base.Fatalf("go: no packages loaded from %s", arg)
}
if len(pkgs) > 1 {
var names []string
for _, p := range pkgs {
names = append(names, p.ImportPath)
}
- base.Fatalf("go run: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
+ base.Fatalf("go: pattern %s matches multiple packages:\n\t%s", arg, strings.Join(names, "\n\t"))
}
p = pkgs[0]
i++
} else {
- base.Fatalf("go run: no go files listed")
+ base.Fatalf("go: no go files listed")
}
cmdArgs := args[i:]
load.CheckPackageErrors([]*load.Package{p})
@@ -151,7 +154,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
if !cfg.BuildContext.CgoEnabled {
hint = " (cgo is disabled)"
}
- base.Fatalf("go run: no suitable source files%s", hint)
+ base.Fatalf("go: no suitable source files%s", hint)
}
p.Internal.ExeName = src[:len(src)-len(".go")]
} else {
diff --git a/src/cmd/go/internal/search/search.go b/src/cmd/go/internal/search/search.go
index a0c806a259329c27c2f45739e1250550bec04f06..ebd4990a68413b2b03476f0305fff05924db2093 100644
--- a/src/cmd/go/internal/search/search.go
+++ b/src/cmd/go/internal/search/search.go
@@ -202,12 +202,6 @@ func (m *Match) MatchPackages() {
}
}
-var modRoot string
-
-func SetModRoot(dir string) {
- modRoot = dir
-}
-
// MatchDirs sets m.Dirs to a non-nil slice containing all directories that
// potentially match a local pattern. The pattern must begin with an absolute
// path, or "./", or "../". On Windows, the pattern may use slash or backslash
@@ -215,7 +209,7 @@ func SetModRoot(dir string) {
//
// If any errors may have caused the set of directories to be incomplete,
// MatchDirs appends those errors to m.Errs.
-func (m *Match) MatchDirs() {
+func (m *Match) MatchDirs(modRoots []string) {
m.Dirs = []string{}
if !m.IsLocal() {
m.AddError(fmt.Errorf("internal error: MatchDirs: %s is not a valid filesystem pattern", m.pattern))
@@ -253,15 +247,24 @@ func (m *Match) MatchDirs() {
// We need to preserve the ./ for pattern matching
// and in the returned import paths.
- if modRoot != "" {
+ if len(modRoots) > 1 {
abs, err := filepath.Abs(dir)
if err != nil {
m.AddError(err)
return
}
- if !hasFilepathPrefix(abs, modRoot) {
- m.AddError(fmt.Errorf("directory %s is outside module root (%s)", abs, modRoot))
- return
+ var found bool
+ for _, modRoot := range modRoots {
+ if modRoot != "" && hasFilepathPrefix(abs, modRoot) {
+ found = true
+ }
+ }
+ if !found {
+ plural := ""
+ if len(modRoots) > 1 {
+ plural = "s"
+ }
+ m.AddError(fmt.Errorf("directory %s is outside module root%s (%s)", abs, plural, strings.Join(modRoots, ", ")))
}
}
@@ -424,19 +427,19 @@ func WarnUnmatched(matches []*Match) {
// ImportPaths returns the matching paths to use for the given command line.
// It calls ImportPathsQuiet and then WarnUnmatched.
-func ImportPaths(patterns []string) []*Match {
- matches := ImportPathsQuiet(patterns)
+func ImportPaths(patterns, modRoots []string) []*Match {
+ matches := ImportPathsQuiet(patterns, modRoots)
WarnUnmatched(matches)
return matches
}
// ImportPathsQuiet is like ImportPaths but does not warn about patterns with no matches.
-func ImportPathsQuiet(patterns []string) []*Match {
+func ImportPathsQuiet(patterns, modRoots []string) []*Match {
var out []*Match
for _, a := range CleanPatterns(patterns) {
m := NewMatch(a)
if m.IsLocal() {
- m.MatchDirs()
+ m.MatchDirs(modRoots)
// Change the file import path to a regular import path if the package
// is in GOPATH or GOROOT. We don't report errors here; LoadImport
diff --git a/src/cmd/go/internal/str/path.go b/src/cmd/go/internal/str/path.go
index 51ab2af82b58a6b42f5fc9eaa5e855d8de6fa5b4..0c8aaeaca1fba0f5f2cbc0d27c2262d8d43932f5 100644
--- a/src/cmd/go/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
index 9106ebf74d5e31cb04e7deddf26e1a72ff6e9c74..021bfbff779162b14a7882cbc1e7b63da9fd929b 100644
--- a/src/cmd/go/internal/str/str.go
+++ b/src/cmd/go/internal/str/str.go
@@ -14,7 +14,7 @@ import (
// StringList flattens its arguments into a single []string.
// Each argument in args must have type string or []string.
-func StringList(args ...interface{}) []string {
+func StringList(args ...any) []string {
var x []string
for _, arg := range args {
switch arg := arg.(type) {
@@ -109,47 +109,3 @@ func Uniq(ss *[]string) {
}
*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
-}
diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go
index 147ce1a63ef32d89a537f49e8fb601a70387dbe5..8ea758e0a8b64bf0c8e4314660f9b79050fb4383 100644
--- a/src/cmd/go/internal/str/str_test.go
+++ b/src/cmd/go/internal/str/str_test.go
@@ -4,7 +4,9 @@
package str
-import "testing"
+import (
+ "testing"
+)
var foldDupTests = []struct {
list []string
diff --git a/src/cmd/go/internal/test/flagdefs.go b/src/cmd/go/internal/test/flagdefs.go
index 37ac81c26782ae226be515bc2f65a42700de978d..1b79314eff365889fa9b4b1cb9b221e91abfbd49 100644
--- a/src/cmd/go/internal/test/flagdefs.go
+++ b/src/cmd/go/internal/test/flagdefs.go
@@ -19,6 +19,9 @@ var passFlagToTest = map[string]bool{
"cpu": true,
"cpuprofile": true,
"failfast": true,
+ "fuzz": true,
+ "fuzzminimizetime": true,
+ "fuzztime": true,
"list": true,
"memprofile": true,
"memprofilerate": true,
@@ -33,3 +36,37 @@ var passFlagToTest = map[string]bool{
"trace": true,
"v": true,
}
+
+var passAnalyzersToVet = map[string]bool{
+ "asmdecl": true,
+ "assign": true,
+ "atomic": true,
+ "bool": true,
+ "bools": true,
+ "buildtag": true,
+ "buildtags": true,
+ "cgocall": true,
+ "composites": true,
+ "copylocks": true,
+ "errorsas": true,
+ "framepointer": true,
+ "httpresponse": true,
+ "ifaceassert": true,
+ "loopclosure": true,
+ "lostcancel": true,
+ "methods": true,
+ "nilfunc": true,
+ "printf": true,
+ "rangeloops": true,
+ "shift": true,
+ "sigchanyzer": true,
+ "stdmethods": true,
+ "stringintconv": true,
+ "structtag": true,
+ "testinggoroutine": true,
+ "tests": true,
+ "unmarshal": true,
+ "unreachable": true,
+ "unsafeptr": true,
+ "unusedresult": true,
+}
diff --git a/src/cmd/go/internal/test/flagdefs_test.go b/src/cmd/go/internal/test/flagdefs_test.go
index ab5440b3801f15af1124d3ce1738035a894926a0..40dc558e9080c01bf1ae8ea0208d07775b2f30dd 100644
--- a/src/cmd/go/internal/test/flagdefs_test.go
+++ b/src/cmd/go/internal/test/flagdefs_test.go
@@ -5,7 +5,9 @@
package test
import (
+ "cmd/go/internal/test/internal/genflags"
"flag"
+ "reflect"
"strings"
"testing"
)
@@ -17,7 +19,7 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) {
}
name := strings.TrimPrefix(f.Name, "test.")
switch name {
- case "testlogfile", "paniconexit0":
+ case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker":
// These are internal flags.
default:
if !passFlagToTest[name] {
@@ -37,3 +39,20 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) {
}
}
}
+
+func TestVetAnalyzersSetIsCorrect(t *testing.T) {
+ vetAns, err := genflags.VetAnalyzers()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := make(map[string]bool)
+ for _, a := range vetAns {
+ want[a] = true
+ }
+
+ if !reflect.DeepEqual(want, passAnalyzersToVet) {
+ t.Errorf("stale vet analyzers: want %v; got %v", want, passAnalyzersToVet)
+ t.Logf("(Run 'go generate cmd/go/internal/test' to refresh the set of analyzers.)")
+ }
+}
diff --git a/src/cmd/go/internal/test/genflags.go b/src/cmd/go/internal/test/genflags.go
index 9277de7fee839e216f8c70b31b8720d839f16c5e..10f290090c7cf8e92420c8f61dae4ca559d926d0 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
@@ -16,6 +15,8 @@ import (
"strings"
"testing"
"text/template"
+
+ "cmd/go/internal/test/internal/genflags"
)
func main() {
@@ -25,9 +26,18 @@ func main() {
}
func regenerate() error {
+ vetAnalyzers, err := genflags.VetAnalyzers()
+ if err != nil {
+ return err
+ }
+
t := template.Must(template.New("fileTemplate").Parse(fileTemplate))
+ tData := map[string][]string{
+ "testFlags": testFlags(),
+ "vetAnalyzers": vetAnalyzers,
+ }
buf := bytes.NewBuffer(nil)
- if err := t.Execute(buf, testFlags()); err != nil {
+ if err := t.Execute(buf, tData); err != nil {
return err
}
@@ -64,7 +74,7 @@ func testFlags() []string {
name := strings.TrimPrefix(f.Name, "test.")
switch name {
- case "testlogfile", "paniconexit0":
+ case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker":
// These flags are only for use by cmd/go.
default:
names = append(names, name)
@@ -85,7 +95,13 @@ package test
// passFlagToTest contains the flags that should be forwarded to
// the test binary with the prefix "test.".
var passFlagToTest = map[string]bool {
-{{- range .}}
+{{- range .testFlags}}
+ "{{.}}": true,
+{{- end }}
+}
+
+var passAnalyzersToVet = map[string]bool {
+{{- range .vetAnalyzers}}
"{{.}}": true,
{{- end }}
}
diff --git a/src/cmd/go/internal/test/internal/genflags/vetflag.go b/src/cmd/go/internal/test/internal/genflags/vetflag.go
new file mode 100644
index 0000000000000000000000000000000000000000..2195cc34479f31bc611860fa9cdfecbbcc5744f3
--- /dev/null
+++ b/src/cmd/go/internal/test/internal/genflags/vetflag.go
@@ -0,0 +1,68 @@
+// 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 genflags
+
+import (
+ "bytes"
+ "cmd/go/internal/base"
+ "encoding/json"
+ "fmt"
+ exec "internal/execabs"
+ "regexp"
+ "sort"
+)
+
+// VetAnalyzers computes analyzers and their aliases supported by vet.
+func VetAnalyzers() ([]string, error) {
+ // get supported vet flag information
+ tool := base.Tool("vet")
+ vetcmd := exec.Command(tool, "-flags")
+ out := new(bytes.Buffer)
+ vetcmd.Stdout = out
+ if err := vetcmd.Run(); err != nil {
+ return nil, fmt.Errorf("go vet: can't execute %s -flags: %v\n", tool, err)
+ }
+ var analysisFlags []struct {
+ Name string
+ Bool bool
+ Usage string
+ }
+ if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
+ return nil, fmt.Errorf("go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
+ }
+
+ // parse the flags to figure out which ones stand for analyses
+ analyzerSet := make(map[string]bool)
+ rEnable := regexp.MustCompile("^enable .+ analysis$")
+ for _, flag := range analysisFlags {
+ if rEnable.MatchString(flag.Usage) {
+ analyzerSet[flag.Name] = true
+ }
+ }
+
+ rDeprecated := regexp.MustCompile("^deprecated alias for -(?P(.+))$")
+ // Returns the original value matched by rDeprecated on input value.
+ // If there is no match, "" is returned.
+ originalValue := func(value string) string {
+ match := rDeprecated.FindStringSubmatch(value)
+ if len(match) < 2 {
+ return ""
+ }
+ return match[1]
+ }
+ // extract deprecated aliases for existing analyses
+ for _, flag := range analysisFlags {
+ if o := originalValue(flag.Usage); analyzerSet[o] {
+ analyzerSet[flag.Name] = true
+ }
+ }
+
+ var analyzers []string
+ for a := range analyzerSet {
+ analyzers = append(analyzers, a)
+ }
+ sort.Strings(analyzers)
+ return analyzers, nil
+}
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index 59ea1ef5445178f052006dd37af2d8b0b209b9ef..50e6d5201b0d6e04cc59a3289d30fa8b4e1cb2b0 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -29,11 +29,15 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"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/sys"
"cmd/internal/test2json"
+
+ "golang.org/x/mod/module"
)
// Break init loop.
@@ -60,8 +64,8 @@ followed by detailed output for each failed package.
'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go".
-These additional files can contain test functions, benchmark functions, and
-example functions. See 'go help testfunc' for more.
+These additional files can contain test functions, benchmark functions, fuzz
+tests and example functions. See 'go help testfunc' for more.
Each listed package causes the execution of a separate test binary.
Files whose names begin with "_" (including "_test.go") or "." are ignored.
@@ -78,7 +82,8 @@ binary. Only a high-confidence subset of the default go vet checks are
used. That subset is: 'atomic', 'bool', 'buildtags', 'errorsas',
'ifaceassert', 'nilfunc', 'printf', and 'stringintconv'. You can see
the documentation for these and other vet tests via "go doc cmd/vet".
-To disable the running of go vet, use the -vet=off flag.
+To disable the running of go vet, use the -vet=off flag. To run all
+checks, use the -vet=all flag.
All test output and summary lines are printed to the go command's
standard output, even if the test printed them to its own standard
@@ -119,16 +124,16 @@ elapsed time in the summary line.
The rule for a match in the cache is that the run involves the same
test binary and the flags on the command line come entirely from a
restricted set of 'cacheable' test flags, defined as -benchtime, -cpu,
--list, -parallel, -run, -short, and -v. If a run of go test has any test
-or non-test flags outside this set, the result is not cached. To
-disable test caching, use any test flag or argument other than the
-cacheable flags. The idiomatic way to disable test caching explicitly
-is to use -count=1. Tests that open files within the package's source
-root (usually $GOPATH) or that consult environment variables only
-match future runs in which the files and environment variables are unchanged.
-A cached test result is treated as executing in no time at all,
-so a successful package test result will be cached and reused
-regardless of -timeout setting.
+-list, -parallel, -run, -short, -timeout, -failfast, and -v.
+If a run of go test has any test or non-test flags outside this set,
+the result is not cached. To disable test caching, use any test flag
+or argument other than the cacheable flags. The idiomatic way to disable
+test caching explicitly is to use -count=1. Tests that open files within
+the package's source root (usually $GOPATH) or that consult environment
+variables only match future runs in which the files and environment
+variables are unchanged. A cached test result is treated as executing
+in no time at all,so a successful package test result will be cached and
+reused regardless of -timeout setting.
In addition to the build flags, the flags handled by 'go test' itself are:
@@ -206,9 +211,10 @@ control the execution of any test:
(for example, -benchtime 100x).
-count n
- Run each test and benchmark n times (default 1).
+ Run each test, benchmark, and fuzz seed n times (default 1).
If -cpu is set, run n times for each GOMAXPROCS value.
- Examples are always run once.
+ Examples are always run once. -count does not apply to
+ fuzz tests matched by -fuzz.
-cover
Enable coverage analysis.
@@ -235,32 +241,67 @@ control the execution of any test:
Sets -cover.
-cpu 1,2,4
- Specify a list of GOMAXPROCS values for which the tests or
- benchmarks should be executed. The default is the current value
- of GOMAXPROCS.
+ Specify a list of GOMAXPROCS values for which the tests, benchmarks or
+ fuzz tests should be executed. The default is the current value
+ of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
-failfast
Do not start new tests after the first test failure.
+ -fuzz regexp
+ Run the fuzz test matching the regular expression. When specified,
+ the command line argument must match exactly one package within the
+ main module, and regexp must match exactly one fuzz test within
+ that package. Fuzzing will occur after tests, benchmarks, seed corpora
+ of other fuzz tests, and examples have completed. See the Fuzzing
+ section of the testing package documentation for details.
+
+ -fuzztime t
+ Run enough iterations of the fuzz target during fuzzing to take t,
+ specified as a time.Duration (for example, -fuzztime 1h30s).
+ The default is to run forever.
+ The special syntax Nx means to run the fuzz target N times
+ (for example, -fuzztime 1000x).
+
+ -fuzzminimizetime t
+ Run enough iterations of the fuzz target during each minimization
+ attempt to take t, as specified as a time.Duration (for example,
+ -fuzzminimizetime 30s).
+ The default is 60s.
+ The special syntax Nx means to run the fuzz target N times
+ (for example, -fuzzminimizetime 100x).
+
+ -json
+ Log verbose output and test results in JSON. This presents the
+ same information as the -v flag in a machine-readable format.
+
-list regexp
- List tests, benchmarks, or examples matching the regular expression.
- No tests, benchmarks or examples will be run. This will only
- list top-level tests. No subtest or subbenchmarks will be shown.
+ List tests, benchmarks, fuzz tests, or examples matching the regular
+ expression. No tests, benchmarks, fuzz tests, or examples will be run.
+ This will only list top-level tests. No subtest or subbenchmarks will be
+ shown.
-parallel n
- Allow parallel execution of test functions that call t.Parallel.
+ Allow parallel execution of test functions that call t.Parallel, and
+ fuzz targets that call t.Parallel when running the seed corpus.
The value of this flag is the maximum number of tests to run
- simultaneously; by default, it is set to the value of GOMAXPROCS.
+ simultaneously.
+ While fuzzing, the value of this flag is the maximum number of
+ subprocesses that may call the fuzz function simultaneously, regardless of
+ whether T.Parallel is called.
+ By default, -parallel is set to the value of GOMAXPROCS.
+ Setting -parallel to values higher than GOMAXPROCS may cause degraded
+ performance due to CPU contention, especially when fuzzing.
Note that -parallel only applies within a single test binary.
The 'go test' command may run tests for different packages
in parallel as well, according to the setting of the -p flag
(see 'go help build').
-run regexp
- Run only those tests and examples matching the regular expression.
- For tests, the regular expression is split by unbracketed slash (/)
- characters into a sequence of regular expressions, and each part
- of a test's identifier must match the corresponding element in
+ Run only those tests, examples, and fuzz tests matching the regular
+ expression. For tests, the regular expression is split by unbracketed
+ slash (/) characters into a sequence of regular expressions, and each
+ part of a test's identifier must match the corresponding element in
the sequence, if any. Note that possible parents of matches are
run too, so that -run=X/Y matches and runs and reports the result
of all tests matching X, even those without sub-tests matching Y,
@@ -273,11 +314,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.
@@ -373,7 +414,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,
@@ -430,6 +475,10 @@ A benchmark function is one named BenchmarkXxx and should have the signature,
func BenchmarkXxx(b *testing.B) { ... }
+A fuzz test is one named FuzzXxx and should have the signature,
+
+ func FuzzXxx(f *testing.F) { ... }
+
An example function is similar to a test function but, instead of using
*testing.T to report success or failure, prints output to os.Stdout.
If the last comment in the function starts with "Output:" then the output
@@ -469,7 +518,7 @@ Here is another example where the ordering of the output is ignored:
The entire test file is presented as the example when it contains a single
example function, at least one other function, type, variable, or constant
-declaration, and no test or benchmark functions.
+declaration, and no tests, benchmarks, or fuzz tests.
See the documentation of the testing package for more information.
`,
@@ -483,6 +532,7 @@ var (
testCoverPaths []string // -coverpkg flag
testCoverPkgs []*load.Package // -coverpkg flag
testCoverProfile string // -coverprofile flag
+ testFuzz string // -fuzz flag
testJSON bool // -json flag
testList string // -list flag
testO string // -o flag
@@ -578,6 +628,7 @@ var defaultVetFlags = []string{
func runTest(ctx context.Context, cmd *base.Command, args []string) {
pkgArgs, testArgs = testFlags(args)
+ modload.InitWorkfile() // The test command does custom flag processing; initialize workspaces after that.
if cfg.DebugTrace != "" {
var close func() error
@@ -615,6 +666,52 @@ 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 != "" {
+ 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())
}
@@ -625,7 +722,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
// to that timeout plus one minute. This is a backup alarm in case
// the test wedges with a goroutine spinning and its background
// timer does not get a chance to fire.
- if testTimeout > 0 {
+ // Don't set this if fuzzing, since it should be able to run
+ // indefinitely.
+ if testTimeout > 0 && testFuzz == "" {
testKillTimeout = testTimeout + 1*time.Minute
}
@@ -649,7 +748,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
b.Init()
if cfg.BuildI {
- fmt.Fprint(os.Stderr, "go test: -i flag is deprecated\n")
+ fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
cfg.BuildV = testV
deps := make(map[string]bool)
@@ -775,6 +874,41 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
}
}
+ // Inform the compiler that it should instrument the binary at
+ // build-time when fuzzing is enabled.
+ if testFuzz != "" {
+ // Don't instrument packages which may affect coverage guidance but are
+ // unlikely to be useful. Most of these are used by the testing or
+ // internal/fuzz packages concurrently with fuzzing.
+ var skipInstrumentation = map[string]bool{
+ "context": true,
+ "internal/fuzz": true,
+ "reflect": true,
+ "runtime": true,
+ "sync": true,
+ "sync/atomic": true,
+ "syscall": true,
+ "testing": true,
+ "time": true,
+ }
+ for _, p := range load.TestPackageList(ctx, pkgOpts, pkgs) {
+ if !skipInstrumentation[p.ImportPath] {
+ p.Internal.FuzzInstrument = true
+ }
+ }
+ }
+
+ // Collect all the packages imported by the packages being tested.
+ allImports := make(map[*load.Package]bool)
+ for _, p := range pkgs {
+ if p.Error != nil && p.Error.IsImportCycle {
+ continue
+ }
+ for _, p1 := range p.Internal.Imports {
+ allImports[p1] = true
+ }
+ }
+
// Prepare build + run + print actions for all packages being tested.
for _, p := range pkgs {
// sync/atomic import is inserted by the cover tool. See #18486
@@ -782,7 +916,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
ensureImport(p, "sync/atomic")
}
- buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p)
+ buildTest, runTest, printTest, err := builderTest(&b, ctx, pkgOpts, p, allImports[p])
if err != nil {
str := err.Error()
str = strings.TrimPrefix(str, "\n")
@@ -849,7 +983,7 @@ var windowsBadWords = []string{
"update",
}
-func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package) (buildAction, runAction, printAction *work.Action, err error) {
+func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool) (buildAction, runAction, printAction *work.Action, err error) {
if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
build := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
run := &work.Action{Mode: "test run", Package: p, Deps: []*work.Action{build}}
@@ -877,6 +1011,16 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
return nil, nil, nil, err
}
+ // If imported is true then this package is imported by some
+ // package being tested. Make building the test version of the
+ // package depend on building the non-test version, so that we
+ // only report build errors once. Issue #44624.
+ if imported && ptest != p {
+ buildTest := b.CompileAction(work.ModeBuild, work.ModeBuild, ptest)
+ buildP := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
+ buildTest.Deps = append(buildTest.Deps, buildP)
+ }
+
// Use last element of import path, not package name.
// They differ when package name is "main".
// But if the import path is "command-line-arguments",
@@ -1080,6 +1224,8 @@ func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVa
}
var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
+var noFuzzTestsToFuzz = []byte("\ntesting: warning: no fuzz tests to fuzz\n")
+var tooManyFuzzTestsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one fuzz test, won't fuzz\n")
type runCache struct {
disableCache bool // cache should be disabled for this run
@@ -1127,10 +1273,10 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
}
var buf bytes.Buffer
- if len(pkgArgs) == 0 || (testBench != "") {
+ if len(pkgArgs) == 0 || testBench != "" || testFuzz != "" {
// Stream test output (no buffering) when no package has
// been given on the command line (implicit current directory)
- // or when benchmarking.
+ // or when benchmarking or fuzzing.
// No change to stdout.
} else {
// If we're only running a single package under test or if parallelism is
@@ -1183,7 +1329,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
}
panicArg := "-test.paniconexit0"
- args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, testArgs)
+ fuzzArg := []string{}
+ if testFuzz != "" {
+ fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath)
+ fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir}
+ }
+ args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, fuzzArg, testArgs)
if testCoverProfile != "" {
// Write coverage to temporary profile, for merging later.
@@ -1276,15 +1427,31 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
norun = " [no tests to run]"
}
+ if bytes.HasPrefix(out, noFuzzTestsToFuzz[1:]) || bytes.Contains(out, noFuzzTestsToFuzz) {
+ norun = " [no fuzz tests to fuzz]"
+ }
+ if bytes.HasPrefix(out, tooManyFuzzTestsToFuzz[1:]) || bytes.Contains(out, tooManyFuzzTestsToFuzz) {
+ norun = "[-fuzz matches more than one fuzz test, won't fuzz]"
+ }
+ 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,
@@ -1347,6 +1514,7 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo
"-test.run",
"-test.short",
"-test.timeout",
+ "-test.failfast",
"-test.v":
// These are cacheable.
// Note that this list is documented above,
@@ -1711,9 +1879,23 @@ func builderNoTest(b *work.Builder, ctx context.Context, a *work.Action) error {
return nil
}
-// printExitStatus is the action for printing the exit status
+// printExitStatus is the action for printing the final exit status.
+// If we are running multiple test targets, print a final "FAIL"
+// in case a failure in an early package has already scrolled
+// off of the user's terminal.
+// (See https://golang.org/issue/30507#issuecomment-470593235.)
+//
+// In JSON mode, we need to maintain valid JSON output and
+// we assume that the test output is being parsed by a tool
+// anyway, so the failure will not be missed and would be
+// awkward to try to wedge into the JSON stream.
+//
+// In fuzz mode, we only allow a single package for now
+// (see CL 350156 and https://golang.org/issue/46312),
+// so there is no possibility of scrolling off and no need
+// to print the final status.
func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error {
- if !testJSON && len(pkgArgs) != 0 {
+ if !testJSON && testFuzz == "" && len(pkgArgs) != 0 {
if base.GetExitStatus() != 0 {
fmt.Println("FAIL")
return nil
diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go
index 08f1efa2c0d26a0cf398f2b778c0adde90fc97ae..b9d1ec91ff7552aba0e1667d96f16d55349931a5 100644
--- a/src/cmd/go/internal/test/testflag.go
+++ b/src/cmd/go/internal/test/testflag.go
@@ -5,6 +5,10 @@
package test
import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/cmdflag"
+ "cmd/go/internal/work"
"errors"
"flag"
"fmt"
@@ -13,11 +17,6 @@ import (
"strconv"
"strings"
"time"
-
- "cmd/go/internal/base"
- "cmd/go/internal/cfg"
- "cmd/go/internal/cmdflag"
- "cmd/go/internal/work"
)
//go:generate go run ./genflags.go
@@ -29,6 +28,7 @@ import (
func init() {
work.AddBuildFlags(CmdTest, work.OmitVFlag)
+ base.AddWorkfileFlag(&CmdTest.Flag)
cf := CmdTest.Flag
cf.BoolVar(&testC, "c", false, "")
@@ -57,6 +57,7 @@ func init() {
cf.String("cpu", "", "")
cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
cf.Bool("failfast", false, "")
+ cf.StringVar(&testFuzz, "fuzz", "", "")
cf.StringVar(&testList, "list", "", "")
cf.StringVar(&testMemProfile, "memprofile", "", "")
cf.String("memprofilerate", "", "")
@@ -67,6 +68,8 @@ func init() {
cf.String("run", "", "")
cf.Bool("short", false, "")
cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
+ cf.String("fuzztime", "", "")
+ cf.String("fuzzminimizetime", "", "")
cf.StringVar(&testTrace, "trace", "", "")
cf.BoolVar(&testV, "v", false, "")
cf.Var(&testShuffle, "shuffle", "")
@@ -134,6 +137,7 @@ type outputdirFlag struct {
func (f *outputdirFlag) String() string {
return f.abs
}
+
func (f *outputdirFlag) Set(value string) (err error) {
if value == "" {
f.abs = ""
@@ -142,6 +146,7 @@ func (f *outputdirFlag) Set(value string) (err error) {
}
return err
}
+
func (f *outputdirFlag) getAbs() string {
if f.abs == "" {
return base.Cwd()
@@ -150,8 +155,12 @@ func (f *outputdirFlag) getAbs() string {
}
// vetFlag implements the special parsing logic for the -vet flag:
-// a comma-separated list, with a distinguished value "off" and
-// a boolean tracking whether it was set explicitly.
+// a comma-separated list, with distinguished values "all" and
+// "off", plus a boolean tracking whether it was set explicitly.
+//
+// "all" is encoded as vetFlag{true, false, nil}, since it will
+// pass no flags to the vet binary, and by default, it runs all
+// analyzers.
type vetFlag struct {
explicit bool
off bool
@@ -159,7 +168,10 @@ type vetFlag struct {
}
func (f *vetFlag) String() string {
- if f.off {
+ switch {
+ case !f.off && !f.explicit && len(f.flags) == 0:
+ return "all"
+ case f.off:
return "off"
}
@@ -174,31 +186,45 @@ func (f *vetFlag) String() string {
}
func (f *vetFlag) Set(value string) error {
- if value == "" {
+ switch {
+ case value == "":
*f = vetFlag{flags: defaultVetFlags}
return nil
- }
-
- if value == "off" {
- *f = vetFlag{
- explicit: true,
- off: true,
- }
- return nil
- }
-
- if strings.Contains(value, "=") {
+ case strings.Contains(value, "="):
return fmt.Errorf("-vet argument cannot contain equal signs")
- }
- if strings.Contains(value, " ") {
+ case strings.Contains(value, " "):
return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
}
+
*f = vetFlag{explicit: true}
+ var single string
for _, arg := range strings.Split(value, ",") {
- if arg == "" {
+ switch arg {
+ case "":
return fmt.Errorf("-vet argument contains empty list element")
+ case "all":
+ single = arg
+ *f = vetFlag{explicit: true}
+ continue
+ case "off":
+ single = arg
+ *f = vetFlag{
+ explicit: true,
+ off: true,
+ }
+ continue
+ default:
+ if _, ok := passAnalyzersToVet[arg]; !ok {
+ return fmt.Errorf("-vet argument must be a supported analyzer or a distinguished value; found %s", arg)
+ }
+ f.flags = append(f.flags, "-"+arg)
}
- f.flags = append(f.flags, "-"+arg)
+ }
+ if len(f.flags) > 1 && single != "" {
+ return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
+ }
+ if len(f.flags) > 1 && single != "" {
+ return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
}
return nil
}
@@ -369,7 +395,7 @@ func testFlags(args []string) (packageNames, passToTest []string) {
if !testC {
buildFlag = "-i"
}
- fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
+ fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
exitWithUsage()
}
diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go
index 95c90ea7c8da4a3f5121af3d78db4b80d8b18f65..4fe4c2baeda1ff7dbe1a1360f9a1d6d8dd9cb5f4 100644
--- a/src/cmd/go/internal/tool/tool.go
+++ b/src/cmd/go/internal/tool/tool.go
@@ -61,7 +61,7 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) {
switch {
case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
default:
- fmt.Fprintf(os.Stderr, "go tool: bad tool name %q\n", toolName)
+ fmt.Fprintf(os.Stderr, "go: bad tool name %q\n", toolName)
base.SetExitStatus(2)
return
}
@@ -117,14 +117,14 @@ func runTool(ctx context.Context, cmd *base.Command, args []string) {
func listTools() {
f, err := os.Open(base.ToolDir)
if err != nil {
- fmt.Fprintf(os.Stderr, "go tool: no tool directory: %s\n", err)
+ fmt.Fprintf(os.Stderr, "go: no tool directory: %s\n", err)
base.SetExitStatus(2)
return
}
defer f.Close()
names, err := f.Readdirnames(-1)
if err != nil {
- fmt.Fprintf(os.Stderr, "go tool: can't read directory: %s\n", err)
+ fmt.Fprintf(os.Stderr, "go: can't read tool directory: %s\n", err)
base.SetExitStatus(2)
return
}
diff --git a/src/cmd/go/internal/txtar/archive_test.go b/src/cmd/go/internal/txtar/archive_test.go
deleted file mode 100644
index 3f734f6762531ec9e4444daa742572b45e1a0721..0000000000000000000000000000000000000000
--- a/src/cmd/go/internal/txtar/archive_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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.
-
-package txtar
-
-import (
- "bytes"
- "fmt"
- "reflect"
- "testing"
-)
-
-var tests = []struct {
- name string
- text string
- parsed *Archive
-}{
- {
- name: "basic",
- text: `comment1
-comment2
--- file1 --
-File 1 text.
--- foo ---
-More file 1 text.
--- file 2 --
-File 2 text.
--- empty --
--- noNL --
-hello world`,
- parsed: &Archive{
- Comment: []byte("comment1\ncomment2\n"),
- Files: []File{
- {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")},
- {"file 2", []byte("File 2 text.\n")},
- {"empty", []byte{}},
- {"noNL", []byte("hello world\n")},
- },
- },
- },
-}
-
-func Test(t *testing.T) {
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- a := Parse([]byte(tt.text))
- if !reflect.DeepEqual(a, tt.parsed) {
- t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed))
- }
- text := Format(a)
- a = Parse(text)
- if !reflect.DeepEqual(a, tt.parsed) {
- t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed))
- }
- })
- }
-}
-
-func shortArchive(a *Archive) string {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "comment: %q\n", a.Comment)
- for _, f := range a.Files {
- fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data)
- }
- return buf.String()
-}
diff --git a/src/cmd/go/internal/vcs/vcs.go b/src/cmd/go/internal/vcs/vcs.go
index 91485f6f745b1ffcc64fcbd95f3449cbcb2b7cb6..313dc62b780e6c9f39981ba416bd0d7077ebfa01 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,8 +18,10 @@ import (
"os"
"path/filepath"
"regexp"
+ "strconv"
"strings"
"sync"
+ "time"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
@@ -29,11 +32,12 @@ import (
"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
+}
+
+// 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")
+ }
- if vcsRet != nil {
- if err := checkGOVCS(vcsRet, rootRet); err != nil {
- return nil, "", err
+ 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 vcsRet, rootRet, nil
}
- return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
+ 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
}
@@ -1063,7 +1311,7 @@ func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.Secu
return res, nil
}
- resi, _, _ := fetchGroup.Do(importPrefix, func() (resi interface{}, err error) {
+ resi, _, _ := fetchGroup.Do(importPrefix, func() (resi any, err error) {
fetchCacheMu.Lock()
if res, ok := fetchCache[importPrefix]; ok {
fetchCacheMu.Unlock()
@@ -1340,7 +1588,7 @@ type importError struct {
err error
}
-func importErrorf(path, format string, args ...interface{}) error {
+func importErrorf(path, format string, args ...any) error {
err := &importError{importPath: path, err: fmt.Errorf(format, args...)}
if errStr := err.Error(); !strings.Contains(errStr, path) {
panic(fmt.Sprintf("path %q not in error %q", path, errStr))
diff --git a/src/cmd/go/internal/vcs/vcs_test.go b/src/cmd/go/internal/vcs/vcs_test.go
index c5c7a3283bce57d1fbc8ac0c09549fe80ace5290..c4e4f4d3c6a6c43106e3731811baf027db6e9361 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)
+
+ wantRepoDir := filepath.Dir(dir)
+ gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false)
if err != nil {
- t.Fatal(err)
+ t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
+ continue
}
- f, err := os.Create(dir)
- if err != nil {
- t.Fatal(err)
+ 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)
}
- 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)
}
}
}
diff --git a/src/cmd/go/internal/version/exe.go b/src/cmd/go/internal/version/exe.go
deleted file mode 100644
index 0e7deef1491a9c4d022ff0ff6b86341d4a7b0cb4..0000000000000000000000000000000000000000
--- 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 58cbd32e78d1158700c64c2d207f40e4456802c1..52502e95c6d673eed8ec1b1a203ba9b9b55bd268 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"
@@ -62,8 +63,14 @@ func runVersion(ctx context.Context, cmd *base.Command, args []string) {
// a reasonable use case. For example, imagine GOFLAGS=-v to
// turn "verbose mode" on for all Go commands, which should not
// break "go version".
- if (!base.InGOFLAGS("-m") && *versionM) || (!base.InGOFLAGS("-v") && *versionV) {
- fmt.Fprintf(os.Stderr, "go version: flags can only be used with arguments\n")
+ var argOnlyFlag string
+ if !base.InGOFLAGS("-m") && *versionM {
+ argOnlyFlag = "-m"
+ } else if !base.InGOFLAGS("-v") && *versionV {
+ argOnlyFlag = "-v"
+ }
+ if argOnlyFlag != "" {
+ fmt.Fprintf(os.Stderr, "go: 'go version' only accepts %s flag with arguments\n", argOnlyFlag)
base.SetExitStatus(2)
return
}
@@ -135,90 +142,26 @@ 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)
- }
- return
- }
- defer x.Close()
-
- vers, mod := findVers(x)
- if vers == "" {
- if mustPrint {
- fmt.Fprintf(os.Stderr, "%s: go version not found\n", file)
+ 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
}
- 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
- }
- }
-
- // 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 ""
+ if *versionM && len(mod) > 0 {
+ fmt.Printf("\t%s\n", bytes.ReplaceAll(mod[:len(mod)-1], []byte("\n"), []byte("\n\t")))
}
- return string(data)
}
diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go
index 1d419dddb98d6cdcc53ef5e3e6eb67b477531988..88b3c570a03b5c52328eaf745dc795d86174c830 100644
--- a/src/cmd/go/internal/vet/vet.go
+++ b/src/cmd/go/internal/vet/vet.go
@@ -103,7 +103,7 @@ func runVet(ctx context.Context, cmd *base.Command, args []string) {
continue
}
if len(ptest.GoFiles) == 0 && len(ptest.CgoFiles) == 0 && pxtest == nil {
- base.Errorf("go vet %s: no Go files in %s", p.ImportPath, p.Dir)
+ base.Errorf("go: can't vet %s: no Go files in %s", p.ImportPath, p.Dir)
continue
}
if len(ptest.GoFiles) > 0 || len(ptest.CgoFiles) > 0 {
diff --git a/src/cmd/go/internal/vet/vetflag.go b/src/cmd/go/internal/vet/vetflag.go
index b5b3c462ff2acced306e495a173b43be76fd579b..3551a5997c5f887d5c3f9f8dd00ac462a902f46b 100644
--- a/src/cmd/go/internal/vet/vetflag.go
+++ b/src/cmd/go/internal/vet/vetflag.go
@@ -82,7 +82,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) {
vetcmd := exec.Command(tool, "-flags")
vetcmd.Stdout = out
if err := vetcmd.Run(); err != nil {
- fmt.Fprintf(os.Stderr, "go vet: can't execute %s -flags: %v\n", tool, err)
+ fmt.Fprintf(os.Stderr, "go: can't execute %s -flags: %v\n", tool, err)
base.SetExitStatus(2)
base.Exit()
}
@@ -92,7 +92,7 @@ func vetFlags(args []string) (passToVet, packageNames []string) {
Usage string
}
if err := json.Unmarshal(out.Bytes(), &analysisFlags); err != nil {
- fmt.Fprintf(os.Stderr, "go vet: can't unmarshal JSON from %s -flags: %v", tool, err)
+ fmt.Fprintf(os.Stderr, "go: can't unmarshal JSON from %s -flags: %v", tool, err)
base.SetExitStatus(2)
base.Exit()
}
diff --git a/src/cmd/go/internal/web/bootstrap.go b/src/cmd/go/internal/web/bootstrap.go
index 08686cdfcf9f4c11e96587a99738a09aa62b4324..ab88e9e4781f4666c9102104d4e1eccf7948c90b 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 f177278eba1e7d9088014ee0ae246e2efb0dca39..a92326db01e629093235ad88d809329bcadb1ab9 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
@@ -17,6 +16,7 @@ import (
"errors"
"fmt"
"mime"
+ "net"
"net/http"
urlpkg "net/url"
"os"
@@ -84,8 +84,15 @@ func get(security SecurityMode, url *urlpkg.URL) (*Response, error) {
if url.Host == "localhost.localdev" {
return nil, fmt.Errorf("no such host localhost.localdev")
}
- if os.Getenv("TESTGONETWORK") == "panic" && !strings.HasPrefix(url.Host, "127.0.0.1") && !strings.HasPrefix(url.Host, "0.0.0.0") {
- panic("use of network: " + url.String())
+ if os.Getenv("TESTGONETWORK") == "panic" {
+ host := url.Host
+ if h, _, err := net.SplitHostPort(url.Host); err == nil && h != "" {
+ host = h
+ }
+ addr := net.ParseIP(host)
+ if addr == nil || (!addr.IsLoopback() && !addr.IsUnspecified()) {
+ panic("use of network: " + url.String())
+ }
}
fetch := func(url *urlpkg.URL) (*urlpkg.URL, *http.Response, error) {
diff --git a/src/cmd/go/internal/web/url_other.go b/src/cmd/go/internal/web/url_other.go
index 453af402b43dd9eb9208af234701127cfa887f25..84bbd72820fcab516624f09755265dcd97f0f81d 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 4d6ed2ec7f8c1dd0d9ab90f83f4478057c36b46d..5c197de800dc1f31e73ca132da21218f7c8df148 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/action.go b/src/cmd/go/internal/work/action.go
index 69940cb00159b64c60024a9f248e5a0db337b266..c0862c5efe503af8cf6384aebd1468b9d89e024f 100644
--- a/src/cmd/go/internal/work/action.go
+++ b/src/cmd/go/internal/work/action.go
@@ -37,7 +37,7 @@ type Builder struct {
actionCache map[cacheKey]*Action // a cache of already-constructed actions
mkdirCache map[string]bool // a cache of created directories
flagCache map[[2]string]bool // a cache of supported compiler flags
- Print func(args ...interface{}) (int, error)
+ Print func(args ...any) (int, error)
IsCmdList bool // running as part of go list; set p.Stale and additional fields below
NeedError bool // list needs p.Error
@@ -120,8 +120,8 @@ type actionQueue []*Action
func (q *actionQueue) Len() int { return len(*q) }
func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
-func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*Action)) }
-func (q *actionQueue) Pop() interface{} {
+func (q *actionQueue) Push(x any) { *q = append(*q, x.(*Action)) }
+func (q *actionQueue) Pop() any {
n := len(*q) - 1
x := (*q)[n]
*q = (*q)[:n]
@@ -241,7 +241,7 @@ const (
)
func (b *Builder) Init() {
- b.Print = func(a ...interface{}) (int, error) {
+ b.Print = func(a ...any) (int, error) {
return fmt.Fprint(os.Stderr, a...)
}
b.actionCache = make(map[cacheKey]*Action)
@@ -294,14 +294,14 @@ func (b *Builder) Init() {
}
if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {
- fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
+ fmt.Fprintf(os.Stderr, "go: %v\n", err)
base.SetExitStatus(2)
base.Exit()
}
for _, tag := range cfg.BuildContext.BuildTags {
if strings.Contains(tag, ",") {
- fmt.Fprintf(os.Stderr, "cmd/go: -tags space-separated list contains comma\n")
+ fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n")
base.SetExitStatus(2)
base.Exit()
}
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 0ed2389cd5a81af6f3ff862dd275b9fb9ba0109e..9d0ad27f0dc7b7d284d582ca5450fc44048b780b 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
@@ -121,6 +135,14 @@ and test commands:
directory, but it is not accessed. When -modfile is specified, an
alternate go.sum file is also used: its path is derived from the
-modfile flag by trimming the ".mod" extension and appending ".sum".
+ -workfile file
+ in module aware mode, use the given go.work file as a workspace file.
+ By default or when -workfile is "auto", the go command searches for a
+ file named go.work in the current directory and then containing directories
+ until one is found. If a valid go.work file is found, the modules
+ specified will collectively be used as the main modules. If -workfile
+ is "off", or a go.work file is not found in "auto" mode, workspace
+ mode is disabled.
-overlay file
read a JSON config file that provides an overlay for build operations.
The file is a JSON struct with a single field, named 'Replace', that
@@ -201,6 +223,7 @@ func init() {
AddBuildFlags(CmdBuild, DefaultBuildFlags)
AddBuildFlags(CmdInstall, DefaultBuildFlags)
+ base.AddWorkfileFlag(&CmdBuild.Flag)
}
// Note that flags consulted by other parts of the code
@@ -289,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", "", "")
@@ -364,6 +390,7 @@ var pkgsFilter = func(pkgs []*load.Package) []*load.Package { return pkgs }
var runtimeVersion = runtime.Version()
func runBuild(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
BuildInit()
var b Builder
b.Init()
@@ -396,7 +423,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
depMode := ModeBuild
if cfg.BuildI {
depMode = ModeInstall
- fmt.Fprint(os.Stderr, "go build: -i flag is deprecated\n")
+ fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
}
pkgs = omitTestOnly(pkgsFilter(pkgs))
@@ -415,7 +442,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
strings.HasSuffix(cfg.BuildO, "/") ||
strings.HasSuffix(cfg.BuildO, string(os.PathSeparator)) {
if !explicitO {
- base.Fatalf("go build: build output %q already exists and is a directory", cfg.BuildO)
+ base.Fatalf("go: build output %q already exists and is a directory", cfg.BuildO)
}
a := &Action{Mode: "go build"}
for _, p := range pkgs {
@@ -430,13 +457,13 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
a.Deps = append(a.Deps, b.AutoAction(ModeInstall, depMode, p))
}
if len(a.Deps) == 0 {
- base.Fatalf("go build: no main packages to build")
+ base.Fatalf("go: no main packages to build")
}
b.Do(ctx, a)
return
}
if len(pkgs) > 1 {
- base.Fatalf("go build: cannot write multiple packages to non-directory %s", cfg.BuildO)
+ base.Fatalf("go: cannot write multiple packages to non-directory %s", cfg.BuildO)
} else if len(pkgs) == 0 {
base.Fatalf("no packages to build")
}
@@ -486,14 +513,17 @@ allowed, even if they refer to the same version.
- All arguments must refer to packages in the same module at the same version.
+- Package path arguments must refer to main packages. Pattern arguments
+will only match main packages.
+
- No module is considered the "main" module. If the module containing
packages named on the command line has a go.mod file, it must not contain
directives (replace and exclude) that would cause it to be interpreted
differently than if it were the main module. The module must not require
a higher version of itself.
-- Package path arguments must refer to main packages. Pattern arguments
-will only match main packages.
+- Vendor directories are not used in any module. (Vendor directories are not
+included in the module zip files downloaded by 'go install'.)
If the arguments don't have version suffixes, "go install" may run in
module-aware mode or GOPATH mode, depending on the GO111MODULE environment
@@ -580,7 +610,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
for _, arg := range args {
if strings.Contains(arg, "@") && !build.IsLocalImport(arg) && !filepath.IsAbs(arg) {
if cfg.BuildI {
- fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
+ fmt.Fprint(os.Stderr, "go: -i flag is deprecated\n")
}
installOutsideModule(ctx, args)
return
@@ -608,7 +638,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
latestArgs[i] = args[i] + "@latest"
}
hint := strings.Join(latestArgs, " ")
- base.Fatalf("go install: version is required when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint)
+ base.Fatalf("go: 'go install' requires a version when current directory is not in a module\n\tTry 'go install %s' to install the latest version", hint)
}
}
load.CheckPackageErrors(pkgs)
@@ -621,7 +651,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
}
}
if !allGoroot {
- fmt.Fprint(os.Stderr, "go install: -i flag is deprecated\n")
+ fmt.Fprintf(os.Stderr, "go: -i flag is deprecated\n")
}
}
@@ -667,14 +697,14 @@ func InstallPackages(ctx context.Context, patterns []string, pkgs []*load.Packag
case p.Name != "main" && p.Module != nil:
// Non-executables have no target (except the cache) when building with modules.
case p.Internal.GobinSubdir:
- base.Errorf("go %s: cannot install cross-compiled binaries when GOBIN is set", cfg.CmdName)
+ base.Errorf("go: cannot install cross-compiled binaries when GOBIN is set")
case p.Internal.CmdlineFiles:
- base.Errorf("go %s: no install location for .go files listed on command line (GOBIN not set)", cfg.CmdName)
+ base.Errorf("go: no install location for .go files listed on command line (GOBIN not set)")
case p.ConflictDir != "":
- base.Errorf("go %s: no install location for %s: hidden by %s", cfg.CmdName, p.Dir, p.ConflictDir)
+ base.Errorf("go: no install location for %s: hidden by %s", p.Dir, p.ConflictDir)
default:
- base.Errorf("go %s: no install location for directory %s outside GOPATH\n"+
- "\tFor more details see: 'go help gopath'", cfg.CmdName, p.Dir)
+ base.Errorf("go: no install location for directory %s outside GOPATH\n"+
+ "\tFor more details see: 'go help gopath'", p.Dir)
}
}
}
@@ -769,7 +799,7 @@ func installOutsideModule(ctx context.Context, args []string) {
pkgOpts := load.PackageOpts{MainOnly: true}
pkgs, err := load.PackagesAndErrorsOutsideModule(ctx, pkgOpts, args)
if err != nil {
- base.Fatalf("go install: %v", err)
+ base.Fatalf("go: %v", err)
}
load.CheckPackageErrors(pkgs)
patterns := make([]string, len(args))
diff --git a/src/cmd/go/internal/work/build_test.go b/src/cmd/go/internal/work/build_test.go
index 600fc3083f0d506228fe1e26797013f94e11607b..0b6b83a706cd400faba2bb45ef531adce2166180 100644
--- a/src/cmd/go/internal/work/build_test.go
+++ b/src/cmd/go/internal/work/build_test.go
@@ -234,7 +234,7 @@ func TestRespectSetgidDir(t *testing.T) {
// of `(*Builder).ShowCmd` afterwards as a sanity check.
cfg.BuildX = true
var cmdBuf bytes.Buffer
- b.Print = func(a ...interface{}) (int, error) {
+ b.Print = func(a ...any) (int, error) {
return cmdBuf.WriteString(fmt.Sprint(a...))
}
diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go
index 4e9189a36320ace016890c567fd241ffb92a7a87..76335e9bb170b145d71ff15de5d5383090b2db17 100644
--- a/src/cmd/go/internal/work/buildid.go
+++ b/src/cmd/go/internal/work/buildid.go
@@ -570,6 +570,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string)
b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
}
if !cfg.BuildN {
+ b.output.Lock()
+ defer b.output.Unlock()
b.Print(string(stdout))
}
}
@@ -578,6 +580,8 @@ func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string)
// flushOutput flushes the output being queued in a.
func (b *Builder) flushOutput(a *Action) {
+ b.output.Lock()
+ defer b.output.Unlock()
b.Print(string(a.output))
a.output = nil
}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 5a225fb9f1f62666ca959add62e75a458fc8f279..ccd5aee221bd04f7af1c4c603c26e4008c05e9df 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -36,6 +36,8 @@ import (
"cmd/go/internal/modload"
"cmd/go/internal/str"
"cmd/go/internal/trace"
+ "cmd/internal/quoted"
+ "cmd/internal/sys"
)
// actionList returns the list of actions in the dag rooted at root
@@ -222,18 +224,32 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
// same compiler settings and can reuse each other's results.
// If not, the reason is already recorded in buildGcflags.
fmt.Fprintf(h, "compile\n")
- // Only include the package directory if it may affect the output.
- // We trim workspace paths for all packages when -trimpath is set.
- // The compiler hides the exact value of $GOROOT
- // when building things in GOROOT.
- // Assume b.WorkDir is being trimmed properly.
- // When -trimpath is used with a package built from the module cache,
- // use the module path and version instead of the directory.
- if !p.Goroot && !cfg.BuildTrimpath && !strings.HasPrefix(p.Dir, b.WorkDir) {
+
+ // Include information about the origin of the package that
+ // may be embedded in the debug info for the object file.
+ if cfg.BuildTrimpath {
+ // When -trimpath is used with a package built from the module cache,
+ // its debug information refers to the module path and version
+ // instead of the directory.
+ if p.Module != nil {
+ fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)
+ }
+ } else if p.Goroot {
+ // The Go compiler always hides the exact value of $GOROOT
+ // when building things in GOROOT, but the C compiler
+ // merely rewrites GOROOT to GOROOT_FINAL.
+ if len(p.CFiles) > 0 {
+ fmt.Fprintf(h, "goroot %s\n", cfg.GOROOT_FINAL)
+ }
+ // b.WorkDir is always either trimmed or rewritten to
+ // the literal string "/tmp/go-build".
+ } else if !strings.HasPrefix(p.Dir, b.WorkDir) {
+ // -trimpath is not set and no other rewrite rules apply,
+ // so the object file may refer to the absolute directory
+ // containing the package.
fmt.Fprintf(h, "dir %s\n", p.Dir)
- } else if cfg.BuildTrimpath && p.Module != nil {
- fmt.Fprintf(h, "module %s@%s\n", p.Module.Path, p.Module.Version)
}
+
if p.Module != nil {
fmt.Fprintf(h, "go %s\n", p.Module.GoVersion)
}
@@ -281,6 +297,11 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
if p.Internal.CoverMode != "" {
fmt.Fprintf(h, "cover %q %q\n", p.Internal.CoverMode, b.toolID("cover"))
}
+ if p.Internal.FuzzInstrument {
+ if fuzzFlags := fuzzInstrumentFlags(); fuzzFlags != nil {
+ fmt.Fprintf(h, "fuzz %q\n", fuzzFlags)
+ }
+ }
fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo)
// Configuration specific to compiler toolchain.
@@ -773,10 +794,13 @@ OverlayLoop:
}
if p.Internal.BuildInfo != "" && cfg.ModulesEnabled {
- if err := b.writeFile(objdir+"_gomod_.go", modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")); err != nil {
- return err
+ prog := modload.ModInfoProg(p.Internal.BuildInfo, cfg.BuildToolchainName == "gccgo")
+ if len(prog) > 0 {
+ if err := b.writeFile(objdir+"_gomod_.go", prog); err != nil {
+ return err
+ }
+ gofiles = append(gofiles, objdir+"_gomod_.go")
}
- gofiles = append(gofiles, objdir+"_gomod_.go")
}
// Compile Go.
@@ -1373,6 +1397,7 @@ func (b *Builder) writeLinkImportcfg(a *Action, file string) error {
fmt.Fprintf(&icfg, "packageshlib %s=%s\n", p1.ImportPath, p1.Shlib)
}
}
+ fmt.Fprintf(&icfg, "modinfo %q\n", modload.ModInfoData(a.Package.Internal.BuildInfo))
return b.writeFile(file, icfg.Bytes())
}
@@ -1487,6 +1512,8 @@ func (b *Builder) getPkgConfigFlags(p *load.Package) (cflags, ldflags []string,
return nil, nil, errPrintedOutput
}
if len(out) > 0 {
+ // NOTE: we don't attempt to parse quotes and unescapes here. pkg-config
+ // is typically used within shell backticks, which treats quotes literally.
ldflags = strings.Fields(string(out))
if err := checkLinkerFlags("LDFLAGS", "pkg-config --libs", ldflags); err != nil {
return nil, nil, err
@@ -1921,7 +1948,7 @@ func mayberemovefile(s string) {
// fmtcmd replaces the name of the current directory with dot (.)
// but only when it is at the beginning of a space-separated token.
//
-func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string {
+func (b *Builder) fmtcmd(dir string, format string, args ...any) string {
cmd := fmt.Sprintf(format, args...)
if dir != "" && dir != "/" {
dot := " ."
@@ -1947,7 +1974,7 @@ func (b *Builder) fmtcmd(dir string, format string, args ...interface{}) string
// showcmd prints the given command to standard output
// for the implementation of -n or -x.
-func (b *Builder) Showcmd(dir string, format string, args ...interface{}) {
+func (b *Builder) Showcmd(dir string, format string, args ...any) {
b.output.Lock()
defer b.output.Unlock()
b.Print(b.fmtcmd(dir, format, args...) + "\n")
@@ -2011,7 +2038,7 @@ var cgoTypeSigRe = lazyregexp.New(`\b_C2?(type|func|var|macro)_\B`)
// run runs the command given by cmdline in the directory dir.
// If the command fails, run prints information about the failure
// and returns a non-nil error.
-func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...interface{}) error {
+func (b *Builder) run(a *Action, dir string, desc string, env []string, cmdargs ...any) error {
out, err := b.runOut(a, dir, env, cmdargs...)
if len(out) > 0 {
if desc == "" {
@@ -2045,7 +2072,7 @@ func (b *Builder) processOutput(out []byte) string {
// runOut runs the command given by cmdline in the directory dir.
// It returns the command output and any errors that occurred.
// It accumulates execution time in a.
-func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...interface{}) ([]byte, error) {
+func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([]byte, error) {
cmdline := str.StringList(cmdargs...)
for _, arg := range cmdline {
@@ -2382,7 +2409,7 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag
cmd = b.GccCmd(p.Dir, objdir)
}
- cmdargs := []interface{}{cmd, "-o", outfile, objs, flags}
+ cmdargs := []any{cmd, "-o", outfile, objs, flags}
dir := p.Dir
out, err := b.runOut(a, base.Cwd(), b.cCompilerEnv(), cmdargs...)
@@ -2429,12 +2456,6 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag
return err
}
-// Grab these before main helpfully overwrites them.
-var (
- origCC = cfg.Getenv("CC")
- origCXX = cfg.Getenv("CXX")
-)
-
// gccCmd returns a gcc command line prefix
// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
func (b *Builder) GccCmd(incdir, workdir string) []string {
@@ -2454,40 +2475,23 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string {
// ccExe returns the CC compiler setting without all the extra flags we add implicitly.
func (b *Builder) ccExe() []string {
- return b.compilerExe(origCC, cfg.DefaultCC(cfg.Goos, cfg.Goarch))
+ return envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
}
// cxxExe returns the CXX compiler setting without all the extra flags we add implicitly.
func (b *Builder) cxxExe() []string {
- return b.compilerExe(origCXX, cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
+ return envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
}
// fcExe returns the FC compiler setting without all the extra flags we add implicitly.
func (b *Builder) fcExe() []string {
- return b.compilerExe(cfg.Getenv("FC"), "gfortran")
-}
-
-// compilerExe returns the compiler to use given an
-// environment variable setting (the value not the name)
-// and a default. The resulting slice is usually just the name
-// of the compiler but can have additional arguments if they
-// were present in the environment value.
-// For example if CC="gcc -DGOPHER" then the result is ["gcc", "-DGOPHER"].
-func (b *Builder) compilerExe(envValue string, def string) []string {
- compiler := strings.Fields(envValue)
- if len(compiler) == 0 {
- compiler = strings.Fields(def)
- }
- return compiler
+ return envList("FC", "gfortran")
}
// compilerCmd returns a command line prefix for the given environment
// variable and using the default command when the variable is empty.
func (b *Builder) compilerCmd(compiler []string, incdir, workdir string) []string {
- // NOTE: env.go's mkEnv knows that the first three
- // strings returned are "gcc", "-I", incdir (and cuts them off).
- a := []string{compiler[0], "-I", incdir}
- a = append(a, compiler[1:]...)
+ a := append(compiler, "-I", incdir)
// Definitely want -fPIC but on Windows gcc complains
// "-fPIC ignored for target (all code is position independent)"
@@ -2658,12 +2662,20 @@ func (b *Builder) gccArchArgs() []string {
// envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty.
+//
+// The environment variable must be quoted correctly for
+// str.SplitQuotedFields. This should be done before building
+// anything, for example, in BuildInit.
func envList(key, def string) []string {
v := cfg.Getenv(key)
if v == "" {
v = def
}
- return strings.Fields(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))
+ }
+ return args
}
// CFlags returns the flags to use when invoking the C, C++ or Fortran compilers, or cgo.
@@ -2729,6 +2741,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.
@@ -2750,7 +2766,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")
}
@@ -2976,18 +2992,24 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles))
dynobj := objdir + "_cgo_.o"
- // we need to use -pie for Linux/ARM to get accurate imported sym
ldflags := cgoLDFLAGS
if (cfg.Goarch == "arm" && cfg.Goos == "linux") || cfg.Goos == "android" {
- // -static -pie doesn't make sense, and causes link errors.
- // Issue 26197.
- n := make([]string, 0, len(ldflags))
- for _, flag := range ldflags {
- if flag != "-static" {
- n = append(n, flag)
+ if !str.Contains(ldflags, "-no-pie") {
+ // we need to use -pie for Linux/ARM to get accurate imported sym (added in https://golang.org/cl/5989058)
+ // this seems to be outdated, but we don't want to break existing builds depending on this (Issue 45940)
+ ldflags = append(ldflags, "-pie")
+ }
+ if str.Contains(ldflags, "-pie") && str.Contains(ldflags, "-static") {
+ // -static -pie doesn't make sense, and causes link errors.
+ // Issue 26197.
+ n := make([]string, 0, len(ldflags)-1)
+ for _, flag := range ldflags {
+ if flag != "-static" {
+ n = append(n, flag)
+ }
}
+ ldflags = n
}
- ldflags = append(n, "-pie")
}
if err := b.gccld(a, p, objdir, dynobj, ldflags, linkobj); err != nil {
return err
@@ -3309,12 +3331,6 @@ func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) {
return cleanup
}
-// Windows has a limit of 32 KB arguments. To be conservative and not worry
-// about whether that includes spaces or not, just use 30 KB. Darwin's limit is
-// less clear. The OS claims 256KB, but we've seen failures with arglen as
-// small as 50KB.
-const ArgLengthForResponseFile = (30 << 10)
-
func useResponseFile(path string, argLen int) bool {
// Unless the program uses objabi.Flagparse, which understands
// response files, don't use response files.
@@ -3326,7 +3342,7 @@ func useResponseFile(path string, argLen int) bool {
return false
}
- if argLen > ArgLengthForResponseFile {
+ if argLen > sys.ExecArgLengthLimit {
return true
}
diff --git a/src/cmd/go/internal/work/exec_test.go b/src/cmd/go/internal/work/exec_test.go
index 4eb762cb289d28394668f501d1f54b2902da651e..8bbf25bb337e8b44e49cf56ae3dd8dfba4aafcf7 100644
--- a/src/cmd/go/internal/work/exec_test.go
+++ b/src/cmd/go/internal/work/exec_test.go
@@ -7,6 +7,7 @@ package work
import (
"bytes"
"cmd/internal/objabi"
+ "cmd/internal/sys"
"fmt"
"math/rand"
"testing"
@@ -56,7 +57,7 @@ func TestEncodeDecodeFuzz(t *testing.T) {
}
t.Parallel()
- nRunes := ArgLengthForResponseFile + 100
+ nRunes := sys.ExecArgLengthLimit + 100
rBuffer := make([]rune, nRunes)
buf := bytes.NewBuffer([]byte(string(rBuffer)))
@@ -67,7 +68,7 @@ func TestEncodeDecodeFuzz(t *testing.T) {
for i := 0; i < 50; i++ {
// Generate a random string of runes.
buf.Reset()
- for buf.Len() < ArgLengthForResponseFile+1 {
+ for buf.Len() < sys.ExecArgLengthLimit+1 {
var r rune
for {
r = rune(rng.Intn(utf8.MaxRune + 1))
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 85da4f89f991f48e4ad9a3aad4b145f622363cb1..40175324d2654dc029ae5f038ebe4ceb1270c2f5 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -22,6 +22,7 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/str"
"cmd/internal/objabi"
+ "cmd/internal/quoted"
"cmd/internal/sys"
"crypto/sha1"
)
@@ -29,6 +30,18 @@ import (
// The 'path' used for GOROOT_FINAL when -trimpath is specified
const trimPathGoRootFinal = "go"
+var runtimePackages = map[string]struct{}{
+ "internal/abi": struct{}{},
+ "internal/bytealg": struct{}{},
+ "internal/cpu": struct{}{},
+ "internal/goarch": struct{}{},
+ "internal/goos": struct{}{},
+ "runtime": struct{}{},
+ "runtime/internal/atomic": struct{}{},
+ "runtime/internal/math": struct{}{},
+ "runtime/internal/sys": struct{}{},
+}
+
// The Go toolchain.
type gcToolchain struct{}
@@ -63,7 +76,7 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
}
pkgpath := pkgPath(a)
- gcargs := []string{"-p", pkgpath}
+ defaultGcFlags := []string{"-p", pkgpath}
if p.Module != nil {
v := p.Module.GoVersion
if v == "" {
@@ -82,22 +95,19 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
v = "1.16"
}
if allowedVersion(v) {
- gcargs = append(gcargs, "-lang=go"+v)
+ defaultGcFlags = append(defaultGcFlags, "-lang=go"+v)
}
}
if p.Standard {
- gcargs = append(gcargs, "-std")
- }
- compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal"))
- // The runtime package imports a couple of general internal packages.
- if p.Standard && (p.ImportPath == "internal/cpu" || p.ImportPath == "internal/bytealg" || p.ImportPath == "internal/abi") {
- compilingRuntime = true
+ defaultGcFlags = append(defaultGcFlags, "-std")
}
+ _, compilingRuntime := runtimePackages[p.ImportPath]
+ compilingRuntime = compilingRuntime && p.Standard
if compilingRuntime {
// runtime compiles with a special gc flag to check for
// memory allocations that are invalid in the runtime package,
// and to implement some special compiler pragmas.
- gcargs = append(gcargs, "-+")
+ defaultGcFlags = append(defaultGcFlags, "-+")
}
// If we're giving the compiler the entire package (no C etc files), tell it that,
@@ -116,25 +126,28 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
}
}
if extFiles == 0 {
- gcargs = append(gcargs, "-complete")
+ defaultGcFlags = append(defaultGcFlags, "-complete")
}
if cfg.BuildContext.InstallSuffix != "" {
- gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix)
+ defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix)
}
if a.buildID != "" {
- gcargs = append(gcargs, "-buildid", a.buildID)
+ defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID)
}
if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
- gcargs = append(gcargs, "-dwarf=false")
+ defaultGcFlags = append(defaultGcFlags, "-dwarf=false")
}
if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
- gcargs = append(gcargs, "-goversion", runtimeVersion)
+ defaultGcFlags = append(defaultGcFlags, "-goversion", runtimeVersion)
}
if symabis != "" {
- gcargs = append(gcargs, "-symabis", symabis)
+ defaultGcFlags = append(defaultGcFlags, "-symabis", symabis)
}
gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
+ if p.Internal.FuzzInstrument {
+ gcflags = append(gcflags, fuzzInstrumentFlags()...)
+ }
if compilingRuntime {
// Remove -N, if present.
// It is not possible to build the runtime with no optimizations,
@@ -147,10 +160,15 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
}
}
}
+ // Add -c=N to use concurrent backend compilation, if possible.
+ if c := gcBackendConcurrency(gcflags); c > 1 {
+ gcflags = append(gcflags, fmt.Sprintf("-c=%d", c))
+ }
- args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs}
- if p.Internal.LocalPrefix != "" {
- // Workaround #43883.
+ args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags}
+ if p.Internal.LocalPrefix == "" {
+ args = append(args, "-nolocalimports")
+ } else {
args = append(args, "-D", p.Internal.LocalPrefix)
}
if importcfg != nil {
@@ -172,11 +190,6 @@ func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg
args = append(args, "-asmhdr", objdir+"go_asm.h")
}
- // Add -c=N to use concurrent backend compilation, if possible.
- if c := gcBackendConcurrency(gcflags); c > 1 {
- args = append(args, fmt.Sprintf("-c=%d", c))
- }
-
for _, f := range gofiles {
f := mkAbs(p.Dir, f)
@@ -223,7 +236,7 @@ CheckFlags:
// except for known commonly used flags.
// If the user knows better, they can manually add their own -c to the gcflags.
switch flag {
- case "-N", "-l", "-S", "-B", "-C", "-I":
+ case "-N", "-l", "-S", "-B", "-C", "-I", "-shared":
// OK
default:
canDashC = false
@@ -349,11 +362,11 @@ func (a *Action) trimpath() string {
return rewrite
}
-func asmArgs(a *Action, p *load.Package) []interface{} {
+func asmArgs(a *Action, p *load.Package) []any {
// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
inc := filepath.Join(cfg.GOROOT, "pkg", "include")
pkgpath := pkgPath(a)
- args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
+ args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
if p.ImportPath == "runtime" && cfg.Goarch == "386" {
for _, arg := range forcedAsmflags {
if arg == "-dynlink" {
@@ -365,6 +378,16 @@ func asmArgs(a *Action, p *load.Package) []interface{} {
args = append(args, "-compiling-runtime")
}
+ if cfg.Goarch == "386" {
+ // Define GO386_value from cfg.GO386.
+ args = append(args, "-D", "GO386_"+cfg.GO386)
+ }
+
+ if cfg.Goarch == "amd64" {
+ // Define GOAMD64_value from cfg.GOAMD64.
+ args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
+ }
+
if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
// Define GOMIPS_value from cfg.GOMIPS.
args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
@@ -432,8 +455,8 @@ func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, erro
// toolVerify checks that the command line args writes the same output file
// if run using newTool instead.
// Unused now but kept around for future use.
-func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error {
- newArgs := make([]interface{}, len(args))
+func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error {
+ newArgs := make([]any, len(args))
copy(newArgs, args)
newArgs[1] = base.Tool(newTool)
newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
@@ -536,33 +559,18 @@ func packInternal(afile string, ofiles []string) error {
}
// setextld sets the appropriate linker flags for the specified compiler.
-func setextld(ldflags []string, compiler []string) []string {
+func setextld(ldflags []string, compiler []string) ([]string, error) {
for _, f := range ldflags {
if f == "-extld" || strings.HasPrefix(f, "-extld=") {
// don't override -extld if supplied
- return ldflags
+ return ldflags, nil
}
}
- ldflags = append(ldflags, "-extld="+compiler[0])
- if len(compiler) > 1 {
- extldflags := false
- add := strings.Join(compiler[1:], " ")
- for i, f := range ldflags {
- if f == "-extldflags" && i+1 < len(ldflags) {
- ldflags[i+1] = add + " " + ldflags[i+1]
- extldflags = true
- break
- } else if strings.HasPrefix(f, "-extldflags=") {
- ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]
- extldflags = true
- break
- }
- }
- if !extldflags {
- ldflags = append(ldflags, "-extldflags="+add)
- }
+ joined, err := quoted.Join(compiler)
+ if err != nil {
+ return nil, err
}
- return ldflags
+ return append(ldflags, "-extld="+joined), nil
}
// pluginPath computes the package path for a plugin main package.
@@ -649,7 +657,10 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string)
}
ldflags = append(ldflags, forcedLdflags...)
ldflags = append(ldflags, root.Package.Internal.Ldflags...)
- ldflags = setextld(ldflags, compiler)
+ ldflags, err := setextld(ldflags, compiler)
+ if err != nil {
+ return err
+ }
// On OS X when using external linking to build a shared library,
// the argument passed here to -o ends up recorded in the final
@@ -693,7 +704,10 @@ func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action,
} else {
compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
}
- ldflags = setextld(ldflags, compiler)
+ ldflags, err := setextld(ldflags, compiler)
+ if err != nil {
+ return err
+ }
for _, d := range toplevelactions {
if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
continue
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 37a3e2d0ffd1232368863992603432fbd783e828..26192ecaed11cad5ebcbbd6c21ba7b9d0462777c 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -11,8 +11,8 @@ import (
"cmd/go/internal/cfg"
"cmd/go/internal/fsys"
"cmd/go/internal/modload"
+ "cmd/internal/quoted"
"cmd/internal/sys"
- "flag"
"fmt"
"os"
"path/filepath"
@@ -32,27 +32,63 @@ func BuildInit() {
if cfg.BuildPkgdir != "" && !filepath.IsAbs(cfg.BuildPkgdir) {
p, err := filepath.Abs(cfg.BuildPkgdir)
if err != nil {
- fmt.Fprintf(os.Stderr, "go %s: evaluating -pkgdir: %v\n", flag.Args()[0], err)
+ fmt.Fprintf(os.Stderr, "go: evaluating -pkgdir: %v\n", err)
base.SetExitStatus(2)
base.Exit()
}
cfg.BuildPkgdir = p
}
- // Make sure CC and CXX are absolute paths
- for _, key := range []string{"CC", "CXX"} {
- if path := cfg.Getenv(key); !filepath.IsAbs(path) && path != "" && path != filepath.Base(path) {
- base.Fatalf("go %s: %s environment variable is relative; must be absolute path: %s\n", flag.Args()[0], key, path)
+ if cfg.BuildP <= 0 {
+ base.Fatalf("go: -p must be a positive integer: %v\n", cfg.BuildP)
+ }
+
+ // Make sure CC, CXX, and FC are absolute paths.
+ for _, key := range []string{"CC", "CXX", "FC"} {
+ value := cfg.Getenv(key)
+ args, err := quoted.Split(value)
+ if err != nil {
+ base.Fatalf("go: %s environment variable could not be parsed: %v", key, err)
}
+ if len(args) == 0 {
+ continue
+ }
+ path := args[0]
+ if !filepath.IsAbs(path) && path != filepath.Base(path) {
+ base.Fatalf("go: %s environment variable is relative; must be absolute path: %s\n", key, path)
+ }
+ }
+}
+
+// fuzzInstrumentFlags returns compiler flags that enable fuzzing instrumation
+// on supported platforms.
+//
+// On unsupported platforms, fuzzInstrumentFlags returns nil, meaning no
+// 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 {
+ 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 {
- fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously\n", flag.Args()[0])
+ fmt.Fprintf(os.Stderr, "go: may not use -race and -msan simultaneously\n")
+ 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()
}
@@ -61,12 +97,15 @@ func instrumentInit() {
base.SetExitStatus(2)
base.Exit()
}
- if cfg.BuildRace {
- if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
- fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n", flag.Args()[0])
- 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 {
@@ -77,13 +116,16 @@ func instrumentInit() {
cfg.BuildBuildmode = "pie"
}
}
+ if cfg.BuildASan {
+ mode = "asan"
+ }
modeFlag := "-" + mode
if !cfg.BuildContext.CgoEnabled {
if runtime.GOOS != cfg.Goos || runtime.GOARCH != cfg.Goarch {
- fmt.Fprintf(os.Stderr, "go %s: %s requires cgo\n", flag.Args()[0], modeFlag)
+ fmt.Fprintf(os.Stderr, "go: %s requires cgo\n", modeFlag)
} else {
- fmt.Fprintf(os.Stderr, "go %s: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0], modeFlag)
+ fmt.Fprintf(os.Stderr, "go: %s requires cgo; enable cgo by setting CGO_ENABLED=1\n", modeFlag)
}
base.SetExitStatus(2)
@@ -96,7 +138,7 @@ func instrumentInit() {
cfg.BuildContext.InstallSuffix += "_"
}
cfg.BuildContext.InstallSuffix += mode
- cfg.BuildContext.BuildTags = append(cfg.BuildContext.BuildTags, mode)
+ cfg.BuildContext.ToolTags = append(cfg.BuildContext.ToolTags, mode)
}
func buildModeInit() {
diff --git a/src/cmd/go/internal/work/testgo.go b/src/cmd/go/internal/work/testgo.go
index 8b77871b23f26922b4612c9017522710f1511af5..a09b65a23c35cd3bc928200faec75f5fa8de334c 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/internal/workcmd/edit.go b/src/cmd/go/internal/workcmd/edit.go
new file mode 100644
index 0000000000000000000000000000000000000000..c42000710e0f5c56d037288f164156d3d7b7e585
--- /dev/null
+++ b/src/cmd/go/internal/workcmd/edit.go
@@ -0,0 +1,315 @@
+// 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.
+
+// go work edit
+
+package workcmd
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/modload"
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "golang.org/x/mod/module"
+
+ "golang.org/x/mod/modfile"
+)
+
+var cmdEdit = &base.Command{
+ UsageLine: "go work edit [editing flags] [go.work]",
+ Short: "edit go.work from tools or scripts",
+ Long: `Edit provides a command-line interface for editing go.work,
+for use primarily by tools or scripts. It only reads go.work;
+it does not look up information about the modules involved.
+If no file is specified, Edit looks for a go.work file in the current
+directory and its parent directories
+
+The editing flags specify a sequence of editing operations.
+
+The -fmt flag reformats the go.work file without making other changes.
+This reformatting is also implied by any other modifications that use or
+rewrite the go.mod file. The only time this flag is needed is if no other
+flags are specified, as in 'go work edit -fmt'.
+
+The -use=path and -dropuse=path flags
+add and drop a use directive from the go.work file's set of module directories.
+
+The -replace=old[@v]=new[@v] flag adds a replacement of the given
+module path and version pair. If the @v in old@v is omitted, a
+replacement without a version on the left side is added, which applies
+to all versions of the old module path. If the @v in new@v is omitted,
+the new path should be a local module root directory, not a module
+path. Note that -replace overrides any redundant replacements for old[@v],
+so omitting @v will drop existing replacements for specific versions.
+
+The -dropreplace=old[@v] flag drops a replacement of the given
+module path and version pair. If the @v is omitted, a replacement without
+a version on the left side is dropped.
+
+The -use, -dropuse, -replace, and -dropreplace,
+editing flags may be repeated, and the changes are applied in the order given.
+
+The -go=version flag sets the expected Go language version.
+
+The -print flag prints the final go.work in its text format instead of
+writing it back to go.mod.
+
+The -json flag prints the final go.work file in JSON format instead of
+writing it back to go.mod. The JSON output corresponds to these Go types:
+
+ type Module struct {
+ Path string
+ Version string
+ }
+
+ type GoWork struct {
+ Go string
+ Directory []Directory
+ Replace []Replace
+ }
+
+ type Use struct {
+ Path string
+ ModulePath string
+ }
+
+ type Replace struct {
+ Old Module
+ New Module
+ }
+
+See the workspaces design proposal at
+https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
+more information.
+`,
+}
+
+var (
+ editFmt = cmdEdit.Flag.Bool("fmt", false, "")
+ editGo = cmdEdit.Flag.String("go", "", "")
+ editJSON = cmdEdit.Flag.Bool("json", false, "")
+ editPrint = cmdEdit.Flag.Bool("print", false, "")
+ workedits []func(file *modfile.WorkFile) // edits specified in flags
+)
+
+type flagFunc func(string)
+
+func (f flagFunc) String() string { return "" }
+func (f flagFunc) Set(s string) error { f(s); return nil }
+
+func init() {
+ cmdEdit.Run = runEditwork // break init cycle
+
+ cmdEdit.Flag.Var(flagFunc(flagEditworkUse), "use", "")
+ cmdEdit.Flag.Var(flagFunc(flagEditworkDropUse), "dropuse", "")
+ cmdEdit.Flag.Var(flagFunc(flagEditworkReplace), "replace", "")
+ cmdEdit.Flag.Var(flagFunc(flagEditworkDropReplace), "dropreplace", "")
+
+ base.AddWorkfileFlag(&cmdEdit.Flag)
+}
+
+func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
+ anyFlags :=
+ *editGo != "" ||
+ *editJSON ||
+ *editPrint ||
+ *editFmt ||
+ len(workedits) > 0
+
+ if !anyFlags {
+ base.Fatalf("go: no flags specified (see 'go help work edit').")
+ }
+
+ if *editJSON && *editPrint {
+ base.Fatalf("go: cannot use both -json and -print")
+ }
+
+ if len(args) > 1 {
+ base.Fatalf("go: 'go help work edit' accepts at most one argument")
+ }
+ var gowork string
+ if len(args) == 1 {
+ gowork = args[0]
+ } else {
+ modload.InitWorkfile()
+ gowork = modload.WorkFilePath()
+ }
+
+ if *editGo != "" {
+ if !modfile.GoVersionRE.MatchString(*editGo) {
+ base.Fatalf(`go mod: invalid -go option; expecting something like "-go %s"`, modload.LatestGoVersion())
+ }
+ }
+
+ workFile, err := modload.ReadWorkFile(gowork)
+ if err != nil {
+ base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gowork), err)
+ }
+
+ if *editGo != "" {
+ if err := workFile.AddGoStmt(*editGo); err != nil {
+ base.Fatalf("go: internal error: %v", err)
+ }
+ }
+
+ if len(workedits) > 0 {
+ for _, edit := range workedits {
+ edit(workFile)
+ }
+ }
+
+ modload.UpdateWorkFile(workFile)
+
+ workFile.SortBlocks()
+ workFile.Cleanup() // clean file after edits
+
+ if *editJSON {
+ editPrintJSON(workFile)
+ return
+ }
+
+ if *editPrint {
+ os.Stdout.Write(modfile.Format(workFile.Syntax))
+ return
+ }
+
+ modload.WriteWorkFile(gowork, workFile)
+}
+
+// flagEditworkUse implements the -use flag.
+func flagEditworkUse(arg string) {
+ workedits = append(workedits, func(f *modfile.WorkFile) {
+ _, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil)
+ modulePath := ""
+ if err == nil {
+ modulePath = mf.Module.Mod.Path
+ }
+ f.AddUse(modload.ToDirectoryPath(arg), modulePath)
+ if err := f.AddUse(modload.ToDirectoryPath(arg), ""); err != nil {
+ base.Fatalf("go: -use=%s: %v", arg, err)
+ }
+ })
+}
+
+// flagEditworkDropUse implements the -dropuse flag.
+func flagEditworkDropUse(arg string) {
+ workedits = append(workedits, func(f *modfile.WorkFile) {
+ if err := f.DropUse(modload.ToDirectoryPath(arg)); err != nil {
+ base.Fatalf("go: -dropdirectory=%s: %v", arg, err)
+ }
+ })
+}
+
+// allowedVersionArg returns whether a token may be used as a version in go.mod.
+// We don't call modfile.CheckPathVersion, because that insists on versions
+// being in semver form, but here we want to allow versions like "master" or
+// "1234abcdef", which the go command will resolve the next time it runs (or
+// during -fix). Even so, we need to make sure the version is a valid token.
+func allowedVersionArg(arg string) bool {
+ return !modfile.MustQuote(arg)
+}
+
+// parsePathVersionOptional parses path[@version], using adj to
+// describe any errors.
+func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
+ if i := strings.Index(arg, "@"); i < 0 {
+ path = arg
+ } else {
+ path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
+ }
+ if err := module.CheckImportPath(path); err != nil {
+ if !allowDirPath || !modfile.IsDirectoryPath(path) {
+ return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
+ }
+ }
+ if path != arg && !allowedVersionArg(version) {
+ return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
+ }
+ return path, version, nil
+}
+
+// flagReplace implements the -replace flag.
+func flagEditworkReplace(arg string) {
+ var i int
+ if i = strings.Index(arg, "="); i < 0 {
+ base.Fatalf("go: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
+ }
+ old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
+ if strings.HasPrefix(new, ">") {
+ base.Fatalf("go: -replace=%s: separator between old and new is =, not =>", arg)
+ }
+ oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
+ if err != nil {
+ base.Fatalf("go: -replace=%s: %v", arg, err)
+ }
+ newPath, newVersion, err := parsePathVersionOptional("new", new, true)
+ if err != nil {
+ base.Fatalf("go: -replace=%s: %v", arg, err)
+ }
+ if newPath == new && !modfile.IsDirectoryPath(new) {
+ base.Fatalf("go: -replace=%s: unversioned new path must be local directory", arg)
+ }
+
+ workedits = append(workedits, func(f *modfile.WorkFile) {
+ if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
+ base.Fatalf("go: -replace=%s: %v", arg, err)
+ }
+ })
+}
+
+// flagDropReplace implements the -dropreplace flag.
+func flagEditworkDropReplace(arg string) {
+ path, version, err := parsePathVersionOptional("old", arg, true)
+ if err != nil {
+ base.Fatalf("go: -dropreplace=%s: %v", arg, err)
+ }
+ workedits = append(workedits, func(f *modfile.WorkFile) {
+ if err := f.DropReplace(path, version); err != nil {
+ base.Fatalf("go: -dropreplace=%s: %v", arg, err)
+ }
+ })
+}
+
+type replaceJSON struct {
+ Old module.Version
+ New module.Version
+}
+
+// editPrintJSON prints the -json output.
+func editPrintJSON(workFile *modfile.WorkFile) {
+ var f workfileJSON
+ if workFile.Go != nil {
+ f.Go = workFile.Go.Version
+ }
+ for _, d := range workFile.Use {
+ f.Use = append(f.Use, useJSON{DiskPath: d.Path, ModPath: d.ModulePath})
+ }
+
+ for _, r := range workFile.Replace {
+ f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
+ }
+ data, err := json.MarshalIndent(&f, "", "\t")
+ if err != nil {
+ base.Fatalf("go: internal error: %v", err)
+ }
+ data = append(data, '\n')
+ os.Stdout.Write(data)
+}
+
+// workfileJSON is the -json output data structure.
+type workfileJSON struct {
+ Go string `json:",omitempty"`
+ Use []useJSON
+ Replace []replaceJSON
+}
+
+type useJSON struct {
+ DiskPath string
+ ModPath string `json:",omitempty"`
+}
diff --git a/src/cmd/go/internal/workcmd/init.go b/src/cmd/go/internal/workcmd/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..2297ac20d0a4ead31a74fb366c0086aae746c86d
--- /dev/null
+++ b/src/cmd/go/internal/workcmd/init.go
@@ -0,0 +1,54 @@
+// 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.
+
+// go work init
+
+package workcmd
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/modload"
+ "context"
+ "path/filepath"
+)
+
+// TODO(#49232) Add more documentation below. Though this is
+// enough for those trying workspaces out, there should be more through
+// documentation before Go 1.18 is released.
+
+var cmdInit = &base.Command{
+ UsageLine: "go work init [moddirs]",
+ Short: "initialize workspace file",
+ Long: `Init initializes and writes a new go.work file in the current
+directory, in effect creating a new workspace at the current directory.
+
+go work init optionally accepts paths to the workspace modules as arguments.
+If the argument is omitted, an empty workspace with no modules will be created.
+
+See the workspaces design proposal at
+https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
+more information.
+`,
+ Run: runInit,
+}
+
+func init() {
+ base.AddModCommonFlags(&cmdInit.Flag)
+ base.AddWorkfileFlag(&cmdInit.Flag)
+}
+
+func runInit(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
+ modload.ForceUseModules = true
+
+ // TODO(matloob): support using the -workfile path
+ // To do that properly, we'll have to make the module directories
+ // make dirs relative to workFile path before adding the paths to
+ // the directory entries
+
+ workFile := filepath.Join(base.Cwd(), "go.work")
+
+ modload.CreateWorkFile(ctx, workFile, args)
+}
diff --git a/src/cmd/go/internal/workcmd/sync.go b/src/cmd/go/internal/workcmd/sync.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f33e057f6d58d79e236fd6e76b2879bded0b2d1
--- /dev/null
+++ b/src/cmd/go/internal/workcmd/sync.go
@@ -0,0 +1,119 @@
+// 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.
+
+// go work sync
+
+package workcmd
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/imports"
+ "cmd/go/internal/modload"
+ "context"
+
+ "golang.org/x/mod/module"
+)
+
+// TODO(#49232) Add more documentation below. Though this is
+// enough for those trying workspaces out, there should be more thorough
+// documentation before Go 1.18 is released.
+
+var cmdSync = &base.Command{
+ UsageLine: "go work sync [moddirs]",
+ Short: "sync workspace build list to modules",
+ Long: `go work sync`,
+ Run: runSync,
+}
+
+func init() {
+ base.AddModCommonFlags(&cmdSync.Flag)
+ base.AddWorkfileFlag(&cmdSync.Flag)
+}
+
+func runSync(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
+ modload.ForceUseModules = true
+
+ workGraph := modload.LoadModGraph(ctx, "")
+ _ = workGraph
+ mustSelectFor := map[module.Version][]module.Version{}
+
+ mms := modload.MainModules
+
+ opts := modload.PackageOpts{
+ Tags: imports.AnyTags(),
+ VendorModulesInGOROOTSrc: true,
+ ResolveMissingImports: false,
+ LoadTests: true,
+ AllowErrors: true,
+ SilencePackageErrors: true,
+ SilenceUnmatchedWarnings: true,
+ }
+ for _, m := range mms.Versions() {
+ opts.MainModule = m
+ _, pkgs := modload.LoadPackages(ctx, opts, "all")
+ opts.MainModule = module.Version{} // reset
+
+ var (
+ mustSelect []module.Version
+ inMustSelect = map[module.Version]bool{}
+ )
+ for _, pkg := range pkgs {
+ if r := modload.PackageModule(pkg); r.Version != "" && !inMustSelect[r] {
+ // r has a known version, so force that version.
+ mustSelect = append(mustSelect, r)
+ inMustSelect[r] = true
+ }
+ }
+ module.Sort(mustSelect) // ensure determinism
+ mustSelectFor[m] = mustSelect
+ }
+
+ workFilePath := modload.WorkFilePath() // save go.work path because EnterModule clobbers it.
+
+ for _, m := range mms.Versions() {
+ if mms.ModRoot(m) == "" && m.Path == "command-line-arguments" {
+ // This is not a real module.
+ // TODO(#49228): Remove this special case once the special
+ // command-line-arguments module is gone.
+ continue
+ }
+
+ // Use EnterModule to reset the global state in modload to be in
+ // single-module mode using the modroot of m.
+ modload.EnterModule(ctx, mms.ModRoot(m))
+
+ // Edit the build list in the same way that 'go get' would if we
+ // requested the relevant module versions explicitly.
+ changed, err := modload.EditBuildList(ctx, nil, mustSelectFor[m])
+ if err != nil {
+ base.Errorf("go: %v", err)
+ }
+ if !changed {
+ continue
+ }
+
+ modload.LoadPackages(ctx, modload.PackageOpts{
+ Tags: imports.AnyTags(),
+ VendorModulesInGOROOTSrc: true,
+ ResolveMissingImports: false,
+ LoadTests: true,
+ AllowErrors: true,
+ SilencePackageErrors: true,
+ Tidy: true,
+ SilenceUnmatchedWarnings: true,
+ }, "all")
+ modload.WriteGoMod(ctx)
+ }
+
+ wf, err := modload.ReadWorkFile(workFilePath)
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
+ modload.UpdateWorkFile(wf)
+ if err := modload.WriteWorkFile(workFilePath, wf); err != nil {
+ base.Fatalf("go: %v", err)
+ }
+}
diff --git a/src/cmd/go/internal/workcmd/use.go b/src/cmd/go/internal/workcmd/use.go
new file mode 100644
index 0000000000000000000000000000000000000000..97c493685ad95dc83211e5a8b7a411ccf4788925
--- /dev/null
+++ b/src/cmd/go/internal/workcmd/use.go
@@ -0,0 +1,115 @@
+// 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.
+
+// go work use
+
+package workcmd
+
+import (
+ "cmd/go/internal/base"
+ "cmd/go/internal/fsys"
+ "cmd/go/internal/modload"
+ "context"
+ "io/fs"
+ "os"
+ "path/filepath"
+)
+
+// TODO(#49232) Add more documentation below. Though this is
+// enough for those trying workspaces out, there should be more thorough
+// documentation before Go 1.18 is released.
+
+var cmdUse = &base.Command{
+ UsageLine: "go work use [-r] [moddirs]",
+ Short: "add modules to workspace file",
+ Long: `Use provides a command-line interface for adding directories,
+optionally recursively, to a go.work file.
+
+The -r flag searches recursively for modules in the argument directories.`,
+}
+
+var useR = cmdUse.Flag.Bool("r", false, "")
+
+func init() {
+ cmdUse.Run = runUse // break init cycle
+
+ base.AddModCommonFlags(&cmdUse.Flag)
+ base.AddWorkfileFlag(&cmdUse.Flag)
+}
+
+func runUse(ctx context.Context, cmd *base.Command, args []string) {
+ modload.InitWorkfile()
+
+ modload.ForceUseModules = true
+
+ var gowork string
+ modload.InitWorkfile()
+ gowork = modload.WorkFilePath()
+
+ workFile, err := modload.ReadWorkFile(gowork)
+ if err != nil {
+ base.Fatalf("go: %v", err)
+ }
+
+ haveDirs := make(map[string]bool)
+ for _, dir := range workFile.Use {
+ haveDirs[filepath.Join(filepath.Dir(gowork), filepath.FromSlash(dir.Path))] = true
+ }
+
+ addDirs := make(map[string]bool)
+ removeDirs := make(map[string]bool)
+ lookDir := func(dir string) {
+ absDir := filepath.Join(base.Cwd(), dir)
+ // If the path is absolute, keep it absolute. If it's relative,
+ // make it relative to the go.work file rather than the working directory.
+ if !filepath.IsAbs(dir) {
+ rel, err := filepath.Rel(filepath.Dir(gowork), absDir)
+ if err == nil {
+ dir = rel
+ }
+ }
+ fi, err := os.Stat(filepath.Join(dir, "go.mod"))
+ if err != nil {
+ if os.IsNotExist(err) {
+
+ if haveDirs[absDir] {
+ removeDirs[dir] = true
+ }
+ return
+ }
+ base.Errorf("go: %v", err)
+ }
+
+ if !fi.Mode().IsRegular() {
+ base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod"))
+ }
+
+ if !haveDirs[absDir] {
+ addDirs[dir] = true
+ }
+ }
+
+ for _, useDir := range args {
+ if *useR {
+ fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error {
+ if !info.IsDir() {
+ return nil
+ }
+ lookDir(path)
+ return nil
+ })
+ continue
+ }
+ lookDir(useDir)
+ }
+
+ for dir := range removeDirs {
+ workFile.DropUse(filepath.ToSlash(dir))
+ }
+ for dir := range addDirs {
+ workFile.AddUse(filepath.ToSlash(dir), "")
+ }
+ modload.UpdateWorkFile(workFile)
+ modload.WriteWorkFile(gowork, workFile)
+}
diff --git a/src/cmd/go/internal/workcmd/work.go b/src/cmd/go/internal/workcmd/work.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ddbfbe7726ec25776b4650857171597bdfc1759
--- /dev/null
+++ b/src/cmd/go/internal/workcmd/work.go
@@ -0,0 +1,40 @@
+// 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 workcmd implements the ``go work'' command.
+package workcmd
+
+import (
+ "cmd/go/internal/base"
+)
+
+var CmdWork = &base.Command{
+ UsageLine: "go work",
+ Short: "workspace maintenance",
+ Long: `Go workspace provides access to operations on workspaces.
+
+Note that support for workspaces is built into many other commands,
+not just 'go work'.
+
+See 'go help modules' for information about Go's module system of
+which workspaces are a part.
+
+A workspace is specified by a go.work file that specifies a set of
+module directories with the "use" directive. These modules are used
+as root modules by the go command for builds and related operations.
+A workspace that does not specify modules to be used cannot be used
+to do builds from local modules.
+
+To determine whether the go command is operating in workspace mode,
+use the "go env GOWORK" command. This will specify the workspace
+file being used.
+`,
+
+ Commands: []*base.Command{
+ cmdEdit,
+ cmdInit,
+ cmdSync,
+ cmdUse,
+ },
+}
diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 16361e02ca7f7c4d1cadee8a6f93aa3d2b3d46ed..c0a1d3ccfc496cd04d7d3edc3d864455dddbcee9 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -7,6 +7,7 @@
package main
import (
+ "cmd/go/internal/workcmd"
"context"
"flag"
"fmt"
@@ -56,6 +57,7 @@ func init() {
work.CmdInstall,
list.CmdList,
modcmd.CmdMod,
+ workcmd.CmdWork,
run.CmdRun,
test.CmdTest,
tool.CmdTool,
diff --git a/src/cmd/go/proxy_test.go b/src/cmd/go/proxy_test.go
index 74bfecc08dbc6de1ca9768c536113eb878d7f3da..517a88554282de5f133b8fc1196e112abf5a0d87 100644
--- a/src/cmd/go/proxy_test.go
+++ b/src/cmd/go/proxy_test.go
@@ -25,12 +25,12 @@ import (
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/par"
- "cmd/go/internal/txtar"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
"golang.org/x/mod/sumdb"
"golang.org/x/mod/sumdb/dirhash"
+ "golang.org/x/tools/txtar"
)
var (
@@ -357,7 +357,7 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
zip []byte
err error
}
- c := zipCache.Do(a, func() interface{} {
+ c := zipCache.Do(a, func() any {
var buf bytes.Buffer
z := zip.NewWriter(&buf)
for _, f := range a.Files {
@@ -431,7 +431,7 @@ func readArchive(path, vers string) (*txtar.Archive, error) {
prefix := strings.ReplaceAll(enc, "/", "_")
name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt")
- a := archiveCache.Do(name, func() interface{} {
+ a := archiveCache.Do(name, func() any {
a, err := txtar.ParseFile(name)
if err != nil {
if testing.Verbose() || !os.IsNotExist(err) {
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 639e907db075159dd5aba227d38e7ec9261127bf..dbfba2291c44d5971b7faac4e730ee6ab468c117 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -11,6 +11,7 @@ import (
"bytes"
"context"
"errors"
+ "flag"
"fmt"
"go/build"
"internal/testenv"
@@ -30,11 +31,14 @@ import (
"cmd/go/internal/imports"
"cmd/go/internal/par"
"cmd/go/internal/robustio"
- "cmd/go/internal/txtar"
"cmd/go/internal/work"
"cmd/internal/sys"
+
+ "golang.org/x/tools/txtar"
)
+var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`)
+
// TestScript runs the tests in testdata/script/*.txt.
func TestScript(t *testing.T) {
testenv.MustHaveGoBuild(t)
@@ -142,6 +146,7 @@ var extraEnvKeys = []string{
"GO_TESTING_GOTOOLS", // for gccgo testing
"GCCGO", // for gccgo testing
"GCCGOTOOLDIR", // for gccgo testing
+ "MallocNanoZone", // Needed to work around an apparent kernel bug in macOS 12; see https://golang.org/issue/49138.
}
// setup sets up the test execution temporary directory and environment.
@@ -181,6 +186,7 @@ func (ts *testScript) setup() {
"devnull=" + os.DevNull,
"goversion=" + goVersion(ts),
":=" + string(os.PathListSeparator),
+ "/=" + string(os.PathSeparator),
}
if !testenv.HasExternalNetwork() {
ts.env = append(ts.env, "TESTGONETWORK=panic", "TESTGOVCS=panic")
@@ -269,6 +275,22 @@ func (ts *testScript) run() {
ts.mark = ts.log.Len()
}
+ // With -testsum, if a go.mod file is present in the test's initial
+ // working directory, run 'go mod tidy'.
+ if *testSum != "" {
+ if ts.updateSum(a) {
+ defer func() {
+ if ts.t.Failed() {
+ return
+ }
+ data := txtar.Format(a)
+ if err := os.WriteFile(ts.file, data, 0666); err != nil {
+ ts.t.Errorf("rewriting test file: %v", err)
+ }
+ }()
+ }
+ }
+
// Run script.
// See testdata/script/README for documentation of script form.
script := string(a.Comment)
@@ -332,8 +354,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":
@@ -347,7 +375,7 @@ Script:
default:
if strings.HasPrefix(cond.tag, "exec:") {
prog := cond.tag[len("exec:"):]
- ok = execCache.Do(prog, func() interface{} {
+ ok = execCache.Do(prog, func() any {
if runtime.GOOS == "plan9" && prog == "git" {
// The Git command is usually not the real Git on Plan 9.
// See https://golang.org/issues/29640.
@@ -1109,6 +1137,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)
@@ -1125,6 +1164,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.
@@ -1151,7 +1257,7 @@ func waitOrStop(ctx context.Context, cmd *exec.Cmd, interrupt os.Signal, killDel
err := cmd.Process.Signal(interrupt)
if err == nil {
err = ctx.Err() // Report ctx.Err() as the reason we interrupted.
- } else if err.Error() == "os: process already finished" {
+ } else if err == os.ErrProcessDone {
errc <- nil
return
}
@@ -1204,7 +1310,7 @@ func (ts *testScript) expand(s string, inRegexp bool) string {
}
// fatalf aborts the test with the given failure message.
-func (ts *testScript) fatalf(format string, args ...interface{}) {
+func (ts *testScript) fatalf(format string, args ...any) {
fmt.Fprintf(&ts.log, "FAIL: %s:%d: %s\n", ts.file, ts.lineno, fmt.Sprintf(format, args...))
ts.t.FailNow()
}
@@ -1341,6 +1447,68 @@ func (ts *testScript) parse(line string) command {
return cmd
}
+// updateSum runs 'go mod tidy', 'go list -mod=mod -m all', or
+// 'go list -mod=mod all' in the test's current directory if a file named
+// "go.mod" is present after the archive has been extracted. updateSum modifies
+// archive and returns true if go.mod or go.sum were changed.
+func (ts *testScript) updateSum(archive *txtar.Archive) (rewrite bool) {
+ gomodIdx, gosumIdx := -1, -1
+ for i := range archive.Files {
+ switch archive.Files[i].Name {
+ case "go.mod":
+ gomodIdx = i
+ case "go.sum":
+ gosumIdx = i
+ }
+ }
+ if gomodIdx < 0 {
+ return false
+ }
+
+ switch *testSum {
+ case "tidy":
+ ts.cmdGo(success, []string{"mod", "tidy"})
+ case "listm":
+ ts.cmdGo(success, []string{"list", "-m", "-mod=mod", "all"})
+ case "listall":
+ ts.cmdGo(success, []string{"list", "-mod=mod", "all"})
+ default:
+ ts.t.Fatalf(`unknown value for -testsum %q; may be "tidy", "listm", or "listall"`, *testSum)
+ }
+
+ newGomodData, err := os.ReadFile(filepath.Join(ts.cd, "go.mod"))
+ if err != nil {
+ ts.t.Fatalf("reading go.mod after -testsum: %v", err)
+ }
+ if !bytes.Equal(newGomodData, archive.Files[gomodIdx].Data) {
+ archive.Files[gomodIdx].Data = newGomodData
+ rewrite = true
+ }
+
+ newGosumData, err := os.ReadFile(filepath.Join(ts.cd, "go.sum"))
+ if err != nil && !os.IsNotExist(err) {
+ ts.t.Fatalf("reading go.sum after -testsum: %v", err)
+ }
+ switch {
+ case os.IsNotExist(err) && gosumIdx >= 0:
+ // go.sum was deleted.
+ rewrite = true
+ archive.Files = append(archive.Files[:gosumIdx], archive.Files[gosumIdx+1:]...)
+ case err == nil && gosumIdx < 0:
+ // go.sum was created.
+ rewrite = true
+ gosumIdx = gomodIdx + 1
+ archive.Files = append(archive.Files, txtar.File{})
+ copy(archive.Files[gosumIdx+1:], archive.Files[gosumIdx:])
+ archive.Files[gosumIdx] = txtar.File{Name: "go.sum", Data: newGosumData}
+ case err == nil && gosumIdx >= 0 && !bytes.Equal(newGosumData, archive.Files[gosumIdx].Data):
+ // go.sum was changed.
+ rewrite = true
+ archive.Files[gosumIdx].Data = newGosumData
+ }
+ return rewrite
+}
+
// diff returns a formatted diff of the two texts,
// showing the entire text and the minimum line-level
// additions and removals to turn text1 into text2.
diff --git a/src/cmd/go/stop_other_test.go b/src/cmd/go/stop_other_test.go
index e1cc6cf8ba755fffd719f14a2e0455b2ae7874e5..35c12858c1d5e1d36772a0285313c410b7643058 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 ac35b240f0ad9727845fed622a5112f704b95909..5939f0d40d996777493cc6f64263ed340d71029e 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/addmod.go b/src/cmd/go/testdata/addmod.go
index 03869e68defe0845c5cbdef60fa08f2fd3fdf42d..eac2a7ad449a2fea361545cd27a712bfe0821a92 100644
--- a/src/cmd/go/testdata/addmod.go
+++ b/src/cmd/go/testdata/addmod.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
// Addmod adds a module as a txtar archive to the testdata/mod directory.
@@ -29,7 +30,7 @@ import (
"path/filepath"
"strings"
- "cmd/go/internal/txtar"
+ "golang.org/x/tools/txtar"
)
func usage() {
@@ -39,7 +40,7 @@ func usage() {
var tmpdir string
-func fatalf(format string, args ...interface{}) {
+func fatalf(format string, args ...any) {
os.RemoveAll(tmpdir)
log.Fatalf(format, args...)
}
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 0000000000000000000000000000000000000000..af005ffb4119080e158cd4b9cd7ce44d0b951112
--- /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 0000000000000000000000000000000000000000..ea599aa61109aa2138746686412c0d035582c2ea
--- /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/mod/example.com_split-incompatible_v2.0.0+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
index 35c3f277103df132a0588eb4fe5ef6ca4689e18f..00076d74fc2f8868a1e78de129bcbe6a6fbf1d51 100644
--- a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
+++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.0.0+incompatible.txt
@@ -1,6 +1,6 @@
Written by hand.
Test case for getting a package that has been moved to a nested module,
-with a +incompatible verison (and thus no go.mod file) at the root module.
+with a +incompatible version (and thus no go.mod file) at the root module.
-- .mod --
module example.com/split-incompatible
diff --git a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
index 917fc0f55992efa69f6767a486fd2a2908bded95..bb1c1fecc9d2713360069cf593b6df89e6e1c303 100644
--- a/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
+++ b/src/cmd/go/testdata/mod/example.com_split-incompatible_v2.1.0-pre+incompatible.txt
@@ -1,6 +1,6 @@
Written by hand.
Test case for getting a package that has been moved to a nested module,
-with a +incompatible verison (and thus no go.mod file) at the root module.
+with a +incompatible version (and thus no go.mod file) at the root module.
-- .mod --
module example.com/split-incompatible
diff --git a/src/cmd/go/testdata/savedir.go b/src/cmd/go/testdata/savedir.go
index d469c31a919821171affd2be2e88b12ef3861de0..53c78cfb00bbac391b9b1f818a566a0d8d786555 100644
--- a/src/cmd/go/testdata/savedir.go
+++ b/src/cmd/go/testdata/savedir.go
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build ignore
// +build ignore
// Savedir archives a directory tree as a txtar archive printed to standard output.
@@ -24,7 +25,7 @@ import (
"strings"
"unicode/utf8"
- "../internal/txtar"
+ "golang.org/x/tools/txtar"
)
func usage() {
diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README
index 48e4055b0bdb971397be7dd2b0b909205a46a588..2b55fa89777d17f7e2927980a5a52a69b01e60c4 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/build_cache_disabled.txt b/src/cmd/go/testdata/script/build_cache_disabled.txt
index 8b005c857fcf99dd89fda7ef752cf13b52864471..cb1a7558fca71f3edab009c32329376f45b7a45e 100644
--- a/src/cmd/go/testdata/script/build_cache_disabled.txt
+++ b/src/cmd/go/testdata/script/build_cache_disabled.txt
@@ -9,7 +9,7 @@
# * go fix
# * go fmt
# * go generate
-# * go get -d
+# * go get
# * go list (without -export or -compiled)
env GOCACHE=off
diff --git a/src/cmd/go/testdata/script/build_cache_trimpath.txt b/src/cmd/go/testdata/script/build_cache_trimpath.txt
index 9a4b9d7b40c7264ea9cec6df11a66006be3a41fe..7ee3c3b41d489a7cd3059efe567737f25b040559 100644
--- a/src/cmd/go/testdata/script/build_cache_trimpath.txt
+++ b/src/cmd/go/testdata/script/build_cache_trimpath.txt
@@ -16,10 +16,10 @@ stderr 'link( |\.exe)'
# Two distinct versions of the same module with identical content should
# still be cached separately.
# Verifies golang.org/issue/35412.
-go get -d example.com/stack@v1.0.0
+go get example.com/stack@v1.0.0
go run -trimpath printstack.go
stdout '^example.com/stack@v1.0.0/stack.go$'
-go get -d example.com/stack@v1.0.1
+go get example.com/stack@v1.0.1
go run -trimpath printstack.go
stdout '^example.com/stack@v1.0.1/stack.go$'
diff --git a/src/cmd/go/testdata/script/build_concurrent_backend.txt b/src/cmd/go/testdata/script/build_concurrent_backend.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9cac635e5a6760ca21b89163cdf6eec6b650382f
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_concurrent_backend.txt
@@ -0,0 +1,10 @@
+# Tests golang.org/issue/48490
+# cmd/go should enable concurrent compilation by default
+
+# Reset all experiments, since one of them can disable
+# concurrent compilation, e.g: fieldtrack.
+env GOEXPERIMENT=none
+
+env GOMAXPROCS=4
+go build -n -x -a fmt
+stderr ' -c=4 '
diff --git a/src/cmd/go/testdata/script/build_gcflags_order.txt b/src/cmd/go/testdata/script/build_gcflags_order.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0ffe1570f6c6e40c062f318f6cf046a568d0f032
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_gcflags_order.txt
@@ -0,0 +1,20 @@
+# Tests golang.org/issue/47682
+# Flags specified with -gcflags should appear after other flags generated by cmd/go.
+
+cd m
+go build -n -gcflags=-lang=go1.17
+stderr ' -lang=go1.16.* -lang=go1.17'
+
+-- m/go.mod --
+module example.com
+
+go 1.16
+
+-- m/main.go --
+package main
+
+func main() {
+ var s = []int{1, 2, 3}
+ var pa = (*[2]int)(s[1:])
+ println(pa[1])
+}
diff --git a/src/cmd/go/testdata/script/build_i_deprecate.txt b/src/cmd/go/testdata/script/build_i_deprecate.txt
index 71356e5321eb1ca3ab2b9227c55ceba8eb4710d1..5c1799566999e44a76c4555af4e06cfd67b592fd 100644
--- a/src/cmd/go/testdata/script/build_i_deprecate.txt
+++ b/src/cmd/go/testdata/script/build_i_deprecate.txt
@@ -2,13 +2,13 @@
# TODO(golang.org/issue/41696): remove the -i flag after Go 1.16, and this test.
go build -n -i
-stderr '^go build: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
go install -n -i
-stderr '^go install: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
go test -n -i
-stderr '^go test: -i flag is deprecated$'
+stderr '^go: -i flag is deprecated$'
# 'go clean -i' should not print a deprecation warning.
diff --git a/src/cmd/go/testdata/script/build_issue48319.txt b/src/cmd/go/testdata/script/build_issue48319.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f58a5faa3fdd93584425c2afc244d7f07e778a9a
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_issue48319.txt
@@ -0,0 +1,153 @@
+[short] skip
+[!cgo] skip
+
+# Set up fresh GOCACHE
+env GOCACHE=$WORK/gocache
+mkdir $GOCACHE
+
+# 1. unset GOROOT_FINAL, Build a simple binary with cgo by origin go.
+# The DW_AT_comp_dir of runtime/cgo should have a prefix with origin goroot.
+env GOROOT_FINAL=
+# If using "go run", it is no debuginfo in binary. So use "go build".
+# And we can check the stderr to judge if the cache of "runtime/cgo"
+# was used or not.
+go build -o binary.exe
+exec ./binary.exe $TESTGO_GOROOT
+stdout 'cgo DW_AT_comp_dir is right in binary'
+
+
+# 2. GOROOT_FINAL will be changed, the runtime/cgo will be rebuild.
+env GOROOT_FINAL=$WORK/gorootfinal
+go build -x -o binary.exe
+stderr '(clang|gcc)( |\.exe).*gcc_.*\.c'
+exec ./binary.exe $GOROOT_FINAL
+stdout 'cgo DW_AT_comp_dir is right in binary'
+
+
+[!symlink] skip
+
+# Symlink the compiler to another path
+env GOROOT=$WORK/goroot
+symlink $GOROOT -> $TESTGO_GOROOT
+
+# 3. GOROOT_FINAL is same with 2, build with the other go
+# the runtime/cgo will not be rebuild.
+go build -x -o binary.exe
+! stderr '(clang|gcc)( |\.exe).*gcc_.*\.c'
+exec ./binary.exe $GOROOT_FINAL
+stdout 'cgo DW_AT_comp_dir is right in binary'
+
+
+# 4. unset GOROOT_FINAL, build with the other go
+# the runtime/cgo will be rebuild.
+env GOROOT_FINAL=
+go build -x -o binary.exe
+stderr '(clang|gcc)( |\.exe).*gcc_.*\.c'
+exec ./binary.exe $GOROOT
+stdout 'cgo DW_AT_comp_dir is right in binary'
+
+-- go.mod --
+module main
+
+go 1.18
+-- main.go --
+package main
+
+import "C"
+import (
+ "debug/dwarf"
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var _ C.int
+
+func main() {
+ dwarfData, err := readDWARF(os.Args[0])
+ if err != nil {
+ log.Fatal(err)
+ }
+ goroot := filepath.Join(os.Args[1], "src")
+ dwarfReader := dwarfData.Reader()
+ cgopackage := filepath.Join("runtime", "cgo")
+ var hascgo bool
+ for {
+ e, err := dwarfReader.Next()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if e == nil {
+ break
+ }
+ field := e.AttrField(dwarf.AttrCompDir)
+ if field == nil {
+ continue
+ }
+ compdir := field.Val.(string)
+ if strings.HasSuffix(compdir, cgopackage) {
+ hascgo = true
+ if !strings.HasPrefix(compdir, goroot) {
+ fmt.Printf("cgo DW_AT_comp_dir %s contains incorrect path in binary.\n", compdir)
+ return
+ }
+ }
+ }
+ if hascgo {
+ fmt.Println("cgo DW_AT_comp_dir is right in binary")
+ } else {
+ fmt.Println("binary does not contain cgo")
+ }
+}
+-- read_darwin.go --
+package main
+
+import (
+ "debug/dwarf"
+ "debug/macho"
+)
+
+func readDWARF(exePath string) (*dwarf.Data, error) {
+ machoFile, err := macho.Open(exePath)
+ if err != nil {
+ return nil, err
+ }
+ defer machoFile.Close()
+ return machoFile.DWARF()
+}
+-- read_elf.go --
+// +build android dragonfly freebsd illumos linux netbsd openbsd solaris
+
+package main
+
+import (
+ "debug/dwarf"
+ "debug/elf"
+)
+
+func readDWARF(exePath string) (*dwarf.Data, error) {
+ elfFile, err := elf.Open(exePath)
+ if err != nil {
+ return nil, err
+ }
+ defer elfFile.Close()
+ return elfFile.DWARF()
+}
+-- read_windows.go --
+package main
+
+import (
+ "debug/dwarf"
+ "debug/pe"
+)
+
+func readDWARF(exePath string) (*dwarf.Data, error) {
+ peFile, err := pe.Open(exePath)
+ if err != nil {
+ return nil, err
+ }
+ defer peFile.Close()
+ return peFile.DWARF()
+}
diff --git a/src/cmd/go/testdata/script/build_negative_p.txt b/src/cmd/go/testdata/script/build_negative_p.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9123907dc87a40e41803c145433602aada4db3dd
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_negative_p.txt
@@ -0,0 +1,5 @@
+! go build -p=-1 example.go
+stderr 'go: -p must be a positive integer: -1'
+
+-- example.go --
+package example
\ No newline at end of file
diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt
index 2932b94e6c42c70cdec8884c1e36dc281e063369..56e812f44b29e7eb943f4302a17a4aeb825582bb 100644
--- a/src/cmd/go/testdata/script/build_overlay.txt
+++ b/src/cmd/go/testdata/script/build_overlay.txt
@@ -31,17 +31,17 @@ exec ./print_trimpath_two_files$GOEXE
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go
-go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace
-exec ./main_cgo_replace$GOEXE
-stdout '^hello cgo\r?\n'
+[cgo] go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace
+[cgo] exec ./main_cgo_replace$GOEXE
+[cgo] stdout '^hello cgo\r?\n'
-go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote
-exec ./main_cgo_quote$GOEXE
-stdout '^hello cgo\r?\n'
+[cgo] go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote
+[cgo] exec ./main_cgo_quote$GOEXE
+[cgo] stdout '^hello cgo\r?\n'
-go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle
-exec ./main_cgo_angle$GOEXE
-stdout '^hello cgo\r?\n'
+[cgo] go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle
+[cgo] exec ./main_cgo_angle$GOEXE
+[cgo] stdout '^hello cgo\r?\n'
go build -overlay overlay.json -o main_call_asm$GOEXE ./call_asm
exec ./main_call_asm$GOEXE
@@ -55,11 +55,11 @@ cp overlay/test_cache_different.go overlay/test_cache.go
go list -overlay overlay.json -f '{{.Stale}}' ./test_cache
stdout '^true$'
-go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace
-cp stdout compiled_cgo_sources.txt
-go run ../print_line_comments.go compiled_cgo_sources.txt
-stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go
-! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c
+[cgo] go list -compiled -overlay overlay.json -f '{{range .CompiledGoFiles}}{{. | printf "%s\n"}}{{end}}' ./cgo_hello_replace
+[cgo] cp stdout compiled_cgo_sources.txt
+[cgo] go run ../print_line_comments.go compiled_cgo_sources.txt
+[cgo] stdout $GOPATH[/\\]src[/\\]m[/\\]cgo_hello_replace[/\\]cgo_hello_replace.go
+[cgo] ! stdout $GOPATH[/\\]src[/\\]m[/\\]overlay[/\\]hello.c
# Run same tests but with gccgo.
env GO111MODULE=off
diff --git a/src/cmd/go/testdata/script/build_runtime_gcflags.txt b/src/cmd/go/testdata/script/build_runtime_gcflags.txt
index da1b65f06c849788fbf7b7b73a50ee0f40979892..c87e480911933a6211fd31991cbf17ec2e75da44 100644
--- a/src/cmd/go/testdata/script/build_runtime_gcflags.txt
+++ b/src/cmd/go/testdata/script/build_runtime_gcflags.txt
@@ -8,4 +8,4 @@ mkdir $GOCACHE
# Verify the standard library (specifically runtime/internal/atomic) can be
# built with -gcflags when -n is given. See golang.org/issue/29346.
go build -n -gcflags=all='-l' std
-stderr 'compile.* -l .* runtime/internal/atomic'
+stderr 'compile.* runtime/internal/atomic .* -l'
diff --git a/src/cmd/go/testdata/script/build_single_error.txt b/src/cmd/go/testdata/script/build_single_error.txt
new file mode 100644
index 0000000000000000000000000000000000000000..241cdb954ba7664ce796ea16c761d23a59dfe45c
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_single_error.txt
@@ -0,0 +1,18 @@
+# go test ./... with a bad package should report the error once (#44624).
+! go test ./...
+stderr -count=1 undefined
+
+-- go.mod --
+module example.com
+
+go 1.18
+-- a/a.go --
+package a
+
+import "example.com/b"
+-- b/b.go --
+package b
+
+var X = Y
+-- b/b_test.go --
+package b
diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt
index 2c3bee8fdc7a925bb9bd9bdcee078a36b0ed9581..f36b1237dc09413f8cf95fea76a278ed48b187f6 100644
--- a/src/cmd/go/testdata/script/build_trimpath.txt
+++ b/src/cmd/go/testdata/script/build_trimpath.txt
@@ -32,7 +32,8 @@ stdout 'binary contains GOROOT: false'
# A binary from an external module built with -trimpath should not contain
# the current workspace or GOROOT.
-go get -trimpath rsc.io/fortune
+go get rsc.io/fortune
+go install -trimpath rsc.io/fortune
exec $WORK/paths-a.exe $GOPATH/bin/fortune$GOEXE
stdout 'binary contains module root: false'
stdout 'binary contains GOROOT: false'
diff --git a/src/cmd/go/testdata/script/cgo_path_space_quote.txt b/src/cmd/go/testdata/script/cgo_path_space_quote.txt
new file mode 100644
index 0000000000000000000000000000000000000000..955610130088d512759b29d86d0dba92a0875ef2
--- /dev/null
+++ b/src/cmd/go/testdata/script/cgo_path_space_quote.txt
@@ -0,0 +1,58 @@
+# This test checks that the CC environment variable may contain quotes and
+# spaces. Arguments are normally split on spaces, tabs, newlines. If an
+# argument contains these characters, the entire argument may be quoted
+# with single or double quotes. This is the same as -gcflags and similar
+# options.
+
+[short] skip
+[!exec:clang] [!exec:gcc] skip
+[!cgo] skip
+
+env GOENV=$WORK/go.env
+mkdir 'program files'
+go build -o 'program files' './which cc/which cc.go'
+[exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'clang
+[!exec:clang] env CC='"'$PWD${/}program' 'files${/}which' 'cc"' 'gcc
+go env CC
+stdout 'program files[/\\]which cc" (clang|gcc)$'
+go env -w CC=$CC
+env CC=
+go env CC
+stdout 'program files[/\\]which cc" (clang|gcc)$'
+
+go run .
+stdout 1
+
+-- go.mod --
+module test
+
+go 1.17
+-- which cc/which cc.go --
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+)
+
+func main() {
+ args := append([]string{"-DWRAPPER_WAS_USED=1"}, os.Args[2:]...)
+ cmd := exec.Command(os.Args[1], args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+-- hello.go --
+package main
+
+// int x = WRAPPER_WAS_USED;
+import "C"
+import "fmt"
+
+func main() {
+ fmt.Println(C.x)
+}
diff --git a/src/cmd/go/testdata/script/embed.txt b/src/cmd/go/testdata/script/embed.txt
index 04b17cd62b385999188cfa7e6795496bbddd0c50..5f7f6edd77e7f9a4d8fee9c8b26ee278a4d04e8a 100644
--- a/src/cmd/go/testdata/script/embed.txt
+++ b/src/cmd/go/testdata/script/embed.txt
@@ -60,6 +60,18 @@ rm t/x.txt
! go build m/use
stderr '^x.go:5:12: pattern [*]t: cannot embed directory t: contains no embeddable files$'
+# all still ignores .git and symlinks
+cp x.go3 x.go
+! go build -x
+stderr '^x.go:5:12: pattern all:t: cannot embed directory t: contains no embeddable files$'
+
+# all finds dot files and underscore files
+cp x.txt t/.x.txt
+go build -x
+rm t/.x.txt
+cp x.txt t/_x.txt
+go build -x
+
-- x.go --
package p
@@ -92,6 +104,14 @@ import "embed"
//go:embed *t
var X embed.FS
+-- x.go3 --
+package p
+
+import "embed"
+
+//go:embed all:t
+var X embed.FS
+
-- x.txt --
hello
diff --git a/src/cmd/go/testdata/script/env_unset.txt b/src/cmd/go/testdata/script/env_unset.txt
index 4e0f249509873ed7355470f5bd0a1326defbb1a0..22bc845c37bf4ecc7cde8b989b1d899e7a01e522 100644
--- a/src/cmd/go/testdata/script/env_unset.txt
+++ b/src/cmd/go/testdata/script/env_unset.txt
@@ -12,13 +12,13 @@ stderr '^go(\.exe)?: unknown GOEXPERIMENT badexp$'
go env -u GOEXPERIMENT
! go env
-stderr '^cmd/go: unsupported GOOS/GOARCH pair bados/badarch$'
+stderr '^go: unsupported GOOS/GOARCH pair bados/badarch$'
! go env -u GOOS
-stderr '^go env -u: unsupported GOOS/GOARCH pair \w+/badarch$'
+stderr '^go: unsupported GOOS/GOARCH pair \w+/badarch$'
! go env -u GOARCH
-stderr '^go env -u: unsupported GOOS/GOARCH pair bados/\w+$'
+stderr '^go: unsupported GOOS/GOARCH pair bados/\w+$'
go env -u GOOS GOARCH
diff --git a/src/cmd/go/testdata/script/env_write.txt b/src/cmd/go/testdata/script/env_write.txt
index b5e97391679cc52442322e972c7a82fbed0e30bc..132947c623ff2f0472bad5ac22b9baf99d99cce5 100644
--- a/src/cmd/go/testdata/script/env_write.txt
+++ b/src/cmd/go/testdata/script/env_write.txt
@@ -30,9 +30,9 @@ go env
# checking errors
! go env -w
-stderr 'go env -w: no KEY=VALUE arguments given'
+stderr 'go: no KEY=VALUE arguments given'
! go env -u
-stderr 'go env -u: no arguments given'
+stderr 'go: ''go env -u'' requires an argument'
# go env -w changes default setting
env root=
@@ -111,7 +111,7 @@ stderr 'GOPATH entry is relative; must be absolute path'
# go env -w rejects invalid GOTMPDIR values
! go env -w GOTMPDIR=x
-stderr 'go env -w: GOTMPDIR must be an absolute path'
+stderr 'go: GOTMPDIR must be an absolute path'
# go env -w should accept absolute GOTMPDIR value
# and should not create it
@@ -134,24 +134,24 @@ stdout ^$
go env -w CC=clang
[!windows] ! go env -w CC=./clang
[!windows] ! go env -w CC=bin/clang
-[!windows] stderr 'go env -w: CC entry is relative; must be absolute path'
+[!windows] stderr 'go: CC entry is relative; must be absolute path'
[windows] go env -w CC=$WORK\bin\clang
[windows] ! go env -w CC=.\clang
[windows] ! go env -w CC=bin\clang
-[windows] stderr 'go env -w: CC entry is relative; must be absolute path'
+[windows] stderr 'go: CC entry is relative; must be absolute path'
# go env -w rejects relative CXX values
[!windows] go env -w CC=/usr/bin/cpp
go env -w CXX=cpp
[!windows] ! go env -w CXX=./cpp
[!windows] ! go env -w CXX=bin/cpp
-[!windows] stderr 'go env -w: CXX entry is relative; must be absolute path'
+[!windows] stderr 'go: CXX entry is relative; must be absolute path'
[windows] go env -w CXX=$WORK\bin\cpp
[windows] ! go env -w CXX=.\cpp
[windows] ! go env -w CXX=bin\cpp
-[windows] stderr 'go env -w: CXX entry is relative; must be absolute path'
+[windows] stderr 'go: CXX entry is relative; must be absolute path'
# go env -w/-u checks validity of GOOS/ARCH combinations
env GOOS=
@@ -176,9 +176,9 @@ stderr 'unsupported GOOS/GOARCH.*windows/mips$'
# go env -w should reject relative paths in GOMODCACHE environment.
! go env -w GOMODCACHE=~/test
-stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "~/test"'
+stderr 'go: GOMODCACHE entry is relative; must be absolute path: "~/test"'
! go env -w GOMODCACHE=./test
-stderr 'go env -w: GOMODCACHE entry is relative; must be absolute path: "./test"'
+stderr 'go: GOMODCACHE entry is relative; must be absolute path: "./test"'
# go env -w checks validity of GOEXPERIMENT
env GOEXPERIMENT=
diff --git a/src/cmd/go/testdata/script/gcflags_patterns.txt b/src/cmd/go/testdata/script/gcflags_patterns.txt
index f23cecefd3aa30e87ef45d26b15949c2f88d3f28..24ec5aa11b01deaa9d6b6075a3f700623e995c9e 100644
--- a/src/cmd/go/testdata/script/gcflags_patterns.txt
+++ b/src/cmd/go/testdata/script/gcflags_patterns.txt
@@ -7,28 +7,28 @@ 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.* -e.* -p z1'
-stderr 'compile.* -e.* -p z2'
+stderr 'compile.* -p z1.* -e '
+stderr 'compile.* -p z2.* -e '
stderr 'compile.* -p y'
-! stderr 'compile.* -e.* -p [^z]'
+! 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.* -N.* -p z1'
-! stderr 'compile.* -e.* -p z1'
-! stderr 'compile.* -N.* -p z2'
-stderr 'compile.* -e.* -p z2'
+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.* -e.* -p [^z]'
-! stderr 'compile.* -N.* -p [^z]'
+! 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.* -e.* -p z1'
+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
-stderr 'compile.* -e.* -p z3 '
+stderr 'compile.* -p z3.* -e '
# this particular -gcflags argument made the compiler crash
! go build -gcflags=-d=ssa/ z1
@@ -58,8 +58,7 @@ go build -n -ldflags=-X=math.pi=3
stderr 'link.* -X=math.pi=3'
# -ldflags applies to current directory even if GOPATH is funny
-[windows] cd $WORK/GoPath/src/my/cmd/prog
-[darwin] cd $WORK/GoPath/src/my/cmd/prog
+[!case-sensitive] cd $WORK/GoPath/src/my/cmd/prog
go build -n -ldflags=-X=math.pi=3
stderr 'link.* -X=math.pi=3'
diff --git a/src/cmd/go/testdata/script/get_404_meta.txt b/src/cmd/go/testdata/script/get_404_meta.txt
index ec4f8d32432179bc97c5fe9fccb56d1527c664e3..29fc5421e12fb9824c62dcec8d4f0bc526d16cf4 100644
--- a/src/cmd/go/testdata/script/get_404_meta.txt
+++ b/src/cmd/go/testdata/script/get_404_meta.txt
@@ -9,4 +9,10 @@ go get -d bazil.org/fuse/fs/fstestutil
env GO111MODULE=on
env GOPROXY=direct
-go get -d bazil.org/fuse/fs/fstestutil
+go get bazil.org/fuse/fs/fstestutil
+
+
+-- go.mod --
+module m
+
+go 1.18
diff --git a/src/cmd/go/testdata/script/get_go_file.txt b/src/cmd/go/testdata/script/get_go_file.txt
index bed872098769249e851a30ac13a4957c83aed913..f6d0de4d06fe540d92c580fbec4c4f9a9743dfb7 100644
--- a/src/cmd/go/testdata/script/get_go_file.txt
+++ b/src/cmd/go/testdata/script/get_go_file.txt
@@ -9,15 +9,15 @@ go get -d test
# argument has .go suffix, is a file and exists
! go get -d test.go
-stderr 'go get test.go: arguments must be package or module paths'
+stderr 'go: test.go: arguments must be package or module paths'
# argument has .go suffix, doesn't exist and has no slashes
! go get -d test_missing.go
-stderr 'go get test_missing.go: arguments must be package or module paths'
+stderr 'go: test_missing.go: arguments must be package or module paths'
# argument has .go suffix, is a file and exists in sub-directory
! go get -d test/test.go
-stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments'
+stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments'
# argument has .go suffix, doesn't exist and has slashes
! go get -d test/test_missing.go
@@ -27,19 +27,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a
# argument has .go suffix, is a symlink and exists
[symlink] symlink test_sym.go -> test.go
[symlink] ! go get -d test_sym.go
-[symlink] stderr 'go get test_sym.go: arguments must be package or module paths'
+[symlink] stderr 'go: test_sym.go: arguments must be package or module paths'
[symlink] rm test_sym.go
# argument has .go suffix, is a symlink and exists in sub-directory
[symlink] symlink test/test_sym.go -> test.go
[symlink] ! go get -d test/test_sym.go
-[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
+[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
[symlink] rm test_sym.go
# argument has .go suffix, is a directory and exists
mkdir test_dir.go
! go get -d test_dir.go
-stderr 'go get test_dir.go: arguments must be package or module paths'
+stderr 'go: test_dir.go: arguments must be package or module paths'
rm test_dir.go
# argument has .go suffix, is a directory and exists in sub-directory
diff --git a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
index 2517664dd02308901fe338e5daac43a73c16b306..00bf32fc78effbb635570ffa919e96af69e561ee 100644
--- a/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
+++ b/src/cmd/go/testdata/script/get_insecure_no_longer_supported.txt
@@ -3,11 +3,11 @@ env GO111MODULE=off
# GOPATH: Fetch with insecure, should error
! go get -insecure test
-stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead'
+stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead'
# Modules: Set up
env GO111MODULE=on
# Modules: Fetch with insecure, should error
! go get -insecure test
-stderr 'go get: -insecure flag is no longer supported; use GOINSECURE instead'
+stderr 'go: -insecure flag is no longer supported; use GOINSECURE instead'
diff --git a/src/cmd/go/testdata/script/go_version.txt b/src/cmd/go/testdata/script/go_version.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1a787e1b18c1dd9265e6f2ed29f0871ce98f26d8
--- /dev/null
+++ b/src/cmd/go/testdata/script/go_version.txt
@@ -0,0 +1,9 @@
+# test that go version doesn't panic on non-go binaries
+# See Issue #49181
+
+[exec:/bin/true] cp /bin/true true
+[exec:C:\windows\system32\help.exe] cp C:\windows\system32\help.exe help.exe
+
+go version -m .
+! stdout .
+! stderr .
diff --git a/src/cmd/go/testdata/script/goflags.txt b/src/cmd/go/testdata/script/goflags.txt
index 686d1138b8318e3d0ed5affd6673ee66ead24c96..f4872ffd356b5b3874e1205c0959c20c79a38ca0 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/gopath_install.txt b/src/cmd/go/testdata/script/gopath_install.txt
index 4b42fc593f9ddf4b9927d1bcadfab461ef0b74e5..6c572eae619f067eac3ec44681314e1706a851f9 100644
--- a/src/cmd/go/testdata/script/gopath_install.txt
+++ b/src/cmd/go/testdata/script/gopath_install.txt
@@ -26,7 +26,7 @@ cd ..
env GOPATH= # reset to default ($HOME/go, which does not exist)
env GOBIN=
! go install go-cmd-test/helloworld.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
# With $GOBIN set, should install there.
env GOBIN=$WORK/bin1
diff --git a/src/cmd/go/testdata/script/gopath_local.txt b/src/cmd/go/testdata/script/gopath_local.txt
index 7ee1f83471c631f4b5f41bab1105bdefa76c5973..54beaca5e8a9c211db89a82d3d729a673880c1dc 100644
--- a/src/cmd/go/testdata/script/gopath_local.txt
+++ b/src/cmd/go/testdata/script/gopath_local.txt
@@ -22,7 +22,7 @@ stdout '^sub\.Hello'
# Explicit source files listed on the command line should not install without
# GOBIN set, since individual source files aren't part of the containing GOPATH.
! go install testdata/local/easy.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
[windows] stop # Windows does not allow the ridiculous directory name we're about to use.
@@ -58,7 +58,7 @@ stdout '^sub\.Hello'
# Explicit source files listed on the command line should not install without
# GOBIN set, since individual source files aren't part of the containing GOPATH.
! go install testdata/$BAD_DIR_NAME/easy.go
-stderr '^go install: no install location for \.go files listed on command line \(GOBIN not set\)$'
+stderr '^go: no install location for \.go files listed on command line \(GOBIN not set\)$'
-- testdata/local/easy.go --
package main
diff --git a/src/cmd/go/testdata/script/govcs.txt b/src/cmd/go/testdata/script/govcs.txt
index 4180d7da6ab159addd14d344d7d5135e07d50f9d..46f1bd0da2ada67d7cb80664699f6ac3d7e3881c 100644
--- a/src/cmd/go/testdata/script/govcs.txt
+++ b/src/cmd/go/testdata/script/govcs.txt
@@ -5,64 +5,64 @@ env GOPROXY=direct
# GOVCS stops go get
env GOVCS='*:none'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
env GOPRIVATE='github.com/google'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
# public pattern works
env GOPRIVATE='github.com/google'
env GOVCS='public:all,private:none'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for private github.com/google/go-cmp; see ''go help vcs''$'
# private pattern works
env GOPRIVATE='hubgit.com/google'
env GOVCS='private:all,public:none'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
# other patterns work (for more patterns, see TestGOVCS)
env GOPRIVATE=
env GOVCS='github.com:svn|hg'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
env GOVCS='github.com/google/go-cmp/inner:git,github.com:svn|hg'
! go get github.com/google/go-cmp
-stderr '^go get: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
+stderr '^go: GOVCS disallows using git for public github.com/google/go-cmp; see ''go help vcs''$'
# bad patterns are reported (for more bad patterns, see TestGOVCSErrors)
env GOVCS='git'
! go get github.com/google/go-cmp
-stderr '^go get github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$'
+stderr '^go: github.com/google/go-cmp: malformed entry in GOVCS \(missing colon\): "git"$'
env GOVCS=github.com:hg,github.com:git
! go get github.com/google/go-cmp
-stderr '^go get github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$'
+stderr '^go: github.com/google/go-cmp: unreachable pattern in GOVCS: "github.com:git" after "github.com:hg"$'
# bad GOVCS patterns do not stop commands that do not need to check VCS
go list
env GOPROXY=$proxy
-go get -d rsc.io/quote # ok because used proxy
+go get rsc.io/quote # ok because used proxy
env GOPROXY=direct
# svn is disallowed by default
env GOPRIVATE=
env GOVCS=
! go get rsc.io/nonexist.svn/hello
-stderr '^go get rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.svn/hello: GOVCS disallows using svn for public rsc.io/nonexist.svn; see ''go help vcs''$'
# fossil is disallowed by default
env GOPRIVATE=
env GOVCS=
! go get rsc.io/nonexist.fossil/hello
-stderr '^go get rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.fossil/hello: GOVCS disallows using fossil for public rsc.io/nonexist.fossil; see ''go help vcs''$'
# bzr is disallowed by default
env GOPRIVATE=
env GOVCS=
! go get rsc.io/nonexist.bzr/hello
-stderr '^go get rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.bzr/hello: GOVCS disallows using bzr for public rsc.io/nonexist.bzr; see ''go help vcs''$'
# git is OK by default
env GOVCS=
@@ -77,12 +77,12 @@ env GONOSUMDB='*'
# git can be disallowed
env GOVCS=public:hg
! go get rsc.io/nonexist.git/hello
-stderr '^go get rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.git/hello: GOVCS disallows using git for public rsc.io/nonexist.git; see ''go help vcs''$'
# hg can be disallowed
env GOVCS=public:git
! go get rsc.io/nonexist.hg/hello
-stderr '^go get rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$'
+stderr '^go: rsc.io/nonexist.hg/hello: GOVCS disallows using hg for public rsc.io/nonexist.hg; see ''go help vcs''$'
# Repeat in GOPATH mode. Error texts slightly different.
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 7985cd2ab2728dfbb7311e6e96069b62f4b86792..5e88f7b8dbdc7288c3bdc6d8f7c4774b7e7fa1a0 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/link_external_undef.txt b/src/cmd/go/testdata/script/link_external_undef.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d86b3a374e919d744f04227e6ec47e9744d06013
--- /dev/null
+++ b/src/cmd/go/testdata/script/link_external_undef.txt
@@ -0,0 +1,48 @@
+
+# Test case for issue 47993, in which the linker crashes
+# on a bad input instead of issuing an error and exiting.
+
+# This test requires external linking, so use cgo as a proxy
+[!cgo] skip
+
+! go build -ldflags='-linkmode=external' .
+! stderr 'panic'
+stderr '^.*unreachable sym in relocation.*'
+
+-- go.mod --
+
+module issue47993
+
+go 1.16
+
+-- main.go --
+
+package main
+
+type M struct {
+ b bool
+}
+
+// Note the body-less func def here. This is what causes the problems.
+func (m *M) run(fp func())
+
+func doit(m *M) {
+ InAsm()
+ m.run(func() {
+ })
+}
+
+func main() {
+ m := &M{true}
+ doit(m)
+}
+
+func InAsm()
+
+-- main.s --
+
+// Add an assembly function so as to leave open the possibility
+// that body-less functions in Go might be defined in assembly.
+
+// Currently we just need an empty file here.
+
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 0000000000000000000000000000000000000000..e0a47398bbd92f25d3561239eedd330e8067b299
--- /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 0000000000000000000000000000000000000000..b9c5361492be9cfce28f3d94c204cc11be2cdcb9
--- /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_shadow.txt b/src/cmd/go/testdata/script/list_shadow.txt
index 7b24d9367aede1c6e3c32c38fda4b93c571b90dc..660508de9ff83c1b75b8814682823d866109ca9b 100644
--- a/src/cmd/go/testdata/script/list_shadow.txt
+++ b/src/cmd/go/testdata/script/list_shadow.txt
@@ -15,7 +15,7 @@ stdout '^\(.*gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo\) \('$WORK
# The error for go install should mention the conflicting directory.
! go install -n ./shadow/root2/src/foo
-stderr 'go install: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo'
+stderr 'go: no install location for '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root2(\\|/)src(\\|/)foo: hidden by '$WORK'(\\|/)?gopath(\\|/)src(\\|/)shadow(\\|/)root1(\\|/)src(\\|/)foo'
-- shadow/root1/src/foo/foo.go --
package foo
diff --git a/src/cmd/go/testdata/script/list_swigcxx.txt b/src/cmd/go/testdata/script/list_swigcxx.txt
index c6acd9ecdbabf4b467c37fd262344be45dac8fec..d4227a80e8e8834d3af5608c3ee94f7e731a490c 100644
--- a/src/cmd/go/testdata/script/list_swigcxx.txt
+++ b/src/cmd/go/testdata/script/list_swigcxx.txt
@@ -2,17 +2,19 @@
[!exec:swig] skip
[!exec:g++] skip
+[!cgo] skip
# CompiledGoFiles should contain 4 files:
# a.go
# a.swigcxx.go
# _cgo_gotypes.go
# a.cgo1.go
+#
+# These names we see here, other than a.go, will be from the build cache,
+# so we just count them.
go list -f '{{.CompiledGoFiles}}' -compiled=true example/swig
-# These names we see here, other than a.go, will be from the build cache,
-# so we just count them.
stdout a\.go
stdout -count=3 $GOCACHE
diff --git a/src/cmd/go/testdata/script/list_test_err.txt b/src/cmd/go/testdata/script/list_test_err.txt
index c6f1ecf40039097acadbde2977f817bf52f17c74..25dbb969b01c06a04d1869879168f4a52422923e 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_all.txt b/src/cmd/go/testdata/script/mod_all.txt
index 090eeee22df263579b99075bb227f5f32372e0dd..b71a920870a9bb7ffd2ad3668e10af24eb98991c 100644
--- a/src/cmd/go/testdata/script/mod_all.txt
+++ b/src/cmd/go/testdata/script/mod_all.txt
@@ -202,9 +202,9 @@ go mod edit -go=1.17 u/go.mod
go mod edit -go=1.17 w/go.mod
go mod edit -go=1.17 x/go.mod
go mod edit -go=1.17
-cp go.mod go.mod.orig
+cmp go.mod go.mod.beforetidy
go mod tidy
-cmp go.mod go.mod.orig
+cmp go.mod go.mod.aftertidy
# With lazy loading, 'go list all' with neither -mod=vendor nor -test should
# match -mod=vendor without -test in 1.15.
@@ -315,7 +315,7 @@ go 1.15
require (
example.com/a v0.1.0
- example.com/b v0.1.0
+ example.com/b v0.1.0 // indirect
example.com/q v0.1.0
example.com/r v0.1.0 // indirect
example.com/t v0.1.0
@@ -466,3 +466,66 @@ module example.com/x
go 1.15
-- x/x.go --
package x
+-- go.mod.beforetidy --
+module example.com/main
+
+// Note: this go.mod file initially specifies go 1.15,
+// but includes some redundant roots so that it
+// also already obeys the 1.17 lazy loading invariants.
+go 1.17
+
+require (
+ example.com/a v0.1.0
+ example.com/b v0.1.0 // indirect
+ example.com/q v0.1.0
+ example.com/r v0.1.0 // indirect
+ example.com/t v0.1.0
+ example.com/u v0.1.0 // indirect
+)
+
+replace (
+ example.com/a v0.1.0 => ./a
+ example.com/b v0.1.0 => ./b
+ example.com/c v0.1.0 => ./c
+ example.com/d v0.1.0 => ./d
+ example.com/q v0.1.0 => ./q
+ example.com/r v0.1.0 => ./r
+ example.com/s v0.1.0 => ./s
+ example.com/t v0.1.0 => ./t
+ example.com/u v0.1.0 => ./u
+ example.com/w v0.1.0 => ./w
+ example.com/x v0.1.0 => ./x
+)
+-- go.mod.aftertidy --
+module example.com/main
+
+// Note: this go.mod file initially specifies go 1.15,
+// but includes some redundant roots so that it
+// also already obeys the 1.17 lazy loading invariants.
+go 1.17
+
+require (
+ example.com/a v0.1.0
+ example.com/q v0.1.0
+ example.com/t v0.1.0
+)
+
+require (
+ example.com/b v0.1.0 // indirect
+ example.com/r v0.1.0 // indirect
+ example.com/u v0.1.0 // indirect
+)
+
+replace (
+ example.com/a v0.1.0 => ./a
+ example.com/b v0.1.0 => ./b
+ example.com/c v0.1.0 => ./c
+ example.com/d v0.1.0 => ./d
+ example.com/q v0.1.0 => ./q
+ example.com/r v0.1.0 => ./r
+ example.com/s v0.1.0 => ./s
+ example.com/t v0.1.0 => ./t
+ example.com/u v0.1.0 => ./u
+ example.com/w v0.1.0 => ./w
+ example.com/x v0.1.0 => ./x
+)
diff --git a/src/cmd/go/testdata/script/mod_bad_domain.txt b/src/cmd/go/testdata/script/mod_bad_domain.txt
index 7a270d0f07cac7fa76a670037dbb6678536db7d6..afd6e5186c152ac7f9ad6ba6547c7d9bafa10064 100644
--- a/src/cmd/go/testdata/script/mod_bad_domain.txt
+++ b/src/cmd/go/testdata/script/mod_bad_domain.txt
@@ -2,7 +2,7 @@ env GO111MODULE=on
# explicit get should report errors about bad names
! go get appengine
-stderr '^go get: malformed module path "appengine": missing dot in first path element$'
+stderr '^go: malformed module path "appengine": missing dot in first path element$'
! go get x/y.z
stderr 'malformed module path "x/y.z": missing dot in first path element'
@@ -24,10 +24,10 @@ stderr '^usenonexistent[/\\]x.go:2:8: no required module provides package nonexi
# 'get -d' should be similarly definitive
-go get -d ./useappengine # TODO(#41315): This should fail.
+go get ./useappengine # TODO(#41315): This should fail.
# stderr '^useappengine[/\\]x.go:2:8: cannot find package$'
-! go get -d ./usenonexistent
+! go get ./usenonexistent
stderr '^x/usenonexistent imports\n\tnonexistent.rsc.io: cannot find module providing package nonexistent.rsc.io$'
diff --git a/src/cmd/go/testdata/script/mod_build_info_err.txt b/src/cmd/go/testdata/script/mod_build_info_err.txt
index cee055eabe9c26d0c1d6c4ca79d3b2121eb29956..5c3c309b0dc86e40394e88625e9fc3520ba7f263 100644
--- a/src/cmd/go/testdata/script/mod_build_info_err.txt
+++ b/src/cmd/go/testdata/script/mod_build_info_err.txt
@@ -11,7 +11,7 @@ stderr '^bad[/\\]bad.go:3:8: malformed import path "🐧.example.com/string": in
# TODO(#41688): This should include a file and line, and report the reason for the error..
# (Today it includes only an import stack.)
-! go get -d ./main
+! go get ./main
stderr '^m/main imports\n\tm/bad imports\n\t🐧.example.com/string: malformed import path "🐧.example.com/string": invalid char ''🐧''$'
diff --git a/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt b/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt
new file mode 100644
index 0000000000000000000000000000000000000000..859eafcf84dff71d478735102d6777d09ec54f85
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_build_trimpath_issue48557.txt
@@ -0,0 +1,52 @@
+# Regression test for issue #48557.
+# Since builds in module mode do not support relative imports at all, the build
+# ID for (and other contents of) a binary built with -trimpath in module mode
+# should not depend on its working directory, even if the binary is specified as
+# a list of relative source files.
+
+[short] skip # links and runs binaries
+
+env GOFLAGS=-trimpath
+env GOCACHE=$WORK/gocache
+
+
+# When we build a binary in module mode with -trimpath, the -D flag (for the
+# "local import prefix") should not be passed to it.
+
+cd $WORK/tmp/foo
+go build -x -o a.exe main.go
+stderr ${/}compile$GOEXE.*' -nolocalimports'
+! stderr ${/}compile$GOEXE.*' -D[ =]'
+
+go tool buildid a.exe
+cp stdout ../foo-buildid.txt
+go version a.exe
+cp stdout ../foo-version.txt
+cd ..
+
+
+# On the second build — in a different directory but with -trimpath — the
+# compiler should not be invoked, since the cache key should be identical.
+# Only the linker and buildid tool should be needed.
+
+mkdir bar
+cp foo/main.go bar/main.go
+cd bar
+go build -x -o a.exe main.go
+! stderr ${/}compile$GOEXE
+
+go tool buildid a.exe
+cp stdout ../bar-buildid.txt
+go version a.exe
+cp stdout ../bar-version.txt
+cd ..
+
+cmp bar-buildid.txt foo-buildid.txt
+cmp bar-version.txt foo-version.txt
+cmp bar/a.exe foo/a.exe
+
+
+-- $WORK/tmp/foo/main.go --
+package main
+
+func main() {}
diff --git a/src/cmd/go/testdata/script/mod_build_versioned.txt b/src/cmd/go/testdata/script/mod_build_versioned.txt
index d1d74de10c22b1b75cd79a237b8e6c3b36db519c..f55041834aefe874bb195b4ce77999545846a668 100644
--- a/src/cmd/go/testdata/script/mod_build_versioned.txt
+++ b/src/cmd/go/testdata/script/mod_build_versioned.txt
@@ -1,7 +1,7 @@
env GO111MODULE=on
[short] skip
-go get -d rsc.io/fortune/v2
+go get rsc.io/fortune/v2
# The default executable name shouldn't be v2$GOEXE
go build rsc.io/fortune/v2
diff --git a/src/cmd/go/testdata/script/mod_cache_dir.txt b/src/cmd/go/testdata/script/mod_cache_dir.txt
index 7284ccf8bab735aa5613f08608228f41ec778c9f..4045928a9781b7e5cbb826508be511f551f395b3 100644
--- a/src/cmd/go/testdata/script/mod_cache_dir.txt
+++ b/src/cmd/go/testdata/script/mod_cache_dir.txt
@@ -3,9 +3,9 @@ env GO111MODULE=on
# Go should reject relative paths in GOMODCACHE environment.
env GOMODCACHE="~/test"
-! go get example.com/tools/cmd/hello
+! go install example.com/tools/cmd/hello@latest
stderr 'must be absolute path'
env GOMODCACHE="./test"
-! go get example.com/tools/cmd/hello
+! go install example.com/tools/cmd/hello@latest
stderr 'must be absolute path'
diff --git a/src/cmd/go/testdata/script/mod_cache_rw.txt b/src/cmd/go/testdata/script/mod_cache_rw.txt
index a5410764bc00f86469f13173f0bf465d87be9cd4..07755415d608a46748b658ae808f67e575ec5d9b 100644
--- a/src/cmd/go/testdata/script/mod_cache_rw.txt
+++ b/src/cmd/go/testdata/script/mod_cache_rw.txt
@@ -5,7 +5,7 @@ env GO111MODULE=on
# golang.org/issue/31481: an explicit flag should make directories in the module
# cache writable in order to work around the historical inability of 'rm -rf' to
# forcibly remove files in unwritable directories.
-go get -modcacherw -d rsc.io/quote@v1.5.2
+go get -modcacherw rsc.io/quote@v1.5.2
cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
# After adding an extraneous file, 'go mod verify' should fail.
@@ -28,7 +28,7 @@ cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
# Windows does not respect FILE_ATTRIBUTE_READONLY on directories, according
# to MSDN, so there we disable testing whether the directory itself is
# unwritable.
-go get -d rsc.io/quote@latest
+go get rsc.io/quote@latest
[!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
[!windows] [!root] ! cp $WORK/extraneous.txt $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
! exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/extraneous_file.go
diff --git a/src/cmd/go/testdata/script/mod_case.txt b/src/cmd/go/testdata/script/mod_case.txt
index 4a4698600f7fb965162272a5eb7df75919712bc0..d3fb11dfa332168acb0ffa4188800d4bbb4a7d60 100644
--- a/src/cmd/go/testdata/script/mod_case.txt
+++ b/src/cmd/go/testdata/script/mod_case.txt
@@ -1,6 +1,6 @@
env GO111MODULE=on
-go get -d
+go get
go list -m all
stdout '^rsc.io/quote v1.5.2'
stdout '^rsc.io/QUOTE v1.5.2'
@@ -9,7 +9,7 @@ go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE
stdout 'DEPS.*rsc.io/quote'
stdout 'DIR.*!q!u!o!t!e'
-go get -d rsc.io/QUOTE@v1.5.3-PRE
+go get rsc.io/QUOTE@v1.5.3-PRE
go list -m all
stdout '^rsc.io/QUOTE v1.5.3-PRE'
diff --git a/src/cmd/go/testdata/script/mod_case_cgo.txt b/src/cmd/go/testdata/script/mod_case_cgo.txt
index f3d6aaa5abc77bc02d61faed05e242de12cb5476..7c768a096395d93ec984a73eaf712c0ba389ba78 100644
--- a/src/cmd/go/testdata/script/mod_case_cgo.txt
+++ b/src/cmd/go/testdata/script/mod_case_cgo.txt
@@ -2,7 +2,7 @@
env GO111MODULE=on
-go get -d rsc.io/CGO
+go get rsc.io/CGO
[short] stop
go build rsc.io/CGO
diff --git a/src/cmd/go/testdata/script/mod_concurrent.txt b/src/cmd/go/testdata/script/mod_concurrent.txt
index 8c215251587180600da62bdbde2a77b61f5beed4..e2224d659b23a53dc28092a3eb153a5b0648a655 100644
--- a/src/cmd/go/testdata/script/mod_concurrent.txt
+++ b/src/cmd/go/testdata/script/mod_concurrent.txt
@@ -1,7 +1,7 @@
env GO111MODULE=on
# Concurrent builds should succeed, even if they need to download modules.
-go get -d ./x ./y
+go get ./x ./y
go build ./x &
go build ./y
wait
diff --git a/src/cmd/go/testdata/script/mod_deprecate_message.txt b/src/cmd/go/testdata/script/mod_deprecate_message.txt
index 567027935dc338c32ec73644c594253b640c3cc6..8e2771dc1f2250b542c4c737ec0cfeeb196a1278 100644
--- a/src/cmd/go/testdata/script/mod_deprecate_message.txt
+++ b/src/cmd/go/testdata/script/mod_deprecate_message.txt
@@ -1,25 +1,25 @@
# When there is a short single-line message, 'go get' should print it all.
-go get -d short
+go get short
stderr '^go: module short is deprecated: short$'
go list -m -u -f '{{.Deprecated}}' short
stdout '^short$'
# When there is a multi-line message, 'go get' should print the first line.
-go get -d multiline
+go get multiline
stderr '^go: module multiline is deprecated: first line$'
! stderr 'second line'
go list -m -u -f '{{.Deprecated}}' multiline
stdout '^first line\nsecond line.$'
# When there is a long message, 'go get' should print a placeholder.
-go get -d long
+go get long
stderr '^go: module long is deprecated: \(message omitted: too long\)$'
go list -m -u -f '{{.Deprecated}}' long
stdout '^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$'
# When a message contains unprintable chracters, 'go get' should say that
# without printing the message.
-go get -d unprintable
+go get unprintable
stderr '^go: module unprintable is deprecated: \(message omitted: contains non-printable characters\)$'
go list -m -u -f '{{.Deprecated}}' unprintable
stdout '^message contains ASCII BEL\x07$'
diff --git a/src/cmd/go/testdata/script/mod_domain_root.txt b/src/cmd/go/testdata/script/mod_domain_root.txt
index 14745b5812c68296354cb7116cc9553b16357b94..c13029d534163e0f58b7e2de27d989cc7fadcf96 100644
--- a/src/cmd/go/testdata/script/mod_domain_root.txt
+++ b/src/cmd/go/testdata/script/mod_domain_root.txt
@@ -2,7 +2,7 @@
# (example.com not example.com/something)
env GO111MODULE=on
-go get -d
+go get
-- go.mod --
module x
diff --git a/src/cmd/go/testdata/script/mod_dot.txt b/src/cmd/go/testdata/script/mod_dot.txt
index ca8d5c6cc2d62d9ebd1a64f72efdfaae15d22cf4..cb60e988b6dfcc671e59c35f31a9f28b08f67ac0 100644
--- a/src/cmd/go/testdata/script/mod_dot.txt
+++ b/src/cmd/go/testdata/script/mod_dot.txt
@@ -5,11 +5,11 @@ env GO111MODULE=on
# to resolve an external module.
cd dir
! go get
-stderr '^go get: no package in current directory$'
+stderr '^go: no package to get in current directory$'
! go get .
-stderr '^go get \.: no package in current directory$'
+stderr '^go: .: no package to get in current directory$'
! go get ./subdir
-stderr '^go get: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
+stderr '^go: \.[/\\]subdir \('$WORK'[/\\]gopath[/\\]src[/\\]dir[/\\]subdir\) is not a package in module rooted at '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
! go list
! stderr 'cannot find module providing package'
stderr '^no Go files in '$WORK'[/\\]gopath[/\\]src[/\\]dir$'
diff --git a/src/cmd/go/testdata/script/mod_download.txt b/src/cmd/go/testdata/script/mod_download.txt
index c2b72b2a02cab8f681872c637d1d9990c9131db5..154e68338b6b3e2f2bdfd63e3d789c7f664dd1e9 100644
--- a/src/cmd/go/testdata/script/mod_download.txt
+++ b/src/cmd/go/testdata/script/mod_download.txt
@@ -93,19 +93,19 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip
# download reports errors encountered when locating modules
! go mod download bad/path
-stderr '^go mod download: module bad/path: not a known dependency$'
+stderr '^go: module bad/path: not a known dependency$'
! go mod download bad/path@latest
-stderr '^go mod download: bad/path@latest: malformed module path "bad/path": missing dot in first path element$'
+stderr '^go: bad/path@latest: malformed module path "bad/path": missing dot in first path element$'
! go mod download rsc.io/quote@v1.999.999
-stderr '^go mod download: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$'
+stderr '^go: rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$'
! go mod download -json bad/path
stdout '^\t"Error": "module bad/path: not a known dependency"'
# download main module produces a warning or error
go mod download m
-stderr '^go mod download: skipping argument m that resolves to the main module\n'
+stderr '^go: skipping download of m that resolves to the main module\n'
! go mod download m@latest
-stderr '^go mod download: m@latest: malformed module path "m": missing dot in first path element$'
+stderr '^go: m@latest: malformed module path "m": missing dot in first path element$'
# download without arguments updates go.mod and go.sum after loading the
# build list, but does not save sums for downloaded zips.
@@ -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_get_insecure_redirect.txt b/src/cmd/go/testdata/script/mod_download_insecure_redirect.txt
similarity index 65%
rename from src/cmd/go/testdata/script/mod_get_insecure_redirect.txt
rename to src/cmd/go/testdata/script/mod_download_insecure_redirect.txt
index 2e1283449554072c43509f293f8e64c11221e30d..46eb666686502cafdea600ab9b0ec044bb454212 100644
--- a/src/cmd/go/testdata/script/mod_get_insecure_redirect.txt
+++ b/src/cmd/go/testdata/script/mod_download_insecure_redirect.txt
@@ -7,26 +7,26 @@ env GO111MODULE=on
env GOPROXY=direct
env GOSUMDB=off
-! go get -d vcs-test.golang.org/insecure/go/insecure
+! go mod download vcs-test.golang.org/insecure/go/insecure@latest
stderr 'redirected .* to insecure URL'
# insecure host
env GOINSECURE=vcs-test.golang.org
go clean -modcache
-go get -d vcs-test.golang.org/insecure/go/insecure
+go mod download vcs-test.golang.org/insecure/go/insecure@latest
# insecure glob host
env GOINSECURE=*.golang.org
go clean -modcache
-go get -d vcs-test.golang.org/insecure/go/insecure
+go mod download vcs-test.golang.org/insecure/go/insecure@latest
# insecure multiple host
env GOINSECURE=somewhere-else.com,*.golang.org
go clean -modcache
-go get -d vcs-test.golang.org/insecure/go/insecure
+go mod download vcs-test.golang.org/insecure/go/insecure@latest
# different insecure host does not fetch
env GOINSECURE=somewhere-else.com
go clean -modcache
-! go get -d vcs-test.golang.org/insecure/go/insecure
+! go mod download vcs-test.golang.org/insecure/go/insecure@latest
stderr 'redirected .* to insecure URL'
diff --git a/src/cmd/go/testdata/script/mod_download_partial.txt b/src/cmd/go/testdata/script/mod_download_partial.txt
index 0aab60ddaf00f84c24f19073af65dc0bec5dc8a2..3a02fcd7474b5b795f3159d1ac718e3b541f3764 100644
--- a/src/cmd/go/testdata/script/mod_download_partial.txt
+++ b/src/cmd/go/testdata/script/mod_download_partial.txt
@@ -1,5 +1,5 @@
# Download modules and populate go.sum.
-go get -d -modcacherw
+go get -modcacherw
exists $GOPATH/pkg/mod/rsc.io/quote@v1.5.2/go.mod
# 'go mod verify' should fail if we delete a file.
diff --git a/src/cmd/go/testdata/script/mod_get_private_vcs.txt b/src/cmd/go/testdata/script/mod_download_private_vcs.txt
similarity index 79%
rename from src/cmd/go/testdata/script/mod_get_private_vcs.txt
rename to src/cmd/go/testdata/script/mod_download_private_vcs.txt
index 75c776a7fa29c78d1856d381fa2a938b15348034..e126793907241b65c6de002dc3cb200f37dd2fea 100644
--- a/src/cmd/go/testdata/script/mod_get_private_vcs.txt
+++ b/src/cmd/go/testdata/script/mod_download_private_vcs.txt
@@ -5,18 +5,18 @@ env GO111MODULE=on
[!exec:git] skip
env GOPROXY=direct
-! go get github.com/golang/nonexist
+! go mod download github.com/golang/nonexist@latest
stderr 'Confirm the import path was entered correctly.'
stderr 'If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.'
! stdout .
# Fetching a nonexistent commit should return an "unknown revision"
# error message.
-! go get github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b
-stderr '^go get: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$'
+! go mod download github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b
+stderr '^go: github.com/golang/term@86186f3aba07ed0212cfb944f3398997d2d07c6b: invalid version: unknown revision 86186f3aba07ed0212cfb944f3398997d2d07c6b$'
! stdout .
-! go get github.com/golang/nonexist@master
+! go mod download github.com/golang/nonexist@master
stderr '^Confirm the import path was entered correctly.$'
stderr '^If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.$'
! stderr 'unknown revision'
diff --git a/src/cmd/go/testdata/script/mod_get_svn.txt b/src/cmd/go/testdata/script/mod_download_svn.txt
similarity index 61%
rename from src/cmd/go/testdata/script/mod_get_svn.txt
rename to src/cmd/go/testdata/script/mod_download_svn.txt
index 3817fce9b61f87ee027b88c68bcd1ad87fbc199a..79e00dc9706c9fd80ba7f7a37eec2e076678817b 100644
--- a/src/cmd/go/testdata/script/mod_get_svn.txt
+++ b/src/cmd/go/testdata/script/mod_download_svn.txt
@@ -1,7 +1,7 @@
[!net] skip
[!exec:svn] skip
-# 'go get' will fall back to svn+ssh once svn fails over protocols like https.
+# 'go mod download' will fall back to svn+ssh once svn fails over protocols like https.
# If vcs-test.golang.org isn't in the user's known_hosts file, this will result
# in an ssh prompt, which will stop 'go test' entirely
#
@@ -19,18 +19,11 @@ env GOPROXY=direct
env GOSUMDB=off
# Attempting to get a module zip using svn should succeed.
-go get vcs-test.golang.org/svn/hello.svn@000000000001
+go mod download vcs-test.golang.org/svn/hello.svn@000000000001
exists $GOPATH/pkg/mod/cache/download/vcs-test.golang.org/svn/hello.svn/@v/v0.0.0-20170922011245-000000000001.zip
-exists $GOPATH/bin/hello.svn$GOEXE
# Attempting to get a nonexistent module using svn should fail with a
# reasonable message instead of a panic.
-! go get -d vcs-test.golang.org/svn/nonexistent.svn
+! go mod download vcs-test.golang.org/svn/nonexistent.svn@latest
! stderr panic
-stderr 'go get vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "upgrade"'
-
--- go.mod --
-module golang/go/issues/28943/main
--- go.sum --
-vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001 h1:rZjvboXMfQICKXdhx/QHqJ2Y/AQsJVrXnwGqwcTxQiw=
-vcs-test.golang.org/svn/hello.svn v0.0.0-20170922011245-000000000001/go.mod h1:0memnh/BRLuxiK2zF4rvUgz6ts/fhhB28l3ULFWPusc=
+stderr 'go: module vcs-test.golang.org/svn/nonexistent.svn: no matching versions for query "latest"$'
diff --git a/src/cmd/go/testdata/script/mod_get_too_many_redirects.txt b/src/cmd/go/testdata/script/mod_download_too_many_redirects.txt
similarity index 69%
rename from src/cmd/go/testdata/script/mod_get_too_many_redirects.txt
rename to src/cmd/go/testdata/script/mod_download_too_many_redirects.txt
index 9cbe0d279dea3073b7cbdffd60dd36a7830d4c07..a6b5a59054872926ed3fceb269e8899bc89c38d2 100644
--- a/src/cmd/go/testdata/script/mod_get_too_many_redirects.txt
+++ b/src/cmd/go/testdata/script/mod_download_too_many_redirects.txt
@@ -3,8 +3,8 @@ env GOPROXYBASE=$GOPROXY
env GOPROXY=$GOPROXYBASE/redirect/11
env GOSUMDB=off
-! go get -d rsc.io/quote@v1.2.0
+! go mod download rsc.io/quote@v1.2.0
stderr 'stopped after 10 redirects'
env GOPROXY=$GOPROXYBASE/redirect/9
-go get -d rsc.io/quote@v1.2.0
+go mod download rsc.io/quote@v1.2.0
diff --git a/src/cmd/go/testdata/script/mod_e.txt b/src/cmd/go/testdata/script/mod_e.txt
index 3a0d18dabc2c94ab57010a316ac6812601139d9a..3cffaf6ef1c1cf1bdc6bda4bc1ac6ae79966be00 100644
--- a/src/cmd/go/testdata/script/mod_e.txt
+++ b/src/cmd/go/testdata/script/mod_e.txt
@@ -24,11 +24,11 @@ cmp go.mod.orig go.mod
! go mod vendor
-stderr '^example.com/untidy imports\n\texample.net/directnotfound: cannot find module providing package example.net/directnotfound: module example.net/directnotfound: reading http://.*: 404 Not Found$'
+stderr '^example.com/untidy imports\n\texample.net/directnotfound: no required module provides package example.net/directnotfound; to add it:\n\tgo get example.net/directnotfound$'
-stderr '^example.com/untidy imports\n\texample.net/m imports\n\texample.net/indirectnotfound: cannot find module providing package example.net/indirectnotfound: module example.net/indirectnotfound: reading http://.*: 404 Not Found$'
+stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$'
-stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: cannot find module providing package example.net/directtestnotfound: module example.net/directtestnotfound: reading http://.*: 404 Not Found$'
+stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: no required module provides package example.net/directtestnotfound; to add it:\n\tgo get example.net/directtestnotfound$'
! stderr 'indirecttestnotfound' # Vendor prunes test dependencies.
@@ -43,15 +43,22 @@ stderr -count=4 'cannot find module providing package'
cmp go.mod.final go.mod
-# 'go mod vendor -e' still logs the errors, but succeeds and updates go.mod.
-
+# 'go mod vendor -e' still logs the errors, but creates a vendor directory
+# and exits with status 0.
+# 'go mod vendor -e' does not update go.mod and will not vendor packages that
+# would require changing go.mod, for example, by adding a requirement.
cp go.mod.orig go.mod
go mod vendor -e
-stderr -count=3 'cannot find module providing package'
-cmp go.mod.final go.mod
+stderr -count=2 'no required module provides package'
+stderr '^example.com/untidy imports\n\texample.net/m: module example.net/m provides package example.net/m and is replaced but not required; to add it:\n\tgo get example.net/m@v0.1.0$'
exists vendor/modules.txt
-exists vendor/example.net/m/m.go
+! exists vendor/example.net
+go mod edit -require example.net/m@v0.1.0
+go mod vendor -e
+stderr -count=3 'no required module provides package'
+exists vendor/modules.txt
+exists vendor/example.net/m/m.go
-- go.mod --
module example.com/untidy
diff --git a/src/cmd/go/testdata/script/mod_edit.txt b/src/cmd/go/testdata/script/mod_edit.txt
index 5aa5ca1ffc02c520d79eefa46e4d24f3b980dba4..ebc032a73cf7819171cb48b1137f16d497632d38 100644
--- a/src/cmd/go/testdata/script/mod_edit.txt
+++ b/src/cmd/go/testdata/script/mod_edit.txt
@@ -23,18 +23,18 @@ cmpenv go.mod $WORK/go.mod.edit2
# -exclude and -retract reject invalid versions.
! go mod edit -exclude=example.com/m@bad
-stderr '^go mod: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$'
+stderr '^go: -exclude=example.com/m@bad: version "bad" invalid: must be of the form v1.2.3$'
! go mod edit -retract=bad
-stderr '^go mod: -retract=bad: version "bad" invalid: must be of the form v1.2.3$'
+stderr '^go: -retract=bad: version "bad" invalid: must be of the form v1.2.3$'
! go mod edit -exclude=example.com/m@v2.0.0
-stderr '^go mod: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$'
+stderr '^go: -exclude=example.com/m@v2\.0\.0: version "v2\.0\.0" invalid: should be v2\.0\.0\+incompatible \(or module example\.com/m/v2\)$'
! go mod edit -exclude=example.com/m/v2@v1.0.0
-stderr '^go mod: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$'
+stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: should be v2, not v1$'
! go mod edit -exclude=gopkg.in/example.v1@v2.0.0
-stderr '^go mod: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$'
+stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$'
cmpenv go.mod $WORK/go.mod.edit2
diff --git a/src/cmd/go/testdata/script/mod_edit_go.txt b/src/cmd/go/testdata/script/mod_edit_go.txt
index 38321d071fb35e9adf1c8664f9c7de6edfab9549..7e9740fec43763a0727719f623c7ee708e98de97 100644
--- a/src/cmd/go/testdata/script/mod_edit_go.txt
+++ b/src/cmd/go/testdata/script/mod_edit_go.txt
@@ -2,7 +2,7 @@
env GO111MODULE=on
! go build
-stderr 'type aliases only supported as of'
+stderr ' type aliases requires'
go mod edit -go=1.9
grep 'go 1.9' go.mod
go build
@@ -11,7 +11,7 @@ go build
# the cached 1.9 build. (https://golang.org/issue/37804)
go mod edit -go=1.8
! go build
-stderr 'type aliases only supported as of'
+stderr 'type aliases requires'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt
index daed03b02c6cf41b6367d6ffa1846c2e37d1d39d..92149933686a6fc5e7bdbd9733ac8e414c49c808 100644
--- a/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt
+++ b/src/cmd/go/testdata/script/mod_get_ambiguous_arg.txt
@@ -4,14 +4,14 @@ cp go.mod go.mod.orig
# If there is no sensible *package* meaning for 'm/p', it should refer
# to *module* m/p.
-go get -d m/p # @latest
+go get m/p # @latest
go list -m all
stdout '^m/p v0.3.0 '
! stdout '^m '
cp go.mod.orig go.mod
-go get -d m/p@v0.1.0
+go get m/p@v0.1.0
go list -m all
stdout '^m/p v0.1.0 '
! stdout '^m '
@@ -22,7 +22,7 @@ stdout '^m/p v0.1.0 '
# (It only refers to *module* m/p if there is no such package at the
# requested version.)
-go get -d m/p@v0.2.0
+go get m/p@v0.2.0
go list -m all
stdout '^m v0.2.0 '
stdout '^m/p v0.1.0 ' # unchanged from the previous case
@@ -30,7 +30,7 @@ stdout '^m/p v0.1.0 ' # unchanged from the previous case
# Repeating the above with module m/p already in the module graph does not
# change its meaning.
-go get -d m/p@v0.2.0
+go get m/p@v0.2.0
go list -m all
stdout '^m v0.2.0 '
stdout '^m/p v0.1.0 '
diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt
index 33605f51a5b505570a3b7c394bfe426b72862a39..0af78bd4f252d737e2880c783055dd4414fea408 100644
--- a/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt
+++ b/src/cmd/go/testdata/script/mod_get_ambiguous_import.txt
@@ -8,14 +8,14 @@ cp go.mod go.mod.orig
#
# TODO(#27899): Should we automatically upgrade example.net/m to v0.2.0
# to resolve the conflict?
-! go get -d example.net/m/p@v1.0.0
+! go get example.net/m/p@v1.0.0
stderr '^example.net/m/p: ambiguous import: found package example.net/m/p in multiple modules:\n\texample.net/m v0.1.0 \(.*[/\\]m1[/\\]p\)\n\texample.net/m/p v1.0.0 \(.*[/\\]p0\)\n\z'
cmp go.mod go.mod.orig
# Upgrading both modules simultaneously resolves the ambiguous upgrade.
# Note that this command line mixes a module path (example.net/m)
# and a package path (example.net/m/p) in the same command.
-go get -d example.net/m@v0.2.0 example.net/m/p@v1.0.0
+go get example.net/m@v0.2.0 example.net/m/p@v1.0.0
go list -m all
stdout '^example.net/m v0.2.0 '
diff --git a/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt b/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt
index 0e7f93bccb5b8773ee9b461b27efa3f5511074cf..1641196007b8e166a685f00d292207a72b69d25c 100644
--- a/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt
+++ b/src/cmd/go/testdata/script/mod_get_ambiguous_pkg.txt
@@ -8,7 +8,7 @@ cp go.mod go.mod.orig
# From a clean slate, 'go get' currently does the same thing as 'go mod tidy':
# it resolves the package from the module with the longest matching prefix.
-go get -d example.net/ambiguous/nested/pkg@v0.1.0
+go get example.net/ambiguous/nested/pkg@v0.1.0
go list -m all
stdout '^example.net/ambiguous/nested v0.1.0$'
! stdout '^example.net/ambiguous '
@@ -21,7 +21,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$'
cp go.mod.orig go.mod
go mod edit -require=example.net/ambiguous@v0.1.0
-go get -d example.net/ambiguous/nested/pkg@v0.1.0
+go get example.net/ambiguous/nested/pkg@v0.1.0
go list -m all
stdout '^example.net/ambiguous v0.1.0$'
! stdout '^example.net/ambiguous/nested '
@@ -30,7 +30,7 @@ stdout '^example.net/ambiguous v0.1.0$'
# The user should be able to make the command unambiguous by explicitly
# upgrading the conflicting module...
-go get -d example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0
+go get example.net/ambiguous@v0.2.0 example.net/ambiguous/nested/pkg@v0.1.0
go list -m all
stdout '^example.net/ambiguous/nested v0.1.0$'
stdout '^example.net/ambiguous v0.2.0$'
@@ -41,7 +41,7 @@ stdout '^example.net/ambiguous v0.2.0$'
cp go.mod.orig go.mod
go mod edit -require=example.net/ambiguous@v0.1.0
-go get -d example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none
+go get example.net/ambiguous/nested/pkg@v0.1.0 example.net/ambiguous/nested@none
go list -m all
! stdout '^example.net/ambiguous/nested '
stdout '^example.net/ambiguous v0.1.0$'
@@ -53,7 +53,7 @@ stdout '^example.net/ambiguous v0.1.0$'
cp go.mod.orig go.mod
go mod edit -require=example.net/ambiguous@v0.1.0
-go get -d example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0
+go get example.net/ambiguous@none example.net/ambiguous/nested/pkg@v0.1.0
go list -m all
stdout '^example.net/ambiguous/nested v0.1.0$'
! stdout '^example.net/ambiguous '
@@ -66,7 +66,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$'
cp go.mod.orig go.mod
-go get -d example.net/ambiguous/nested/pkg/...@v0.1.0
+go get example.net/ambiguous/nested/pkg/...@v0.1.0
go list -m all
stdout '^example.net/ambiguous/nested v0.1.0$'
! stdout '^example.net/ambiguous '
@@ -75,7 +75,7 @@ stdout '^example.net/ambiguous/nested v0.1.0$'
cp go.mod.orig go.mod
go mod edit -require=example.net/ambiguous@v0.1.0
-go get -d example.net/ambiguous/nested/pkg/...@v0.1.0
+go get example.net/ambiguous/nested/pkg/...@v0.1.0
go list -m all
! stdout '^example.net/ambiguous/nested '
stdout '^example.net/ambiguous v0.1.0$'
diff --git a/src/cmd/go/testdata/script/mod_get_changes.txt b/src/cmd/go/testdata/script/mod_get_changes.txt
index 3287b2a6095a1c180fbfbe99031f4d776be772eb..12a112b4d87af75c8302dad7e5c1eedfbfec8a09 100644
--- a/src/cmd/go/testdata/script/mod_get_changes.txt
+++ b/src/cmd/go/testdata/script/mod_get_changes.txt
@@ -3,9 +3,9 @@
# for changed indirect dependencies.
go list -m all
! stdout golang.org/x/text
-go get -d rsc.io/quote@v1.5.2
-stderr '^go get: added rsc.io/quote v1.5.2$'
-stderr '^go get: upgraded rsc.io/sampler v1.0.0 => v1.3.0$'
+go get rsc.io/quote@v1.5.2
+stderr '^go: added rsc.io/quote v1.5.2$'
+stderr '^go: upgraded rsc.io/sampler v1.0.0 => v1.3.0$'
! stderr '^go get.*golang.org/x/text'
go list -m all
stdout golang.org/x/text
@@ -14,18 +14,18 @@ cmp go.mod go.mod.upgrade
# When removing a requirement, 'go get' prints a message for the requiremnent
# and for changed explicit dependencies. 'go get' does not print messages
# for changed indirect dependencies.
-go get -d rsc.io/sampler@none
-stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.3.0$'
-stderr '^go get: removed rsc.io/sampler v1.3.0$'
+go get rsc.io/sampler@none
+stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.3.0$'
+stderr '^go: removed rsc.io/sampler v1.3.0$'
! stderr '^go get.*golang.org/x/text'
cmp go.mod go.mod.downgrade
# When removing or downgrading a requirement, 'go get' also prints a message
# for explicit dependencies removed as a consequence.
cp go.mod.usequote go.mod
-go get -d rsc.io/quote@v1.5.1
-stderr '^go get: downgraded rsc.io/quote v1.5.2 => v1.5.1$'
-stderr '^go get: removed usequote v0.0.0$'
+go get rsc.io/quote@v1.5.1
+stderr '^go: downgraded rsc.io/quote v1.5.2 => v1.5.1$'
+stderr '^go: removed usequote v0.0.0$'
-- go.mod --
module m
diff --git a/src/cmd/go/testdata/script/mod_get_cmd.txt b/src/cmd/go/testdata/script/mod_get_cmd.txt
deleted file mode 100644
index d31cee1444a6adf5dcc8b4706f06584dbf31ae3c..0000000000000000000000000000000000000000
--- a/src/cmd/go/testdata/script/mod_get_cmd.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-env GO111MODULE=on
-[short] skip
-
-# Test that when 'go get' is run from $GOBIN, it does not delete binaries
-# after it installs them. Verifies golang.org/issue/32766.
-
-go get example.com/tools/cmd/hello
-
-# 'go get' should not delete the command when run from $GOPATH/bin
-cd $GOPATH/bin
-exists hello$GOEXE
-go get example.com/tools/cmd/hello
-exists hello$GOEXE
-
-# 'go get' should not delete the command when run from a different $GOBIN
-mkdir $WORK/bin
-cd $WORK/bin
-env GOBIN=$WORK/bin
-go get example.com/tools/cmd/hello
-exists hello$GOEXE
diff --git a/src/cmd/go/testdata/script/mod_get_commit.txt b/src/cmd/go/testdata/script/mod_get_commit.txt
index 4649491a532a8c7a8b20c88fde9aa16ca7b751be..f60eaab3a77e09d4aee4c57839ce7098f1443f89 100644
--- a/src/cmd/go/testdata/script/mod_get_commit.txt
+++ b/src/cmd/go/testdata/script/mod_get_commit.txt
@@ -5,18 +5,18 @@ env GO111MODULE=on
# golang.org/x/text/language@commit should resolve.
# Because of -d, the compiler should not run.
-go get -d -x golang.org/x/text/language@14c0d48
+go get -x golang.org/x/text/language@14c0d48
! stderr 'compile|cp|gccgo .*language\.a$'
# go get should skip build with no Go files in root
-go get -d golang.org/x/text@14c0d48
+go get golang.org/x/text@14c0d48
# dropping -d, we should see a build.
[short] skip
env GOCACHE=$WORK/gocache # Looking for compile commands, so need a clean cache.
-go get -x golang.org/x/text/language@14c0d48
+go build -x golang.org/x/text/language
stderr 'compile|cp|gccgo .*language\.a$'
# BUG: after the build, the package should not be stale, as 'go install' would
@@ -24,19 +24,20 @@ stderr 'compile|cp|gccgo .*language\.a$'
go list -f '{{.Stale}}' golang.org/x/text/language
stdout ^true
-# install after get should not run the compiler again.
+# install after build should not run the compiler again.
go install -x golang.org/x/text/language
! stderr 'compile|cp|gccgo .*language\.a$'
-# even with -d, we should see an error for unknown packages.
-! go get -d -x golang.org/x/text/foo@14c0d48
+# we should see an error for unknown packages.
+! go get -x golang.org/x/text/foo@14c0d48
+stderr '^go: module golang.org/x/text@14c0d48 found \(v0.3.0\), but does not contain package golang.org/x/text/foo$'
# get pseudo-version should record that version
-go get -d rsc.io/quote@v0.0.0-20180214005840-23179ee8a569
+go get rsc.io/quote@v0.0.0-20180214005840-23179ee8a569
grep 'rsc.io/quote v0.0.0-20180214005840-23179ee8a569' go.mod
# but as commit should record as v1.5.1
-go get -d rsc.io/quote@23179ee8
+go get rsc.io/quote@23179ee8
grep 'rsc.io/quote v1.5.1' go.mod
# go mod edit -require does not interpret commits
@@ -44,7 +45,7 @@ go mod edit -require rsc.io/quote@23179ee
grep 'rsc.io/quote 23179ee' go.mod
# but other commands fix them
-go mod graph
+go list -m -mod=mod all
grep 'rsc.io/quote v1.5.1' go.mod
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
index 63cd27a42d2bc76911d0e36f589adb4f97259f22..03258f52966fc745f3a1e01ac0b182e285c18b13 100644
--- a/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
+++ b/src/cmd/go/testdata/script/mod_get_deprecate_install.txt
@@ -2,10 +2,10 @@
env GO111MODULE=on
-# 'go get' outside a module with an executable prints a deprecation message.
-go get example.com/cmd/a
-stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$'
-stderr 'Use ''go install pkg@version'' instead.'
+# 'go get' outside a module prints an error.
+! go get example.com/cmd/a
+stderr '^go: go.mod file not found in current directory or any parent directory.$'
+stderr '^\t''go get'' is no longer supported outside a module.$'
cp go.mod.orig go.mod
@@ -13,13 +13,16 @@ cp go.mod.orig go.mod
# This will stop building in the future, but it's the command we want to use.
go get rsc.io/quote
! stderr deprecated
+! stderr 'no longer installs'
cp go.mod.orig go.mod
-# 'go get' inside a module with an executable prints a different
-# deprecation message.
+# 'go get' inside a module with an executable does not print a message.
+# In 1.16 and 1.17, 'go get' did print a message in this case suggesting the
+# use of -d. In 1.18, -d is a no-op, and we'd like to begin discouraging
+# its use.
go get example.com/cmd/a
-stderr '^go get: installing executables with ''go get'' in module mode is deprecated.$'
-stderr 'To adjust and download dependencies of the current module, use ''go get -d'''
+! stderr deprecated
+! stderr 'no longer installs'
cp go.mod.orig go.mod
# 'go get' should not print a warning for a main package inside the main module.
diff --git a/src/cmd/go/testdata/script/mod_get_deprecated.txt b/src/cmd/go/testdata/script/mod_get_deprecated.txt
index 7bdd7a58a89b5d15c43520055daf3eb7b4c1d8f8..ec7bcfdb997e2e57218f11108430f09f46ab1862 100644
--- a/src/cmd/go/testdata/script/mod_get_deprecated.txt
+++ b/src/cmd/go/testdata/script/mod_get_deprecated.txt
@@ -1,31 +1,31 @@
# 'go get pkg' should not show a deprecation message for an unrelated module.
-go get -d ./use/nothing
+go get ./use/nothing
! stderr 'module.*is deprecated'
# 'go get pkg' should show a deprecation message for the module providing pkg.
-go get -d example.com/deprecated/a
+go get example.com/deprecated/a
stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
-go get -d example.com/deprecated/a@v1.0.0
+go get example.com/deprecated/a@v1.0.0
stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
# 'go get pkg' should show a deprecation message for a module providing
# packages directly imported by pkg.
-go get -d ./use/a
+go get ./use/a
stderr '^go: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
# 'go get pkg' may show a deprecation message for an indirectly required module
# if it provides a package named on the command line.
-go get -d ./use/b
+go get ./use/b
! stderr 'module.*is deprecated'
-go get -d local/use
+go get local/use
! stderr 'module.*is deprecated'
-go get -d example.com/deprecated/b
+go get example.com/deprecated/b
stderr '^go: module example.com/deprecated/b is deprecated: in example.com/deprecated/b@v1.9.0$'
# 'go get pkg' does not show a deprecation message for a module providing a
# directly imported package if the module is no longer deprecated in its
# latest version, even if the module is deprecated in its current version.
-go get -d ./use/undeprecated
+go get ./use/undeprecated
! stderr 'module.*is deprecated'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_direct.txt b/src/cmd/go/testdata/script/mod_get_direct.txt
index 42ccbcd38a143b49c2f6729881d2791adeb7e786..856e05bc329328d2fe33771d12e8ac7d7cfb11a2 100644
--- a/src/cmd/go/testdata/script/mod_get_direct.txt
+++ b/src/cmd/go/testdata/script/mod_get_direct.txt
@@ -10,7 +10,7 @@ env GO111MODULE=on
env GOPROXY=direct
env GOSUMDB=off
-go list -m cloud.google.com/go@master
+go list -m cloud.google.com/go@main
! stdout 'v0.0.0-'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt b/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt
index efc38f77c85d37b6d94840acc771bc06c2febf9e..0f5ba992da204ed28a6854cb29b0e6807f348e34 100644
--- a/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt
+++ b/src/cmd/go/testdata/script/mod_get_downadd_indirect.txt
@@ -18,7 +18,7 @@ cp go.mod go.mod.orig
go mod tidy
cmp go.mod.orig go.mod
-go get -d example.com/d@v0.1.0
+go get example.com/d@v0.1.0
go list -m all
stdout '^example.com/b v0.1.0 '
stdout '^example.com/c v0.1.0 '
diff --git a/src/cmd/go/testdata/script/mod_get_downgrade.txt b/src/cmd/go/testdata/script/mod_get_downgrade.txt
index c26c5e1c21013ada363965a4b768d36d921c6552..2eed56d9b71b899dac731e794e6bc18a50815653 100644
--- a/src/cmd/go/testdata/script/mod_get_downgrade.txt
+++ b/src/cmd/go/testdata/script/mod_get_downgrade.txt
@@ -3,25 +3,25 @@ env GO111MODULE=on
# downgrade sampler should downgrade quote
cp go.mod.orig go.mod
-go get -d rsc.io/sampler@v1.0.0
+go get rsc.io/sampler@v1.0.0
go list -m all
stdout 'rsc.io/quote v1.4.0'
stdout 'rsc.io/sampler v1.0.0'
# downgrade sampler away should downgrade quote further
-go get -d rsc.io/sampler@none
+go get rsc.io/sampler@none
go list -m all
stdout 'rsc.io/quote v1.3.0'
# downgrade should report inconsistencies and not change go.mod
-go get -d rsc.io/quote@v1.5.1
+go get rsc.io/quote@v1.5.1
go list -m all
stdout 'rsc.io/quote v1.5.1'
stdout 'rsc.io/sampler v1.3.0'
-! go get -d rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none
-stderr -count=1 '^go get:'
-stderr '^go get: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$'
+! go get rsc.io/sampler@v1.0.0 rsc.io/quote@v1.5.2 golang.org/x/text@none
+! stderr add|remove|upgrad|downgrad
+stderr '^go: rsc.io/quote@v1.5.2 requires rsc.io/sampler@v1.3.0, not rsc.io/sampler@v1.0.0$'
go list -m all
stdout 'rsc.io/quote v1.5.1'
@@ -29,7 +29,7 @@ stdout 'rsc.io/sampler v1.3.0'
# go get -u args should limit upgrades
cp go.mod.empty go.mod
-go get -d -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0
+go get -u rsc.io/quote@v1.4.0 rsc.io/sampler@v1.0.0
go list -m all
stdout 'rsc.io/quote v1.4.0'
stdout 'rsc.io/sampler v1.0.0'
@@ -40,7 +40,7 @@ stdout 'rsc.io/sampler v1.0.0'
cp go.mod.orig go.mod
go list -m -versions example.com/latemigrate/v2
stdout v2.0.0 # proxy may serve incompatible versions
-go get -d rsc.io/quote@none
+go get rsc.io/quote@none
go list -m all
! stdout 'example.com/latemigrate/v2'
diff --git a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
index 5b768faeb1802c7ffca5389a0eb74d34aa7418db..582593b96cdfb6241e403216c214fd2668642549 100644
--- a/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
+++ b/src/cmd/go/testdata/script/mod_get_downgrade_missing.txt
@@ -4,15 +4,15 @@ cp go.mod go.mod.orig
# not yet present in that module should report the version mismatch
# rather than a "matched no packages" warning.
-! go get -d example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/...
-stderr '^go get: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$'
+! go get example.net/pkgadded@v1.1.0 example.net/pkgadded/subpkg/...
+stderr '^go: example.net/pkgadded@v1.1.0 conflicts with example.net/pkgadded/subpkg/...@upgrade \(v1.2.0\)$'
! stderr 'matched no packages'
cmp go.mod.orig go.mod
# A wildcard pattern should match the pattern with that path.
-go get -d example.net/pkgadded/...@v1.0.0
+go get example.net/pkgadded/...@v1.0.0
go list -m all
stdout '^example.net/pkgadded v1.0.0'
cp go.mod.orig go.mod
@@ -22,12 +22,12 @@ cp go.mod.orig go.mod
# and another argument constrains away the version that provides that
# package, then 'go get' should fail with a useful error message.
-! go get -d example.net/pkgadded@v1.0.0 .
+! go get example.net/pkgadded@v1.0.0 .
stderr '^example.com/m imports\n\texample.net/pkgadded/subpkg: cannot find module providing package example.net/pkgadded/subpkg$'
! stderr 'example.net/pkgadded v1\.2\.0'
cmp go.mod.orig go.mod
-go get -d example.net/pkgadded@v1.0.0
+go get example.net/pkgadded@v1.0.0
! go list -deps -mod=readonly .
stderr '^m.go:3:8: cannot find module providing package example\.net/pkgadded/subpkg: '
diff --git a/src/cmd/go/testdata/script/mod_get_downup_artifact.txt b/src/cmd/go/testdata/script/mod_get_downup_artifact.txt
index c20583b22a136822effc89c7844217ab4ea8c8e4..111a54f8f7370cacb24af0852354e88246185921 100644
--- a/src/cmd/go/testdata/script/mod_get_downup_artifact.txt
+++ b/src/cmd/go/testdata/script/mod_get_downup_artifact.txt
@@ -55,7 +55,7 @@ stdout '^example.com/d v0.1.0 '
# upgrades of module d and addition of module e, which are not relevant to
# b@v0.1.0 and should not be added to the main module's dependencies.
-go get -u -d example.com/a@latest example.com/c@v0.1.0
+go get -u example.com/a@latest example.com/c@v0.1.0
go list -m all
stdout '^example.com/a v0.1.0 '
diff --git a/src/cmd/go/testdata/script/mod_get_downup_indirect.txt b/src/cmd/go/testdata/script/mod_get_downup_indirect.txt
index ced1dcd6b14e37a528f1a9e494c3e93a6acbf804..3a46a774ce7c4516ae97074111f3db5d6196fec4 100644
--- a/src/cmd/go/testdata/script/mod_get_downup_indirect.txt
+++ b/src/cmd/go/testdata/script/mod_get_downup_indirect.txt
@@ -25,7 +25,7 @@ cp go.mod go.mod.orig
go mod tidy
cmp go.mod.orig go.mod
-go get -d example.com/d@v0.1.0
+go get example.com/d@v0.1.0
go list -m all
! stdout '^example.com/b '
! stdout '^example.com/c '
diff --git a/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt b/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt
index c49615cecb34d22ee114a974af6ecc2469814f4e..b678a177b521e32619b4afd6058e839813026327 100644
--- a/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt
+++ b/src/cmd/go/testdata/script/mod_get_downup_pseudo_artifact.txt
@@ -28,7 +28,7 @@ cmp go.mod.orig go.mod
# When we downgrade d.2 to d.1, no dependency on e should be added
# because nothing else in the module or import graph requires it.
-go get -d example.net/d@v0.1.0
+go get example.net/d@v0.1.0
go list -m all
stdout '^example.net/b v0.2.1-0.20210219000000-000000000000 '
diff --git a/src/cmd/go/testdata/script/mod_get_errors.txt b/src/cmd/go/testdata/script/mod_get_errors.txt
index 5c37058d1c1b9508e8a77ce8fb3de85eabf34eaf..7cb03ce2f1e6b5b4f5b5efd63110653c0a172740 100644
--- a/src/cmd/go/testdata/script/mod_get_errors.txt
+++ b/src/cmd/go/testdata/script/mod_get_errors.txt
@@ -1,35 +1,23 @@
cp go.mod go.mod.orig
-# Both 'go get' and 'go get -d' should fail, without updating go.mod,
-# if the transitive dependencies of the requested package (by default,
-# the package in the current directory) cannot be resolved.
+# 'go get' should fail, without updating go.mod, if the transitive dependencies
+# of the requested package (by default, the package in the current directory)
+# cannot be resolved.
! go get
stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$'
cmp go.mod.orig go.mod
-! go get -d
-stderr '^example.com/m imports\n\texample.com/badimport imports\n\texample.net/oops: cannot find module providing package example.net/oops$'
-cmp go.mod.orig go.mod
-
cd importsyntax
-# If 'go get' fails due to a compile error (such as a syntax error),
-# it should not update the go.mod file.
-
-! go get
-stderr '^..[/\\]badimport[/\\]syntaxerror[/\\]syntaxerror.go:1:1: expected ''package'', found pack$' # TODO: An import stack would be nice.
-cmp ../go.mod.orig ../go.mod
-
-
# A syntax error in a dependency prevents the compiler from needing that
-# dependency's imports, so 'go get -d' should not report an error when those
+# dependency's imports, so 'go get' should not report an error when those
# imports cannot be resolved: it has all of the dependencies that the compiler
# needs, and the user did not request to run the compiler.
-go get -d
+go get
cmp ../go.mod.syntax-d ../go.mod
diff --git a/src/cmd/go/testdata/script/mod_get_extra.txt b/src/cmd/go/testdata/script/mod_get_extra.txt
index 7efa24e87b0d3522d18a14b1c760ca6df88649c5..083e03678e48f1ebc0ffa5b1baece7841e8d258a 100644
--- a/src/cmd/go/testdata/script/mod_get_extra.txt
+++ b/src/cmd/go/testdata/script/mod_get_extra.txt
@@ -4,7 +4,7 @@ cp go.mod go.mod.orig
# determined by explicit queries to any version other than the explicit one.
# Otherwise, 'go get -u' could introduce spurious dependencies.
-go get -d -u example.net/a@v0.1.0 example.net/b@v0.1.0
+go get -u example.net/a@v0.1.0 example.net/b@v0.1.0
go list -m all
stdout '^example.net/a v0.1.0 '
stdout '^example.net/b v0.1.0 '
@@ -16,7 +16,7 @@ stdout '^example.net/b v0.1.0 '
cp go.mod.orig go.mod
-go get -d -u example.net/a@v0.1.0 example.net/b/...@v0.1.0
+go get -u example.net/a@v0.1.0 example.net/b/...@v0.1.0
go list -m all
stdout '^example.net/a v0.1.0 '
stdout '^example.net/b v0.1.0 '
diff --git a/src/cmd/go/testdata/script/mod_get_fallback.txt b/src/cmd/go/testdata/script/mod_get_fallback.txt
index 9733fa366bea57eb19370d6cb125c87933f845f9..35722333d6a0e86b195f80ff13f1fa73c56fa3d1 100644
--- a/src/cmd/go/testdata/script/mod_get_fallback.txt
+++ b/src/cmd/go/testdata/script/mod_get_fallback.txt
@@ -5,6 +5,11 @@ env GO111MODULE=on
env GOPROXY=https://proxy.golang.org,direct
env GOSUMDB=off
-go get -x -v -d golang.org/x/tools/cmd/goimports
+go get -x -v golang.org/x/tools/cmd/goimports
stderr '# get https://proxy.golang.org/golang.org/x/tools/@v/list'
! stderr '# get https://golang.org'
+
+-- go.mod --
+module m
+
+go 1.18
diff --git a/src/cmd/go/testdata/script/mod_get_fossil.txt b/src/cmd/go/testdata/script/mod_get_fossil.txt
index baad544557af8861bed6a0ab30d4126678a2bd8c..c2d42f0f596f4a01b0077a54aa515b9fba1f4f68 100644
--- a/src/cmd/go/testdata/script/mod_get_fossil.txt
+++ b/src/cmd/go/testdata/script/mod_get_fossil.txt
@@ -18,11 +18,10 @@ env GOSUMDB=off
env USER=fossiluser
env FOSSIL_HOME=$WORK/home
-# Attempting to get the latest version of a fossil repo.
+# Attempt to get the latest version of a fossil repo.
go get vcs-test.golang.org/fossil/hello.fossil
! stderr 'unexpected response from fossil info'
grep 'vcs-test.golang.org/fossil/hello.fossil' go.mod
-exists $GOPATH/bin/hello.fossil$GOEXE
-- go.mod --
module x
diff --git a/src/cmd/go/testdata/script/mod_get_go_file.txt b/src/cmd/go/testdata/script/mod_get_go_file.txt
index 0c7b5dc11c581f7a47d0bcb65e26b572da0a47e6..c81e491b947ecbe93955979ad978dfba8be7497d 100644
--- a/src/cmd/go/testdata/script/mod_get_go_file.txt
+++ b/src/cmd/go/testdata/script/mod_get_go_file.txt
@@ -17,7 +17,7 @@ env GO111MODULE=on
# argument has .go suffix, is a file and exists
! go get test.go
-stderr 'go get test.go: arguments must be package or module paths'
+stderr 'go: test.go: arguments must be package or module paths'
# argument has .go suffix, doesn't exist and has no slashes
! go get test_missing.go
@@ -25,7 +25,7 @@ stderr 'arguments must be package or module paths'
# argument has .go suffix, is a file and exists in sub-directory
! go get test/test.go
-stderr 'go get: test/test.go exists as a file, but ''go get'' requires package arguments'
+stderr 'go: test/test.go exists as a file, but ''go get'' requires package arguments'
# argument has .go suffix, doesn't exist and has slashes
! go get test/test_missing.go
@@ -35,19 +35,19 @@ stderr 'go get: test/test.go exists as a file, but ''go get'' requires package a
# argument has .go suffix, is a symlink and exists
[symlink] symlink test_sym.go -> test.go
[symlink] ! go get test_sym.go
-[symlink] stderr 'go get test_sym.go: arguments must be package or module paths'
+[symlink] stderr 'go: test_sym.go: arguments must be package or module paths'
[symlink] rm test_sym.go
# argument has .go suffix, is a symlink and exists in sub-directory
[symlink] symlink test/test_sym.go -> test.go
[symlink] ! go get test/test_sym.go
-[symlink] stderr 'go get: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
+[symlink] stderr 'go: test/test_sym.go exists as a file, but ''go get'' requires package arguments'
[symlink] rm test_sym.go
# argument has .go suffix, is a directory and exists
mkdir test_dir.go
! go get test_dir.go
-stderr 'go get test_dir.go: arguments must be package or module paths'
+stderr 'go: test_dir.go: arguments must be package or module paths'
rm test_dir.go
# argument has .go suffix, is a directory and exists in sub-directory
@@ -58,6 +58,11 @@ mkdir test/test_dir.go
rm test/test_dir.go
+-- go.mod --
+module m
+
+go 1.18
+
-- test.go --
package main
func main() {println("test")}
diff --git a/src/cmd/go/testdata/script/mod_get_incompatible.txt b/src/cmd/go/testdata/script/mod_get_incompatible.txt
index 8000ee61481a01be883b022d17001ed2301f2a6b..5a7d70637180af3eb4c7c5483c102c47819523c8 100644
--- a/src/cmd/go/testdata/script/mod_get_incompatible.txt
+++ b/src/cmd/go/testdata/script/mod_get_incompatible.txt
@@ -1,15 +1,15 @@
env GO111MODULE=on
-go get -d x
+go get x
go list -m all
stdout 'rsc.io/breaker v2.0.0\+incompatible'
cp go.mod2 go.mod
-go get -d rsc.io/breaker@7307b30
+go get rsc.io/breaker@7307b30
go list -m all
stdout 'rsc.io/breaker v2.0.0\+incompatible'
-go get -d rsc.io/breaker@v2.0.0
+go get rsc.io/breaker@v2.0.0
go list -m all
stdout 'rsc.io/breaker v2.0.0\+incompatible'
diff --git a/src/cmd/go/testdata/script/mod_get_indirect.txt b/src/cmd/go/testdata/script/mod_get_indirect.txt
index e1cc1ab4115be419f6a779a508f125f17de11e6c..fa7edf22d7b997d0abd8648023d86488229e9b51 100644
--- a/src/cmd/go/testdata/script/mod_get_indirect.txt
+++ b/src/cmd/go/testdata/script/mod_get_indirect.txt
@@ -27,7 +27,7 @@ grep 'golang.org/x/text v0.3.0 // indirect$' go.mod
# indirect tag should be removed upon seeing direct import.
cp $WORK/tmp/uselang.go x.go
-go get -d
+go get
grep 'rsc.io/quote v1.5.2$' go.mod
grep 'golang.org/x/text [v0-9a-f\.-]+$' go.mod
diff --git a/src/cmd/go/testdata/script/mod_get_issue37438.txt b/src/cmd/go/testdata/script/mod_get_issue37438.txt
index 38b2031d3ae4a0fa16354af38e8879875c45e401..9392e73a174bd69e8efdfe49753f28f2f7d0d6fd 100644
--- a/src/cmd/go/testdata/script/mod_get_issue37438.txt
+++ b/src/cmd/go/testdata/script/mod_get_issue37438.txt
@@ -6,7 +6,7 @@
# 'go get foo@requested' should resolve the requested version,
# not error out on the (unrelated) latest one.
-go get -d example.net/a/p@v0.2.0
+go get example.net/a/p@v0.2.0
-- go.mod --
module example
diff --git a/src/cmd/go/testdata/script/mod_get_issue47979.txt b/src/cmd/go/testdata/script/mod_get_issue47979.txt
new file mode 100644
index 0000000000000000000000000000000000000000..848ee3aa09a9ad3d696b0ed30c37ed3332666145
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_get_issue47979.txt
@@ -0,0 +1,117 @@
+# Regression test for https://golang.org/issue/47979:
+#
+# An argument to 'go get' that results in an upgrade to a different existing
+# root should be allowed, and should not panic the 'go' command.
+
+cp go.mod go.mod.orig
+
+
+# Transitive upgrades from upgraded roots should not prevent
+# 'go get -u' from performing upgrades.
+
+cp go.mod.orig go.mod
+go get -u .
+cmp go.mod go.mod.want
+
+
+# 'go get' of a specific version should allow upgrades of
+# every dependency (transitively) required by that version,
+# including dependencies that are pulled into the module
+# graph by upgrading other root requirements
+# (in this case, example.net/indirect).
+
+cp go.mod.orig go.mod
+go get example.net/a@v0.2.0
+cmp go.mod go.mod.want
+
+
+-- go.mod --
+module golang.org/issue47979
+
+go 1.17
+
+replace (
+ example.net/a v0.1.0 => ./a1
+ example.net/a v0.2.0 => ./a2
+ example.net/indirect v0.1.0 => ./indirect1
+ example.net/indirect v0.2.0 => ./indirect2
+ example.net/other v0.1.0 => ./other
+ example.net/other v0.2.0 => ./other
+)
+
+require (
+ example.net/a v0.1.0
+ example.net/other v0.1.0
+)
+
+require example.net/indirect v0.1.0 // indirect
+-- go.mod.want --
+module golang.org/issue47979
+
+go 1.17
+
+replace (
+ example.net/a v0.1.0 => ./a1
+ example.net/a v0.2.0 => ./a2
+ example.net/indirect v0.1.0 => ./indirect1
+ example.net/indirect v0.2.0 => ./indirect2
+ example.net/other v0.1.0 => ./other
+ example.net/other v0.2.0 => ./other
+)
+
+require (
+ example.net/a v0.2.0
+ example.net/other v0.2.0
+)
+
+require example.net/indirect v0.2.0 // indirect
+-- issue.go --
+package issue
+
+import _ "example.net/a"
+-- useother/useother.go --
+package useother
+
+import _ "example.net/other"
+-- a1/go.mod --
+module example.net/a
+
+go 1.17
+
+require example.net/indirect v0.1.0
+-- a1/a.go --
+package a
+-- a2/go.mod --
+module example.net/a
+
+go 1.17
+
+require example.net/indirect v0.2.0
+-- a2/a.go --
+package a
+
+import "example.net/indirect"
+-- indirect1/go.mod --
+module example.net/indirect
+
+go 1.17
+
+require example.net/other v0.1.0
+-- indirect1/indirect.go --
+package indirect
+-- indirect2/go.mod --
+module example.net/indirect
+
+go 1.17
+
+require example.net/other v0.2.0
+-- indirect2/indirect.go --
+package indirect
+
+import "example.net/other"
+-- other/go.mod --
+module example.net/other
+
+go 1.17
+-- other/other.go --
+package other
diff --git a/src/cmd/go/testdata/script/mod_get_issue48511.txt b/src/cmd/go/testdata/script/mod_get_issue48511.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0ba486d35bd6cecc2d8a6f4236c42613e445f7be
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_get_issue48511.txt
@@ -0,0 +1,68 @@
+# Regression test for https://golang.org/issue/48511:
+# requirement minimization was accidentally replacing previous
+# versions of the main module, causing dependencies to be
+# spuriously dropping during requirement minimization and
+# leading to an infinite loop.
+
+cp go.mod go.mod.orig
+go mod tidy
+cmp go.mod go.mod.orig
+
+go get -u=patch ./...
+cmp go.mod go.mod.want
+
+-- go.mod --
+module example.net/m
+
+go 1.16
+
+replace (
+ example.net/a v0.1.0 => ./a
+ example.net/b v0.1.0 => ./b
+ example.net/b v0.1.1 => ./b
+ example.net/m v0.1.0 => ./m1
+)
+
+require example.net/a v0.1.0
+-- go.mod.want --
+module example.net/m
+
+go 1.16
+
+replace (
+ example.net/a v0.1.0 => ./a
+ example.net/b v0.1.0 => ./b
+ example.net/b v0.1.1 => ./b
+ example.net/m v0.1.0 => ./m1
+)
+
+require (
+ example.net/a v0.1.0
+ example.net/b v0.1.1 // indirect
+)
+-- m.go --
+package m
+
+import "example.net/a"
+-- m1/go.mod --
+module example.net/m
+
+go 1.16
+
+require example.net/b v0.1.0
+-- a/go.mod --
+module example.net/a
+
+go 1.16
+
+require example.net/m v0.1.0
+-- a/a.go --
+package a
+
+import "example.net/b"
+-- b/go.mod --
+module example.net/b
+
+go 1.16
+-- b/b.go --
+package b
diff --git a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
index 241a0c2f0dfa8eaf6435c47007ff157f25f8ac26..00da0c316469f9bd3d5e54f8bd67eb931b53047c 100644
--- a/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
+++ b/src/cmd/go/testdata/script/mod_get_latest_pseudo.txt
@@ -5,6 +5,6 @@
env GO111MODULE=on
go mod init m
-go get -d example.com/notags
+go get example.com/notags
go list -m all
stdout '^example.com/notags v0.0.0-20190507143103-cc8cbe209b64$'
diff --git a/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt b/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3dae383de1e57bd5c9d664c290da73470c0fedab
--- /dev/null
+++ b/src/cmd/go/testdata/script/mod_get_lazy_upgrade_lazy.txt
@@ -0,0 +1,68 @@
+# Check that 'go get -u' will upgrade a dependency (direct or indirect)
+# when the main module and the dependency are both lazy.
+# Verifies #47768.
+
+# Check that go.mod is tidy, and an upgrade is available.
+cp go.mod go.mod.orig
+go mod tidy
+cmp go.mod go.mod.orig
+
+go list -m -u example.com/lazyupgrade
+stdout '^example.com/lazyupgrade v0.1.0 \[v0.1.1\] => ./lazyupgrade@v0.1.0$'
+
+# 'go get -u' on a package that directly imports the dependency should upgrade.
+go get -u ./usedirect
+go list -m example.com/lazyupgrade
+stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$'
+cp go.mod.orig go.mod
+
+# 'go get -u' on a package that indirectly imports the dependency should upgrade.
+go get -u ./useindirect
+go list -m example.com/lazyupgrade
+stdout '^example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1$'
+
+-- go.mod --
+module use
+
+go 1.17
+
+require (
+ direct v0.0.0
+ example.com/lazyupgrade v0.1.0
+)
+
+replace (
+ direct => ./direct
+ example.com/lazyupgrade v0.1.0 => ./lazyupgrade@v0.1.0
+ example.com/lazyupgrade v0.1.1 => ./lazyupgrade@v0.1.1
+)
+-- usedirect/usedirect.go --
+package use
+
+import _ "example.com/lazyupgrade"
+-- useindirect/useindirect.go --
+package use
+
+import _ "direct"
+-- direct/go.mod --
+module direct
+
+go 1.17
+
+require example.com/lazyupgrade v0.1.0
+-- direct/direct.go --
+package direct
+
+import _ "example.com/lazyupgrade"
+-- lazyupgrade@v0.1.0/go.mod --
+module example.com/lazyupgrade
+
+go 1.17
+-- lazyupgrade@v0.1.0/lazyupgrade.go --
+package lazyupgrade
+-- lazyupgrade@v0.1.1/go.mod --
+module example.com/lazyupgrade
+
+go 1.17
+-- lazyupgrade@v0.1.1/lazyupgrade.go --
+package lazyupgrade
diff --git a/src/cmd/go/testdata/script/mod_get_local.txt b/src/cmd/go/testdata/script/mod_get_local.txt
index eb09da58b3e23404626e37993efd9747d0811ff1..4c81d16a1fe072058a6cd4aeb5abcc6fc1ab519d 100644
--- a/src/cmd/go/testdata/script/mod_get_local.txt
+++ b/src/cmd/go/testdata/script/mod_get_local.txt
@@ -7,7 +7,7 @@ cp go.mod go.mod.orig
# 'go get -u' within the main module should work, even if it has a local-only name.
cp go.mod.orig go.mod
-go get -d -u ./...
+go get -u ./...
grep 'rsc.io/quote.*v1.5.2' go.mod
grep 'golang.org/x/text.*v0.3.0' go.mod
cp go.mod go.mod.implicitmod
@@ -15,34 +15,34 @@ cp go.mod go.mod.implicitmod
# 'go get -u local/...' should be equivalent to 'go get -u ./...'
# (assuming no nested modules)
cp go.mod.orig go.mod
-go get -d -u local/...
+go get -u local/...
cmp go.mod go.mod.implicitmod
# For the main module, @patch should be a no-op.
cp go.mod.orig go.mod
-go get -d -u local/...@patch
+go get -u local/...@patch
cmp go.mod go.mod.implicitmod
-# 'go get -u -d' in the empty root of the main module should fail.
-# 'go get -u -d .' should also fail.
+# 'go get -u' in the empty root of the main module should fail.
+# 'go get -u .' should also fail.
cp go.mod.orig go.mod
-! go get -u -d
-! go get -u -d .
+! go get -u
+! go get -u .
-# 'go get -u -d .' within a package in the main module updates the dependencies
+# 'go get -u .' within a package in the main module updates the dependencies
# of that package.
cp go.mod.orig go.mod
cd uselang
-go get -u -d .
+go get -u .
cd ..
grep 'rsc.io/quote.*v1.3.0' go.mod
grep 'golang.org/x/text.*v0.3.0' go.mod
cp go.mod go.mod.dotpkg
-# 'go get -u -d' with an explicit package in the main module updates the
+# 'go get -u' with an explicit package in the main module updates the
# dependencies of that package.
cp go.mod.orig go.mod
-go get -u -d local/uselang
+go get -u local/uselang
cmp go.mod go.mod.dotpkg
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_main.txt b/src/cmd/go/testdata/script/mod_get_main.txt
index 50b2fee9ae1bfd82cd66feecfd420a324c5a10f7..cddd5f70826eb406a18e9efb55ed018f241f4ab0 100644
--- a/src/cmd/go/testdata/script/mod_get_main.txt
+++ b/src/cmd/go/testdata/script/mod_get_main.txt
@@ -2,44 +2,44 @@ env GO111MODULE=on
cp go.mod.orig go.mod
# relative and absolute paths must be within the main module.
-! go get -d ..
-stderr '^go get: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
-! go get -d $WORK
-stderr '^go get: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
-! go get -d ../...
-stderr '^go get: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
-! go get -d $WORK/...
-stderr '^go get: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+! go get ..
+stderr '^go: \.\. \('$WORK'[/\\]gopath\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+! go get $WORK
+stderr '^go: '$WORK' is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+! go get ../...
+stderr '^go: \.\./\.\.\. \('$WORK'[/\\]gopath([/\\]...)?\) is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
+! go get $WORK/...
+stderr '^go: '$WORK'[/\\]\.\.\. is not within module rooted at '$WORK'[/\\]gopath[/\\]src$'
# @patch and @latest within the main module refer to the current version.
# The main module won't be upgraded, but missing dependencies will be added.
-go get -d rsc.io/x
+go get rsc.io/x
grep 'rsc.io/quote v1.5.2' go.mod
-go get -d rsc.io/x@upgrade
+go get rsc.io/x@upgrade
grep 'rsc.io/quote v1.5.2' go.mod
cp go.mod.orig go.mod
-go get -d rsc.io/x@patch
+go get rsc.io/x@patch
grep 'rsc.io/quote v1.5.2' go.mod
cp go.mod.orig go.mod
# Upgrading a package pattern not contained in the main module should not
# attempt to upgrade the main module.
-go get -d rsc.io/quote/...@v1.5.1
+go get rsc.io/quote/...@v1.5.1
grep 'rsc.io/quote v1.5.1' go.mod
# The main module cannot be updated to a specific version.
-! go get -d rsc.io@v0.1.0
-stderr '^go get: can''t request version "v0.1.0" of the main module \(rsc.io\)$'
+! go get rsc.io@v0.1.0
+stderr '^go: can''t request version "v0.1.0" of the main module \(rsc.io\)$'
# A package in the main module can't be upgraded either.
-! go get -d rsc.io/x@v0.1.0
-stderr '^go get: package rsc.io/x is in the main module, so can''t request version v0.1.0$'
+! go get rsc.io/x@v0.1.0
+stderr '^go: package rsc.io/x is in the main module, so can''t request version v0.1.0$'
# Nor can a pattern matching packages in the main module.
-! go get -d rsc.io/x/...@latest
-stderr '^go get: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$'
+! go get rsc.io/x/...@latest
+stderr '^go: pattern rsc.io/x/... matches package rsc.io/x in the main module, so can''t request version latest$'
-- go.mod.orig --
module rsc.io
diff --git a/src/cmd/go/testdata/script/mod_get_major.txt b/src/cmd/go/testdata/script/mod_get_major.txt
index 367ede9ded45ba5278d47437a4c989bdf44fb2d9..2db13180bd79786283919bba2e6e5d61ebe2c5c3 100644
--- a/src/cmd/go/testdata/script/mod_get_major.txt
+++ b/src/cmd/go/testdata/script/mod_get_major.txt
@@ -8,12 +8,12 @@ env GOSUMDB=off
# golang.org/issue/34383: if a module path ends in a major-version suffix,
# ensure that 'direct' mode can resolve the package to a module.
-go get -d vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0
+go get vcs-test.golang.org/git/v3pkg.git/v3@v3.0.0
go list -m vcs-test.golang.org/git/v3pkg.git/v3
stdout '^vcs-test.golang.org/git/v3pkg.git/v3 v3.0.0$'
-go get -d vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0
+go get vcs-test.golang.org/git/empty-v2-without-v1.git/v2@v2.0.0
go list -m vcs-test.golang.org/git/empty-v2-without-v1.git/v2
stdout '^vcs-test.golang.org/git/empty-v2-without-v1.git/v2 v2.0.0$'
diff --git a/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt b/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt
index 789d42d24dbca7184ff3572e50162f448acc5ab4..5934251e4b8b74688a40d060c261147b77a3e6d1 100644
--- a/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt
+++ b/src/cmd/go/testdata/script/mod_get_missing_ziphash.txt
@@ -13,7 +13,7 @@ env GOSUMDB=off
cp go.sum.bug go.sum
! go build -n use
stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$'
-go get -d use
+go get use
cmp go.sum go.sum.tidy
go build -n use
@@ -22,7 +22,7 @@ cp go.sum.bug go.sum
rm $WORK/gopath/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.ziphash
! go build -n use
stderr '^use.go:3:8: missing go.sum entry for module providing package rsc.io/quote \(imported by use\); to add:\n\tgo get use$'
-go get -d use
+go get use
cmp go.sum go.sum.tidy
go build -n use
diff --git a/src/cmd/go/testdata/script/mod_get_moved.txt b/src/cmd/go/testdata/script/mod_get_moved.txt
index 8430a737c40ca00842f14faede5001baa2f81a7e..ba79c8263c06dab4ab02ee466cf08275b99ea02c 100644
--- a/src/cmd/go/testdata/script/mod_get_moved.txt
+++ b/src/cmd/go/testdata/script/mod_get_moved.txt
@@ -4,17 +4,17 @@ env GO111MODULE=on
# A 'go get' that worked at a previous version should continue to work at that version,
# even if the package was subsequently moved into a submodule.
go mod init example.com/foo
-go get -d example.com/split/subpkg@v1.0.0
+go get example.com/split/subpkg@v1.0.0
go list -m all
stdout 'example.com/split v1.0.0'
# A 'go get' that simultaneously upgrades away conflicting package defitions is not ambiguous.
-go get -d example.com/split/subpkg@v1.1.0
+go get example.com/split/subpkg@v1.1.0
# A 'go get' without an upgrade should find the package.
rm go.mod
go mod init example.com/foo
-go get -d example.com/split/subpkg
+go get example.com/split/subpkg
go list -m all
stdout 'example.com/split/subpkg v1.1.0'
@@ -23,18 +23,18 @@ stdout 'example.com/split/subpkg v1.1.0'
# even if the package was subsequently moved into a parent module.
rm go.mod
go mod init example.com/foo
-go get -d example.com/join/subpkg@v1.0.0
+go get example.com/join/subpkg@v1.0.0
go list -m all
stdout 'example.com/join/subpkg v1.0.0'
# A 'go get' that simultaneously upgrades away conflicting package definitions is not ambiguous.
# (A wildcard pattern applies to both packages and modules,
# because we define wildcard matching to apply after version resolution.)
-go get -d example.com/join/subpkg/...@v1.1.0
+go get example.com/join/subpkg/...@v1.1.0
# A 'go get' without an upgrade should find the package.
rm go.mod
go mod init example.com/foo
-go get -d example.com/join/subpkg@v1.1.0
+go get example.com/join/subpkg@v1.1.0
go list -m all
stdout 'example.com/join v1.1.0'
diff --git a/src/cmd/go/testdata/script/mod_get_newcycle.txt b/src/cmd/go/testdata/script/mod_get_newcycle.txt
index f71620c1bcb41cac9e49ffc102c683f533a7ae4f..18dc6503617a566937844904736f80c50ab2a341 100644
--- a/src/cmd/go/testdata/script/mod_get_newcycle.txt
+++ b/src/cmd/go/testdata/script/mod_get_newcycle.txt
@@ -11,4 +11,4 @@ go mod init m
cmp stderr stderr-expected
-- stderr-expected --
-go get: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0
+go: example.com/newcycle/a@v1.0.0 requires example.com/newcycle/a@v1.0.1, not example.com/newcycle/a@v1.0.0
diff --git a/src/cmd/go/testdata/script/mod_get_none.txt b/src/cmd/go/testdata/script/mod_get_none.txt
index b358f05af36745226e909e1f4a81154bbf6fcdae..5aec209f59fe6fa54ffa3f67ce9d31d73fb665de 100644
--- a/src/cmd/go/testdata/script/mod_get_none.txt
+++ b/src/cmd/go/testdata/script/mod_get_none.txt
@@ -3,10 +3,10 @@ env GO111MODULE=on
go mod init example.com/foo
# 'go get bar@none' should be a no-op if module bar is not active.
-go get -d example.com/bar@none
+go get example.com/bar@none
go list -m all
! stdout example.com/bar
-go get -d example.com/bar@none
+go get example.com/bar@none
go list -m all
! stdout example.com/bar
diff --git a/src/cmd/go/testdata/script/mod_get_nopkgs.txt b/src/cmd/go/testdata/script/mod_get_nopkgs.txt
index 078e71a041c470463a9a7e7065de6fadb94bc30a..14176a7dc8786d0538c10ea5ec61b1fe1adefdba 100644
--- a/src/cmd/go/testdata/script/mod_get_nopkgs.txt
+++ b/src/cmd/go/testdata/script/mod_get_nopkgs.txt
@@ -6,17 +6,17 @@ cd subdir
go get ./...
stderr -count=1 'matched no packages'
-go get -d ./...
+go get ./...
stderr -count=1 'matched no packages'
# 'go get' on patterns that could conceivably match nested modules
# should report a module resolution error.
-go get -d example.net/emptysubdir/... # control case
+go get example.net/emptysubdir/... # control case
-! go get -d example.net/emptysubdir/subdir/...
+! go get example.net/emptysubdir/subdir/...
! stderr 'matched no packages'
-stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z'
+stderr '^go: example\.net/emptysubdir/subdir/\.\.\.: module example\.net/emptysubdir/subdir: reading http://.*: 404 Not Found\n\tserver response: 404 page not found\n\z'
# It doesn't make sense to 'go get' a path in the standard library,
# since the standard library necessarily can't have unresolved imports.
@@ -26,8 +26,8 @@ stderr '^go get example\.net/emptysubdir/subdir/\.\.\.: module example\.net/empt
# For that case, we emit a "malformed module path" error message,
# which isn't ideal either.
-! go get -d builtin/... # in GOROOT/src, but contains no packages
-stderr '^go get builtin/...: malformed module path "builtin": missing dot in first path element$'
+! go get builtin/... # in GOROOT/src, but contains no packages
+stderr '^go: builtin/...: malformed module path "builtin": missing dot in first path element$'
-- go.mod --
module example.net/emptysubdir
diff --git a/src/cmd/go/testdata/script/mod_get_patch.txt b/src/cmd/go/testdata/script/mod_get_patch.txt
index 053ef621471ecc6b784ae095573aef6f409352ca..35cc276c5c36839788d2e7dd60be4f122d9d648b 100644
--- a/src/cmd/go/testdata/script/mod_get_patch.txt
+++ b/src/cmd/go/testdata/script/mod_get_patch.txt
@@ -7,8 +7,8 @@ cp go.mod go.mod.orig
# example.net/b@patch refers to the patch for the version of b that was selected
# at the start of 'go get', not the version after applying other changes.
-! go get -d example.net/a@v0.2.0 example.net/b@patch
-stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
+! go get example.net/a@v0.2.0 example.net/b@patch
+stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
cmp go.mod go.mod.orig
@@ -18,8 +18,8 @@ cmp go.mod go.mod.orig
#
# TODO(#42360): Reconsider the change in defaults.
-! go get -d -u=patch example.net/a@v0.2.0 example.net/b
-stderr '^go get: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
+! go get -u=patch example.net/a@v0.2.0 example.net/b
+stderr '^go: example.net/a@v0.2.0 requires example.net/b@v0.2.0, not example.net/b@patch \(v0.1.1\)$'
cmp go.mod go.mod.orig
@@ -27,7 +27,7 @@ cmp go.mod go.mod.orig
# applying other version changes, not the versions that were selected at the start.
# However, it should not patch versions determined by explicit arguments.
-go get -d -u=patch example.net/a@v0.2.0
+go get -u=patch example.net/a@v0.2.0
go list -m all
stdout '^example.net/a v0.2.0 '
stdout '^example.net/b v0.2.1 '
@@ -38,7 +38,7 @@ stdout '^example.net/b v0.2.1 '
cp go.mod.orig go.mod
! go get -u=patch all
-stderr '^go get: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$'
+stderr '^go: example.net/a@v0.1.1 \(matching all@patch\) requires example.net/b@v0.2.0, not example.net/b@v0.1.1 \(matching all@patch\)$'
cmp go.mod go.mod.orig
diff --git a/src/cmd/go/testdata/script/mod_get_patchbound.txt b/src/cmd/go/testdata/script/mod_get_patchbound.txt
index 4fd1ec53e16045fa8def31af242b41e486263144..e4d3c491f45611f45919ef3aca1c3a0d26efe6dd 100644
--- a/src/cmd/go/testdata/script/mod_get_patchbound.txt
+++ b/src/cmd/go/testdata/script/mod_get_patchbound.txt
@@ -5,7 +5,7 @@ go list -m all
stdout '^example.net/a v0.1.0 '
stdout '^example.net/b v0.1.0 '
-go get -d -u=patch example.net/a@v0.2.0
+go get -u=patch example.net/a@v0.2.0
go list -m all
stdout '^example.net/a v0.2.0 '
stdout '^example.net/b v0.1.1 ' # not v0.1.2, which requires …/a v0.3.0.
diff --git a/src/cmd/go/testdata/script/mod_get_patchcycle.txt b/src/cmd/go/testdata/script/mod_get_patchcycle.txt
index d1db56f935ca2ee7c0983bb37fd30c9febf8782a..6600109d2dad9be00b39c17478dec0ec17f1a8f4 100644
--- a/src/cmd/go/testdata/script/mod_get_patchcycle.txt
+++ b/src/cmd/go/testdata/script/mod_get_patchcycle.txt
@@ -6,7 +6,7 @@
# (It used to print v0.1.1 but then silently upgrade to v0.2.0.)
! go get example.net/a@patch
-stderr '^go get: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice.
+stderr '^go: example.net/a@patch \(v0.1.1\) requires example.net/a@v0.2.0, not example.net/a@patch \(v0.1.1\)$' # TODO: A mention of b v0.1.0 would be nice.
-- go.mod --
module example
diff --git a/src/cmd/go/testdata/script/mod_get_patchmod.txt b/src/cmd/go/testdata/script/mod_get_patchmod.txt
index e39d13a0f4184e515e14f7b097f5ddd0d7935491..28277310aa15e5041c137d69dcc2bbdf9cefb242 100644
--- a/src/cmd/go/testdata/script/mod_get_patchmod.txt
+++ b/src/cmd/go/testdata/script/mod_get_patchmod.txt
@@ -1,5 +1,5 @@
# example.net/pkgremoved@v0.1.0 refers to a package.
-go get -d example.net/pkgremoved@v0.1.0
+go get example.net/pkgremoved@v0.1.0
go list example.net/pkgremoved
stdout '^example.net/pkgremoved'
@@ -15,8 +15,8 @@ cp go.mod go.mod.orig
# be constrained to the latest patch of its originally-selected version (v0.1.0),
# not upgraded to the latest patch of the new transitive dependency.
-! go get -d example.net/pkgremoved@patch example.net/other@v0.1.0
-stderr '^go get: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$'
+! go get example.net/pkgremoved@patch example.net/other@v0.1.0
+stderr '^go: example.net/other@v0.1.0 requires example.net/pkgremoved@v0.2.0, not example.net/pkgremoved@patch \(v0.1.1\)$'
cmp go.mod.orig go.mod
@@ -24,19 +24,19 @@ cmp go.mod.orig go.mod
# Package to module ...
-go get -d example.net/pkgremoved@v0.3.0
+go get example.net/pkgremoved@v0.3.0
go list example.net/pkgremoved
stdout 'example.net/pkgremoved'
-go get -d example.net/pkgremoved@patch
+go get example.net/pkgremoved@patch
! go list example.net/pkgremoved
# ... and module to package.
-go get -d example.net/pkgremoved@v0.4.0
+go get example.net/pkgremoved@v0.4.0
! go list example.net/pkgremoved
-go get -d example.net/pkgremoved@patch
+go get example.net/pkgremoved@patch
go list example.net/pkgremoved
stdout 'example.net/pkgremoved'
diff --git a/src/cmd/go/testdata/script/mod_get_patterns.txt b/src/cmd/go/testdata/script/mod_get_patterns.txt
index aee4374dc8a88a5dff020078c9e392178d9e7ba1..891353fd4b9a0b6ef59098a609bbe97ddc2112ae 100644
--- a/src/cmd/go/testdata/script/mod_get_patterns.txt
+++ b/src/cmd/go/testdata/script/mod_get_patterns.txt
@@ -5,29 +5,29 @@ env GO111MODULE=on
# in the build list, we assume the pattern matches a single module
# whose path is a prefix of the part of the pattern before "...".
cp go.mod.orig go.mod
-go get -d rsc.io/quote/...
+go get rsc.io/quote/...
grep 'require rsc.io/quote' go.mod
cp go.mod.orig go.mod
-! go get -d rsc.io/quote/x...
-stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...'
+! go get rsc.io/quote/x...
+stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x...'
! grep 'require rsc.io/quote' go.mod
-! go get -d rsc.io/quote/x/...
-stderr 'go get: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...'
+! go get rsc.io/quote/x/...
+stderr 'go: module rsc.io/quote@upgrade found \(v1.5.2\), but does not contain packages matching rsc.io/quote/x/...'
! grep 'require rsc.io/quote' go.mod
# If a pattern matches no packages within a module, the module should not
# be upgraded, even if the module path is a prefix of the pattern.
cp go.mod.orig go.mod
go mod edit -require example.com/nest@v1.0.0
-go get -d example.com/nest/sub/y...
+go get example.com/nest/sub/y...
grep 'example.com/nest/sub v1.0.0' go.mod
grep 'example.com/nest v1.0.0' go.mod
# However, if the pattern matches the module path itself, the module
# should be upgraded even if it contains no matching packages.
-go get -d example.com/n...t
+go get example.com/n...t
grep 'example.com/nest v1.1.0' go.mod
grep 'example.com/nest/sub v1.0.0' go.mod
diff --git a/src/cmd/go/testdata/script/mod_get_pkgtags.txt b/src/cmd/go/testdata/script/mod_get_pkgtags.txt
index 0c79ec71b7b41d628d6a412e34816399d4599007..7ad3c3c7c44a4caf8c40c05f8d764580815f587b 100644
--- a/src/cmd/go/testdata/script/mod_get_pkgtags.txt
+++ b/src/cmd/go/testdata/script/mod_get_pkgtags.txt
@@ -12,10 +12,10 @@ stderr '^module example\.net/cmd provides package example\.net/cmd/tool and is r
go mod edit -droprequire example.net/tools
-# 'go get -d' makes a best effort to fetch those dependencies, but shouldn't
+# 'go get' makes a best effort to fetch those dependencies, but shouldn't
# error out if dependencies of tag-guarded files are missing.
-go get -d example.net/tools@v0.1.0
+go get example.net/tools@v0.1.0
! stderr 'no Go source files'
! go list example.net/tools
@@ -48,27 +48,27 @@ stderr '^package example.net/tools: build constraints exclude all Go files in .*
# 'go get' should fetch modules whose roots contain test-only packages, but
# without the -t flag shouldn't error out if the test has missing dependencies.
-go get -d example.net/testonly@v0.1.0
+go get example.net/testonly@v0.1.0
# With the -t flag, the test dependencies must resolve successfully.
-! go get -d -t example.net/testonly@v0.1.0
+! go get -t example.net/testonly@v0.1.0
stderr '^example.net/testonly tested by\n\texample.net/testonly\.test imports\n\texample.net/missing: cannot find module providing package example.net/missing$'
-# 'go get -d' should succeed for a module path that does not contain a package,
+# 'go get' should succeed for a module path that does not contain a package,
# but fail for a non-package subdirectory of a module.
-! go get -d example.net/missing/subdir@v0.1.0
-stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
+! go get example.net/missing/subdir@v0.1.0
+stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
-go get -d example.net/missing@v0.1.0
+go get example.net/missing@v0.1.0
# Getting the subdirectory should continue to fail even if the corresponding
# module is already present in the build list.
-! go get -d example.net/missing/subdir@v0.1.0
-stderr '^go get: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
+! go get example.net/missing/subdir@v0.1.0
+stderr '^go: module example.net/missing@v0.1.0 found \(replaced by ./missing\), but does not contain package example.net/missing/subdir$'
-- go.mod --
diff --git a/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt b/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt
index be3db42d1d9c1f104c78e0ba0c92c90b49288300..06e2fc686029184d8b15cfa2f0b4e39edb8aad7f 100644
--- a/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt
+++ b/src/cmd/go/testdata/script/mod_get_prefer_incompatible.txt
@@ -8,7 +8,7 @@ cmp go.mod.orig go.mod
grep '^example.com/incompatiblewithsub v2\.0\.0\+incompatible' go.sum
! grep '^example.com/incompatiblewithsub v1.0.0' go.sum
-go get -d example.com/incompatiblewithsub/sub
+go get example.com/incompatiblewithsub/sub
cmp go.mod.orig go.mod
! grep '^example.com/incompatiblewithsub v1.0.0' go.sum
diff --git a/src/cmd/go/testdata/script/mod_get_promote_implicit.txt b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt
index 9eec2013210350fdd7b4234eeea6160abaa70ec7..03f6810e354a0a3e56a112a424b499ffdb7d1fcb 100644
--- a/src/cmd/go/testdata/script/mod_get_promote_implicit.txt
+++ b/src/cmd/go/testdata/script/mod_get_promote_implicit.txt
@@ -12,7 +12,7 @@ stderr '^package m/use-indirect imports indirect-with-pkg from implicitly requir
# NOTE: the hint recommends getting the imported package (tested below) since
# it's more obvious and doesn't require -d. However, that adds an '// indirect'
# comment on the requirement.
-go get -d m/use-indirect
+go get m/use-indirect
cmp go.mod go.mod.use
cp go.mod.orig go.mod
@@ -21,7 +21,7 @@ cp go.mod.orig go.mod
# know they're needed by the main module. See #43131 for the rationale.
# The hint above recommends this because it's more obvious usage and doesn't
# require the -d flag.
-go get -d indirect-with-pkg indirect-without-pkg
+go get indirect-with-pkg indirect-without-pkg
cmp go.mod go.mod.indirect
-- go.mod.orig --
diff --git a/src/cmd/go/testdata/script/mod_get_pseudo.txt b/src/cmd/go/testdata/script/mod_get_pseudo.txt
index 582837a1665e28aaa0b2fd4e4ea83e92d3ca50b9..b964ae448453b9860fa1d854109110e975649b7c 100644
--- a/src/cmd/go/testdata/script/mod_get_pseudo.txt
+++ b/src/cmd/go/testdata/script/mod_get_pseudo.txt
@@ -9,69 +9,69 @@ env GOSUMDB=off
# We can resolve the @master branch without unshallowing the local repository
# (even with older gits), so try that before we do anything else.
# (This replicates https://golang.org/issue/26713 with git 2.7.4.)
-go get -d github.com/rsc/legacytest@master
+go get github.com/rsc/legacytest@master
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$'
# get should include incompatible tags in "latest" calculation.
go mod edit -droprequire github.com/rsc/legacytest
-go get -d github.com/rsc/legacytest@latest
+go get github.com/rsc/legacytest@latest
go list
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$'
# v2.0.1-0.pseudo+incompatible
-go get -d ...test@7303f77
+go get ...test@7303f77
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.1-0\.\d{14}-7303f7796364\+incompatible$'
# v2.0.0+incompatible by tag+incompatible
-go get -d ...test@v2.0.0+incompatible
+go get ...test@v2.0.0+incompatible
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$'
# v2.0.0+incompatible by tag
-go get -d ...test@v2.0.0
+go get ...test@v2.0.0
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$'
# v2.0.0+incompatible by hash (back on master)
-go get -d ...test@d7ae1e4
+go get ...test@d7ae1e4
go list -m all
stdout '^github.com/rsc/legacytest v2\.0\.0\+incompatible$'
# v1.2.1-0.pseudo
-go get -d ...test@d2d4c3e
+go get ...test@d2d4c3e
go list -m all
stdout '^github.com/rsc/legacytest v1\.2\.1-0\.\d{14}-d2d4c3ea6623$'
# v1.2.0
-go get -d ...test@9f6f860
+go get ...test@9f6f860
go list -m all
stdout '^github.com/rsc/legacytest v1\.2\.0$'
# v1.1.0-pre.0.pseudo
-go get -d ...test@fb3c628
+go get ...test@fb3c628
go list -m all
stdout '^github.com/rsc/legacytest v1\.1\.0-pre\.0\.\d{14}-fb3c628075e3$'
# v1.1.0-pre (no longer on master)
-go get -d ...test@731e3b1
+go get ...test@731e3b1
go list -m all
stdout '^github.com/rsc/legacytest v1\.1\.0-pre$'
# v1.0.1-0.pseudo
-go get -d ...test@fa4f5d6
+go get ...test@fa4f5d6
go list -m all
stdout '^github.com/rsc/legacytest v1\.0\.1-0\.\d{14}-fa4f5d6a71c6$'
# v1.0.0
-go get -d ...test@7fff7f3
+go get ...test@7fff7f3
go list -m all
stdout '^github.com/rsc/legacytest v1\.0\.0$'
# v0.0.0-pseudo
-go get -d ...test@52853eb
+go get ...test@52853eb
go list -m all
stdout '^github.com/rsc/legacytest v0\.0\.0-\d{14}-52853eb7b552$'
diff --git a/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt b/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt
index 0fbd041f8603fd57a3b94e09008072431a56e285..d085f4fa3c8e6b2319bce5ed94c9b569f58d3ded 100644
--- a/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt
+++ b/src/cmd/go/testdata/script/mod_get_pseudo_other_branch.txt
@@ -17,7 +17,7 @@ env GOSUMDB=off
# The pseudo-version hence sorts immediately after v0.2.2 rather
# than v0.2.1, even though the v0.2.2 tag is not on master.
-go get -d vcs-test.golang.org/git/tagtests.git@master
+go get vcs-test.golang.org/git/tagtests.git@master
go list -m all
stdout '^vcs-test.golang.org/git/tagtests.git v0.2.3-0\.'
diff --git a/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt b/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt
index b78e6e644f7622a3d22dd4333672c7f3731dcf48..8e6cd907f183495842855232179f2ed9f16e3bb1 100644
--- a/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt
+++ b/src/cmd/go/testdata/script/mod_get_pseudo_prefix.txt
@@ -16,11 +16,11 @@ env GOSUMDB=off
#
# The pseudo-version is based on sub/v0.0.10, since v0.2.0 doesn't
# contain the prefix.
-go get -d vcs-test.golang.org/git/prefixtagtests.git/sub
+go get vcs-test.golang.org/git/prefixtagtests.git/sub
go list -m all
stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.10$'
-go get -d -u vcs-test.golang.org/git/prefixtagtests.git/sub@master
+go get -u vcs-test.golang.org/git/prefixtagtests.git/sub@master
go list -m all
stdout '^vcs-test.golang.org/git/prefixtagtests.git/sub v0.0.11-0\.'
diff --git a/src/cmd/go/testdata/script/mod_get_replaced.txt b/src/cmd/go/testdata/script/mod_get_replaced.txt
index d97f3f1a40117242fba478162505e0abb9d6eb97..b1fc8b80563307cb948b50361507275bd8b578ff 100644
--- a/src/cmd/go/testdata/script/mod_get_replaced.txt
+++ b/src/cmd/go/testdata/script/mod_get_replaced.txt
@@ -6,7 +6,7 @@ env oldGOPROXY=$GOPROXY
# 'go get' should resolve it to the minimum valid pseudo-version.
go mod edit -replace=example.com/x=./x
-go get -d example.com/x
+go get example.com/x
go list -m example.com/x
stdout '^example.com/x v0.0.0-00010101000000-000000000000 '
@@ -15,11 +15,11 @@ stdout '^example.com/x v0.0.0-00010101000000-000000000000 '
go mod edit -replace=example.com/x@v0.1.0=./x
go mod edit -replace=example.com/x@v0.2.0=./x
-go get -d example.com/x
+go get example.com/x
go list -m example.com/x
stdout '^example.com/x v0.2.0 '
-go get -d example.com/x@