diff --git a/README.md b/README.md
index 829fe777b9..e40f3aa0a1 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ Go is the work of thousands of contributors. We appreciate your help!
To contribute, please read the contribution guidelines at https://go.dev/doc/contribute.
Note that the Go project uses the issue tracker for bug reports and
-proposals only. See https://golang.org/wiki/Questions for a list of
+proposals only. See https://go.dev/wiki/Questions for a list of
places to ask questions about the Go language.
[rf]: https://reneefrench.blogspot.com/
diff --git a/SECURITY.md b/SECURITY.md
index 9e92e8b1ea..ab608f3af5 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,12 +2,12 @@
## Supported Versions
-We support the past two Go releases (for example, Go 1.12.x and Go 1.13.x).
+We support the past two Go releases (for example, Go 1.17.x and Go 1.18.x when Go 1.18.x is the latest stable release).
-See https://golang.org/wiki/Go-Release-Cycle and in particular the
-[Release Maintenance](https://github.com/golang/go/wiki/Go-Release-Cycle#release-maintenance)
+See https://go.dev/wiki/Go-Release-Cycle and in particular the
+[Release Maintenance](https://go.dev/wiki/Go-Release-Cycle#release-maintenance)
part of that page.
## Reporting a Vulnerability
-See https://golang.org/security for how to report a vulnerability.
+See https://go.dev/security for how to report a vulnerability.
diff --git a/api/next/30715.txt b/api/next/30715.txt
new file mode 100644
index 0000000000..077a8d136f
--- /dev/null
+++ b/api/next/30715.txt
@@ -0,0 +1,3 @@
+pkg net/http, type MaxBytesError struct #30715
+pkg net/http, type MaxBytesError struct, Limit int64 #30715
+pkg net/http, method (*MaxBytesError) Error() string #30715
diff --git a/api/next/50599.txt b/api/next/50599.txt
new file mode 100644
index 0000000000..be271ea5e4
--- /dev/null
+++ b/api/next/50599.txt
@@ -0,0 +1 @@
+pkg os/exec, method (*Cmd) Environ() []string #50599
diff --git a/api/next/51684.txt b/api/next/51684.txt
new file mode 100644
index 0000000000..b8a0645256
--- /dev/null
+++ b/api/next/51684.txt
@@ -0,0 +1,2 @@
+pkg regexp/syntax, const ErrNestingDepth = "expression nests too deeply" #51684
+pkg regexp/syntax, const ErrNestingDepth ErrorCode #51684
diff --git a/api/next/regexpdepth.txt b/api/next/regexpdepth.txt
deleted file mode 100644
index 9810218560..0000000000
--- a/api/next/regexpdepth.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-pkg regexp/syntax, const ErrInvalidDepth = "invalid nesting depth" #0
-pkg regexp/syntax, const ErrInvalidDepth ErrorCode #0
-
diff --git a/doc/go1.19.html b/doc/go1.19.html
index a813d59cb8..51b5a54e16 100644
--- a/doc/go1.19.html
+++ b/doc/go1.19.html
@@ -92,6 +92,16 @@ Do not send CLs removing the interior tags from such phrases.
TODO: complete this section
diff --git a/doc/go_spec.html b/doc/go_spec.html
index b496e9e48f..b272cb5df6 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
@@ -1278,7 +1278,8 @@ then the File interface is implemented by both S1 and
Every type that is a member of the type set of an interface implements that interface.
Any given type may implement several distinct interfaces.
-For instance, all types implement the empty interface which stands for the set of all types:
+For instance, all types implement the empty interface which stands for the set
+of all (non-interface) types:
@@ -1380,7 +1381,7 @@ definition of an interface's type set as follows:
of its interface elements.
-
The type set of a method specification is the set of types
+
The type set of a method specification is the set of all non-interface types
whose method sets include that method.
@@ -1389,7 +1390,7 @@ definition of an interface's type set as follows:
The type set of a term of the form ~T
- is the set of types whose underlying type is T.
+ is the set of all types whose underlying type is T.
The type set of a union of terms
@@ -1398,6 +1399,15 @@ definition of an interface's type set as follows:
+
+The quantification "the set of all non-interface types" refers not just to all (non-interface)
+types declared in the program at hand, but all possible types in all possible programs, and
+hence is infinite.
+Similarly, given the set of all non-interface types that implement a particular method, the
+intersection of the method sets of those types will contain exactly that method, even if all
+types in the program at hand always pair that method with another method.
+
+
By construction, an interface's type set never contains an interface type.
diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c
index 8921b7306c..8ecf70f272 100644
--- a/misc/cgo/test/callback_c.c
+++ b/misc/cgo/test/callback_c.c
@@ -3,8 +3,7 @@
// license that can be found in the LICENSE file.
#include
-#include
-#include
+
#include "_cgo_export.h"
void
@@ -31,32 +30,10 @@ IntoC(void)
BackIntoGo();
}
-#ifdef WIN32
-#include
-long long
-mysleep(int seconds) {
- long long st = GetTickCount();
- Sleep(1000 * seconds);
- return st;
-}
-#else
-#include
-long long
-mysleep(int seconds) {
- long long st;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- st = tv.tv_sec * 1000 + tv.tv_usec / 1000;
- sleep(seconds);
- return st;
-}
-#endif
-
-long long
-twoSleep(int n)
+void
+Issue1560InC(void)
{
- BackgroundSleep(n);
- return mysleep(n);
+ Issue1560FromC();
}
void
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index 774277e10d..dee6164354 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -11,6 +11,7 @@ import "testing"
// These wrappers are here for gotest to find.
func Test1328(t *testing.T) { test1328(t) }
+func Test1560(t *testing.T) { test1560(t) }
func Test1635(t *testing.T) { test1635(t) }
func Test3250(t *testing.T) { test3250(t) }
func Test3729(t *testing.T) { test3729(t) }
@@ -89,7 +90,6 @@ func TestLibgcc(t *testing.T) { testLibgcc(t) }
func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) }
func TestNaming(t *testing.T) { testNaming(t) }
func TestPanicFromC(t *testing.T) { testPanicFromC(t) }
-func TestParallelSleep(t *testing.T) { testParallelSleep(t) }
func TestPrintf(t *testing.T) { testPrintf(t) }
func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) }
func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) }
diff --git a/misc/cgo/test/testx.go b/misc/cgo/test/testx.go
index 8ec84a8b22..6a8e97ddf3 100644
--- a/misc/cgo/test/testx.go
+++ b/misc/cgo/test/testx.go
@@ -18,7 +18,6 @@ import (
"sync"
"sync/atomic"
"testing"
- "time"
"unsafe"
)
@@ -30,8 +29,7 @@ extern void doAdd(int, int);
void IntoC(void);
// issue 1560
-// mysleep returns the absolute start time in ms.
-long long mysleep(int seconds);
+extern void Issue1560InC(void);
// twoSleep returns the absolute start time of the first sleep
// in ms.
@@ -183,35 +181,40 @@ func test1328(t *testing.T) {
}
// issue 1560
+// Test that C functions and Go functions run in parallel.
-var sleepDone = make(chan int64)
+var (
+ issue1560 int32
-// parallelSleep returns the absolute difference between the start time
-// of the two sleeps.
-func parallelSleep(n int) int64 {
- t := int64(C.twoSleep(C.int(n))) - <-sleepDone
- if t < 0 {
- return -t
+ issue1560Ch = make(chan bool, 2)
+)
+
+//export Issue1560FromC
+func Issue1560FromC() {
+ for atomic.LoadInt32(&issue1560) != 1 {
+ runtime.Gosched()
}
- return t
+ atomic.AddInt32(&issue1560, 1)
+ for atomic.LoadInt32(&issue1560) != 3 {
+ runtime.Gosched()
+ }
+ issue1560Ch <- true
}
-//export BackgroundSleep
-func BackgroundSleep(n int32) {
- go func() {
- sleepDone <- int64(C.mysleep(C.int(n)))
- }()
+func Issue1560FromGo() {
+ atomic.AddInt32(&issue1560, 1)
+ for atomic.LoadInt32(&issue1560) != 2 {
+ runtime.Gosched()
+ }
+ atomic.AddInt32(&issue1560, 1)
+ issue1560Ch <- true
}
-func testParallelSleep(t *testing.T) {
- sleepSec := 1
- dt := time.Duration(parallelSleep(sleepSec)) * time.Millisecond
- t.Logf("difference in start time for two sleep(%d) is %v", sleepSec, dt)
- // bug used to run sleeps in serial, producing a 2*sleepSec-second delay.
- // we detect if the start times of those sleeps are > 0.5*sleepSec-second.
- if dt >= time.Duration(sleepSec)*time.Second/2 {
- t.Fatalf("parallel %d-second sleeps slept for %f seconds", sleepSec, dt.Seconds())
- }
+func test1560(t *testing.T) {
+ go Issue1560FromGo()
+ go C.Issue1560InC()
+ <-issue1560Ch
+ <-issue1560Ch
}
// issue 2462
diff --git a/src/bootstrap.bash b/src/bootstrap.bash
index 88c080a948..4038eaf942 100755
--- a/src/bootstrap.bash
+++ b/src/bootstrap.bash
@@ -96,7 +96,7 @@ if [ "$BOOTSTRAP_FORMAT" = "mintgz" ]; then
echo "Preparing to generate build system's ${OUTGZ}; cleaning ..."
rm -rf bin/gofmt
rm -rf src/runtime/race/race_*.syso
- rm -rf api test doc misc/cgo/test misc/trace
+ rm -rf api test doc misc/cgo/test
rm -rf pkg/tool/*_*/{addr2line,api,cgo,cover,doc,fix,nm,objdump,pack,pprof,test2json,trace,vet}
rm -rf pkg/*_*/{image,database,cmd}
rm -rf $(find . -type d -name testdata)
diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go
index 7483946fc0..bcc273c78b 100644
--- a/src/bufio/bufio.go
+++ b/src/bufio/bufio.go
@@ -731,13 +731,28 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
// If the count is less than len(s), it also returns an error explaining
// why the write is short.
func (b *Writer) WriteString(s string) (int, error) {
+ var sw io.StringWriter
+ tryStringWriter := true
+
nn := 0
for len(s) > b.Available() && b.err == nil {
- n := copy(b.buf[b.n:], s)
- b.n += n
+ var n int
+ if b.Buffered() == 0 && sw == nil && tryStringWriter {
+ // Check at most once whether b.wr is a StringWriter.
+ sw, tryStringWriter = b.wr.(io.StringWriter)
+ }
+ if b.Buffered() == 0 && tryStringWriter {
+ // Large write, empty buffer, and the underlying writer supports
+ // WriteString: forward the write to the underlying StringWriter.
+ // This avoids an extra copy.
+ n, b.err = sw.WriteString(s)
+ } else {
+ n = copy(b.buf[b.n:], s)
+ b.n += n
+ b.Flush()
+ }
nn += n
s = s[n:]
- b.Flush()
}
if b.err != nil {
return nn, b.err
diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go
index ff3396e946..b3456d2341 100644
--- a/src/bufio/bufio_test.go
+++ b/src/bufio/bufio_test.go
@@ -762,6 +762,67 @@ func TestWriteString(t *testing.T) {
}
}
+func TestWriteStringStringWriter(t *testing.T) {
+ const BufSize = 8
+ {
+ tw := &teststringwriter{}
+ b := NewWriterSize(tw, BufSize)
+ b.WriteString("1234")
+ tw.check(t, "", "")
+ b.WriteString("56789012") // longer than BufSize
+ tw.check(t, "12345678", "") // but not enough (after filling the partially-filled buffer)
+ b.Flush()
+ tw.check(t, "123456789012", "")
+ }
+ {
+ tw := &teststringwriter{}
+ b := NewWriterSize(tw, BufSize)
+ b.WriteString("123456789") // long string, empty buffer:
+ tw.check(t, "", "123456789") // use WriteString
+ }
+ {
+ tw := &teststringwriter{}
+ b := NewWriterSize(tw, BufSize)
+ b.WriteString("abc")
+ tw.check(t, "", "")
+ b.WriteString("123456789012345") // long string, non-empty buffer
+ tw.check(t, "abc12345", "6789012345") // use Write and then WriteString since the remaining part is still longer than BufSize
+ }
+ {
+ tw := &teststringwriter{}
+ b := NewWriterSize(tw, BufSize)
+ b.Write([]byte("abc")) // same as above, but use Write instead of WriteString
+ tw.check(t, "", "")
+ b.WriteString("123456789012345")
+ tw.check(t, "abc12345", "6789012345") // same as above
+ }
+}
+
+type teststringwriter struct {
+ write string
+ writeString string
+}
+
+func (w *teststringwriter) Write(b []byte) (int, error) {
+ w.write += string(b)
+ return len(b), nil
+}
+
+func (w *teststringwriter) WriteString(s string) (int, error) {
+ w.writeString += s
+ return len(s), nil
+}
+
+func (w *teststringwriter) check(t *testing.T, write, writeString string) {
+ t.Helper()
+ if w.write != write {
+ t.Errorf("write: expected %q, got %q", write, w.write)
+ }
+ if w.writeString != writeString {
+ t.Errorf("writeString: expected %q, got %q", writeString, w.writeString)
+ }
+}
+
func TestBufferFull(t *testing.T) {
const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party"
buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize)
diff --git a/src/cmd/compile/abi-internal.md b/src/cmd/compile/abi-internal.md
index be47e9a31b..14464ed904 100644
--- a/src/cmd/compile/abi-internal.md
+++ b/src/cmd/compile/abi-internal.md
@@ -32,19 +32,19 @@ specification](/doc/go_spec.html#Size_and_alignment_guarantees).
Those that aren't guaranteed may change in future versions of Go (for
example, we've considered changing the alignment of int64 on 32-bit).
-| Type | 64-bit | | 32-bit | |
-| --- | --- | --- | --- | --- |
-| | Size | Align | Size | Align |
-| bool, uint8, int8 | 1 | 1 | 1 | 1 |
-| uint16, int16 | 2 | 2 | 2 | 2 |
-| uint32, int32 | 4 | 4 | 4 | 4 |
-| uint64, int64 | 8 | 8 | 8 | 4 |
-| int, uint | 8 | 8 | 4 | 4 |
-| float32 | 4 | 4 | 4 | 4 |
-| float64 | 8 | 8 | 8 | 4 |
-| complex64 | 8 | 4 | 8 | 4 |
-| complex128 | 16 | 8 | 16 | 4 |
-| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 |
+| Type | 64-bit | | 32-bit | |
+|-----------------------------|--------|-------|--------|-------|
+| | Size | Align | Size | Align |
+| bool, uint8, int8 | 1 | 1 | 1 | 1 |
+| uint16, int16 | 2 | 2 | 2 | 2 |
+| uint32, int32 | 4 | 4 | 4 | 4 |
+| uint64, int64 | 8 | 8 | 8 | 4 |
+| int, uint | 8 | 8 | 4 | 4 |
+| float32 | 4 | 4 | 4 | 4 |
+| float64 | 8 | 8 | 8 | 4 |
+| complex64 | 8 | 4 | 8 | 4 |
+| complex128 | 16 | 8 | 16 | 4 |
+| uintptr, *T, unsafe.Pointer | 8 | 8 | 4 | 4 |
The types `byte` and `rune` are aliases for `uint8` and `int32`,
respectively, and hence have the same size and alignment as these
diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go
index ef7fa86749..b8862f62cf 100644
--- a/src/cmd/compile/doc.go
+++ b/src/cmd/compile/doc.go
@@ -219,11 +219,13 @@ calling the function.
//go:uintptrescapes
The //go:uintptrescapes directive must be followed by a function declaration.
-It specifies that the function's uintptr arguments may be pointer values
-that have been converted to uintptr and must be treated as such by the
-garbage collector. The conversion from pointer to uintptr must appear in
-the argument list of any call to this function. This directive is necessary
-for some low-level system call implementations and should be avoided otherwise.
+It specifies that the function's uintptr arguments may be pointer values that
+have been converted to uintptr and must be on the heap and kept alive for the
+duration of the call, even though from the types alone it would appear that the
+object is no longer needed during the call. The conversion from pointer to
+uintptr must appear in the argument list of any call to this function. This
+directive is necessary for some low-level system call implementations and
+should be avoided otherwise.
//go:noinline
diff --git a/src/cmd/compile/internal/compare/compare.go b/src/cmd/compile/internal/compare/compare.go
new file mode 100644
index 0000000000..c0017b1b72
--- /dev/null
+++ b/src/cmd/compile/internal/compare/compare.go
@@ -0,0 +1,272 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package compare contains code for generating comparison
+// routines for structs, strings and interfaces.
+package compare
+
+import (
+ "cmd/compile/internal/base"
+ "cmd/compile/internal/ir"
+ "cmd/compile/internal/typecheck"
+ "cmd/compile/internal/types"
+ "fmt"
+ "math/bits"
+ "sort"
+)
+
+// IsRegularMemory reports whether t can be compared/hashed as regular memory.
+func IsRegularMemory(t *types.Type) bool {
+ a, _ := types.AlgType(t)
+ return a == types.AMEM
+}
+
+// Memrun finds runs of struct fields for which memory-only algs are appropriate.
+// t is the parent struct type, and start is the field index at which to start the run.
+// size is the length in bytes of the memory included in the run.
+// next is the index just after the end of the memory run.
+func Memrun(t *types.Type, start int) (size int64, next int) {
+ next = start
+ for {
+ next++
+ if next == t.NumFields() {
+ break
+ }
+ // Stop run after a padded field.
+ if types.IsPaddedField(t, next-1) {
+ break
+ }
+ // Also, stop before a blank or non-memory field.
+ if f := t.Field(next); f.Sym.IsBlank() || !IsRegularMemory(f.Type) {
+ break
+ }
+ // For issue 46283, don't combine fields if the resulting load would
+ // require a larger alignment than the component fields.
+ if base.Ctxt.Arch.Alignment > 1 {
+ align := t.Alignment()
+ if off := t.Field(start).Offset; off&(align-1) != 0 {
+ // Offset is less aligned than the containing type.
+ // Use offset to determine alignment.
+ align = 1 << uint(bits.TrailingZeros64(uint64(off)))
+ }
+ size := t.Field(next).End() - t.Field(start).Offset
+ if size > align {
+ break
+ }
+ }
+ }
+ return t.Field(next-1).End() - t.Field(start).Offset, next
+}
+
+// EqCanPanic reports whether == on type t could panic (has an interface somewhere).
+// t must be comparable.
+func EqCanPanic(t *types.Type) bool {
+ switch t.Kind() {
+ default:
+ return false
+ case types.TINTER:
+ return true
+ case types.TARRAY:
+ return EqCanPanic(t.Elem())
+ case types.TSTRUCT:
+ for _, f := range t.FieldSlice() {
+ if !f.Sym.IsBlank() && EqCanPanic(f.Type) {
+ return true
+ }
+ }
+ return false
+ }
+}
+
+// EqStruct compares two structs np and nq for equality.
+// It works by building a list of boolean conditions to satisfy.
+// Conditions must be evaluated in the returned order and
+// properly short circuited by the caller.
+func EqStruct(t *types.Type, np, nq ir.Node) []ir.Node {
+ // The conditions are a list-of-lists. Conditions are reorderable
+ // within each inner list. The outer lists must be evaluated in order.
+ var conds [][]ir.Node
+ conds = append(conds, []ir.Node{})
+ and := func(n ir.Node) {
+ i := len(conds) - 1
+ conds[i] = append(conds[i], n)
+ }
+
+ // Walk the struct using memequal for runs of AMEM
+ // and calling specific equality tests for the others.
+ for i, fields := 0, t.FieldSlice(); i < len(fields); {
+ f := fields[i]
+
+ // Skip blank-named fields.
+ if f.Sym.IsBlank() {
+ i++
+ continue
+ }
+
+ // Compare non-memory fields with field equality.
+ if !IsRegularMemory(f.Type) {
+ if EqCanPanic(f.Type) {
+ // Enforce ordering by starting a new set of reorderable conditions.
+ conds = append(conds, []ir.Node{})
+ }
+ p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym)
+ q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym)
+ switch {
+ case f.Type.IsString():
+ eqlen, eqmem := EqString(p, q)
+ and(eqlen)
+ and(eqmem)
+ default:
+ and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q))
+ }
+ if EqCanPanic(f.Type) {
+ // Also enforce ordering after something that can panic.
+ conds = append(conds, []ir.Node{})
+ }
+ i++
+ continue
+ }
+
+ // Find maximal length run of memory-only fields.
+ size, next := Memrun(t, i)
+
+ // TODO(rsc): All the calls to newname are wrong for
+ // cross-package unexported fields.
+ if s := fields[i:next]; len(s) <= 2 {
+ // Two or fewer fields: use plain field equality.
+ for _, f := range s {
+ and(eqfield(np, nq, ir.OEQ, f.Sym))
+ }
+ } else {
+ // More than two fields: use memequal.
+ cc := eqmem(np, nq, f.Sym, size)
+ and(cc)
+ }
+ i = next
+ }
+
+ // Sort conditions to put runtime calls last.
+ // Preserve the rest of the ordering.
+ var flatConds []ir.Node
+ for _, c := range conds {
+ isCall := func(n ir.Node) bool {
+ return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC
+ }
+ sort.SliceStable(c, func(i, j int) bool {
+ return !isCall(c[i]) && isCall(c[j])
+ })
+ flatConds = append(flatConds, c...)
+ }
+ return flatConds
+}
+
+// EqString returns the nodes
+//
+// len(s) == len(t)
+//
+// and
+//
+// memequal(s.ptr, t.ptr, len(s))
+//
+// which can be used to construct string equality comparison.
+// eqlen must be evaluated before eqmem, and shortcircuiting is required.
+func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
+ s = typecheck.Conv(s, types.Types[types.TSTRING])
+ t = typecheck.Conv(t, types.Types[types.TSTRING])
+ sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
+ tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t)
+ slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR])
+ tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR])
+
+ fn := typecheck.LookupRuntime("memequal")
+ fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
+ 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)
+ cmp.SetType(types.Types[types.TBOOL])
+ return cmp, call
+}
+
+// EqInterface returns the nodes
+//
+// s.tab == t.tab (or s.typ == t.typ, as appropriate)
+//
+// and
+//
+// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
+//
+// which can be used to construct interface equality comparison.
+// eqtab must be evaluated before eqdata, and shortcircuiting is required.
+func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
+ if !types.Identical(s.Type(), t.Type()) {
+ base.Fatalf("EqInterface %v %v", s.Type(), t.Type())
+ }
+ // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
+ // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
+ var fn ir.Node
+ if s.Type().IsEmptyInterface() {
+ fn = typecheck.LookupRuntime("efaceeq")
+ } else {
+ fn = typecheck.LookupRuntime("ifaceeq")
+ }
+
+ stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s)
+ ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t)
+ sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s)
+ tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t)
+ sdata.SetType(types.Types[types.TUNSAFEPTR])
+ tdata.SetType(types.Types[types.TUNSAFEPTR])
+ sdata.SetTypecheck(1)
+ tdata.SetTypecheck(1)
+
+ 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)
+ cmp.SetType(types.Types[types.TBOOL])
+ return cmp, call
+}
+
+// eqfield returns the node
+//
+// p.field == q.field
+func eqfield(p ir.Node, q ir.Node, op ir.Op, field *types.Sym) ir.Node {
+ nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
+ ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
+ ne := ir.NewBinaryExpr(base.Pos, op, nx, ny)
+ return ne
+}
+
+// eqmem returns the node
+//
+// memequal(&p.field, &q.field, size])
+func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
+ nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
+ ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))
+
+ fn, needsize := eqmemfunc(size, nx.Type().Elem())
+ call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
+ call.Args.Append(nx)
+ call.Args.Append(ny)
+ if needsize {
+ call.Args.Append(ir.NewInt(size))
+ }
+
+ return call
+}
+
+func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) {
+ switch size {
+ default:
+ fn = typecheck.LookupRuntime("memequal")
+ needsize = true
+ case 1, 2, 4, 8, 16:
+ buf := fmt.Sprintf("memequal%d", int(size)*8)
+ fn = typecheck.LookupRuntime(buf)
+ }
+
+ fn = typecheck.SubstArgTypes(fn, t, t)
+ return fn, needsize
+}
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index 4408a531ec..05fbe58bbc 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -422,8 +422,6 @@ func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
}
if fn.Pragma&ir.UintptrEscapes != 0 {
- fn.Pragma |= ir.UintptrKeepAlive
-
if f.Type.IsUintptr() {
if diagnose {
base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index 8c2ea49c8f..486a6ad319 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -120,6 +120,17 @@ func CanInline(fn *ir.Func) {
return
}
+ // If marked as "go:uintptrkeepalive", don't inline, since the
+ // keep alive information is lost during inlining.
+ //
+ // TODO(prattmic): This is handled on calls during escape analysis,
+ // which is after inlining. Move prior to inlining so the keep-alive is
+ // maintained after inlining.
+ if fn.Pragma&ir.UintptrKeepAlive != 0 {
+ reason = "marked as having a keep-alive uintptr argument"
+ return
+ }
+
// If marked as "go:uintptrescapes", don't inline, since the
// escape information is lost during inlining.
if fn.Pragma&ir.UintptrEscapes != 0 {
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 9ccb8e3c30..0d91d17344 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -459,7 +459,7 @@ const (
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)
+ UintptrKeepAlive // pointers converted to uintptr must be kept alive
UintptrEscapes // pointers converted to uintptr escape
// Runtime-only func pragmas.
diff --git a/src/cmd/compile/internal/noder/decl.go b/src/cmd/compile/internal/noder/decl.go
index f985648c66..91a90d9e09 100644
--- a/src/cmd/compile/internal/noder/decl.go
+++ b/src/cmd/compile/internal/noder/decl.go
@@ -125,8 +125,26 @@ func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
}
}
- if decl.Body != nil && fn.Pragma&ir.Noescape != 0 {
- base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations")
+ if decl.Body != nil {
+ if fn.Pragma&ir.Noescape != 0 {
+ base.ErrorfAt(fn.Pos(), "can only use //go:noescape with external func implementations")
+ }
+ if (fn.Pragma&ir.UintptrKeepAlive != 0 && fn.Pragma&ir.UintptrEscapes == 0) && fn.Pragma&ir.Nosplit == 0 {
+ // Stack growth can't handle uintptr arguments that may
+ // be pointers (as we don't know which are pointers
+ // when creating the stack map). Thus uintptrkeepalive
+ // functions (and all transitive callees) must be
+ // nosplit.
+ //
+ // N.B. uintptrescapes implies uintptrkeepalive but it
+ // is OK since the arguments must escape to the heap.
+ //
+ // TODO(prattmic): Add recursive nosplit check of callees.
+ // TODO(prattmic): Functions with no body (i.e.,
+ // assembly) must also be nosplit, but we can't check
+ // that here.
+ base.ErrorfAt(fn.Pos(), "go:uintptrkeepalive requires go:nosplit")
+ }
}
if decl.Name.Value == "init" && decl.Recv == nil {
diff --git a/src/cmd/compile/internal/noder/lex.go b/src/cmd/compile/internal/noder/lex.go
index 66a56a50ec..cef0f082ca 100644
--- a/src/cmd/compile/internal/noder/lex.go
+++ b/src/cmd/compile/internal/noder/lex.go
@@ -30,6 +30,7 @@ const (
ir.NoCheckPtr |
ir.RegisterParams | // TODO(register args) remove after register abi is working
ir.CgoUnsafeArgs |
+ ir.UintptrKeepAlive |
ir.UintptrEscapes |
ir.Systemstack |
ir.Nowritebarrier |
@@ -67,19 +68,13 @@ func pragmaFlag(verb string) ir.PragmaFlag {
return ir.Yeswritebarrierrec
case "go:cgo_unsafe_args":
return ir.CgoUnsafeArgs | ir.NoCheckPtr // implies NoCheckPtr (see #34968)
+ case "go:uintptrkeepalive":
+ return ir.UintptrKeepAlive
case "go:uintptrescapes":
- // For the next function declared in the file
- // any uintptr arguments may be pointer values
- // converted to uintptr. This directive
- // ensures that the referenced allocated
- // object, if any, is retained and not moved
- // until the call completes, even though from
- // the types alone it would appear that the
- // object is no longer needed during the
- // call. The conversion to uintptr must appear
- // in the argument list.
- // Used in syscall/dll_windows.go.
- return ir.UintptrEscapes
+ // This directive extends //go:uintptrkeepalive by forcing
+ // uintptr arguments to escape to the heap, which makes stack
+ // growth safe.
+ return ir.UintptrEscapes | ir.UintptrKeepAlive // implies UintptrKeepAlive
case "go:registerparams": // TODO(register args) remove after register abi is working
return ir.RegisterParams
case "go:notinheap":
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index cc5610acda..9a42b5afd1 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -340,6 +340,9 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P
if !base.Flag.CompilingRuntime && flag&runtimePragmas != 0 {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s only allowed in runtime", verb)})
}
+ if flag == ir.UintptrKeepAlive && !base.Flag.Std {
+ p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is only allowed in the standard library", verb)})
+ }
if flag == 0 && !allowedStdPragmas[verb] && base.Flag.Std {
p.error(syntax.Error{Pos: pos, Msg: fmt.Sprintf("//%s is not allowed in the standard library", verb)})
}
diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go
index c5c346b784..0fb162d381 100644
--- a/src/cmd/compile/internal/noder/writer.go
+++ b/src/cmd/compile/internal/noder/writer.go
@@ -742,6 +742,22 @@ func (w *writer) funcExt(obj *types2.Func) {
if pragma&ir.Noescape != 0 {
w.p.errorf(decl, "can only use //go:noescape with external func implementations")
}
+ if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 {
+ // Stack growth can't handle uintptr arguments that may
+ // be pointers (as we don't know which are pointers
+ // when creating the stack map). Thus uintptrkeepalive
+ // functions (and all transitive callees) must be
+ // nosplit.
+ //
+ // N.B. uintptrescapes implies uintptrkeepalive but it
+ // is OK since the arguments must escape to the heap.
+ //
+ // TODO(prattmic): Add recursive nosplit check of callees.
+ // TODO(prattmic): Functions with no body (i.e.,
+ // assembly) must also be nosplit, but we can't check
+ // that here.
+ w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")
+ }
} else {
if base.Flag.Complete || decl.Name.Value == "init" {
// Linknamed functions are allowed to have no body. Hopefully
diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go
index 9fe90da0fe..de23387ca1 100644
--- a/src/cmd/compile/internal/reflectdata/alg.go
+++ b/src/cmd/compile/internal/reflectdata/alg.go
@@ -6,10 +6,9 @@ package reflectdata
import (
"fmt"
- "math/bits"
- "sort"
"cmd/compile/internal/base"
+ "cmd/compile/internal/compare"
"cmd/compile/internal/ir"
"cmd/compile/internal/objw"
"cmd/compile/internal/typecheck"
@@ -17,32 +16,6 @@ import (
"cmd/internal/obj"
)
-// isRegularMemory reports whether t can be compared/hashed as regular memory.
-func isRegularMemory(t *types.Type) bool {
- a, _ := types.AlgType(t)
- return a == types.AMEM
-}
-
-// eqCanPanic reports whether == on type t could panic (has an interface somewhere).
-// t must be comparable.
-func eqCanPanic(t *types.Type) bool {
- switch t.Kind() {
- default:
- return false
- case types.TINTER:
- return true
- case types.TARRAY:
- return eqCanPanic(t.Elem())
- case types.TSTRUCT:
- for _, f := range t.FieldSlice() {
- if !f.Sym.IsBlank() && eqCanPanic(f.Type) {
- return true
- }
- }
- return false
- }
-}
-
// AlgType returns the fixed-width AMEMxx variants instead of the general
// AMEM kind when possible.
func AlgType(t *types.Type) types.AlgKind {
@@ -206,7 +179,7 @@ func genhash(t *types.Type) *obj.LSym {
}
// Hash non-memory fields with appropriate hash function.
- if !isRegularMemory(f.Type) {
+ if !compare.IsRegularMemory(f.Type) {
hashel := hashfor(f.Type)
call := ir.NewCallExpr(base.Pos, ir.OCALL, hashel, nil)
nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym) // TODO: fields from other packages?
@@ -219,7 +192,7 @@ func genhash(t *types.Type) *obj.LSym {
}
// Otherwise, hash a maximal length run of raw memory.
- size, next := memrun(t, i)
+ size, next := compare.Memrun(t, i)
// h = hashel(&p.first, size, h)
hashel := hashmem(f.Type)
@@ -510,12 +483,12 @@ func geneq(t *types.Type) *obj.LSym {
// Second, check that all the contents match (expensive).
checkAll(3, false, func(pi, qi ir.Node) ir.Node {
// Compare lengths.
- eqlen, _ := EqString(pi, qi)
+ eqlen, _ := compare.EqString(pi, qi)
return eqlen
})
checkAll(1, true, func(pi, qi ir.Node) ir.Node {
// Compare contents.
- _, eqmem := EqString(pi, qi)
+ _, eqmem := compare.EqString(pi, qi)
return eqmem
})
case types.TFLOAT32, types.TFLOAT64:
@@ -532,81 +505,7 @@ func geneq(t *types.Type) *obj.LSym {
}
case types.TSTRUCT:
- // Build a list of conditions to satisfy.
- // The conditions are a list-of-lists. Conditions are reorderable
- // within each inner list. The outer lists must be evaluated in order.
- var conds [][]ir.Node
- conds = append(conds, []ir.Node{})
- and := func(n ir.Node) {
- i := len(conds) - 1
- conds[i] = append(conds[i], n)
- }
-
- // Walk the struct using memequal for runs of AMEM
- // and calling specific equality tests for the others.
- for i, fields := 0, t.FieldSlice(); i < len(fields); {
- f := fields[i]
-
- // Skip blank-named fields.
- if f.Sym.IsBlank() {
- i++
- continue
- }
-
- // Compare non-memory fields with field equality.
- if !isRegularMemory(f.Type) {
- if eqCanPanic(f.Type) {
- // Enforce ordering by starting a new set of reorderable conditions.
- conds = append(conds, []ir.Node{})
- }
- p := ir.NewSelectorExpr(base.Pos, ir.OXDOT, np, f.Sym)
- q := ir.NewSelectorExpr(base.Pos, ir.OXDOT, nq, f.Sym)
- switch {
- case f.Type.IsString():
- eqlen, eqmem := EqString(p, q)
- and(eqlen)
- and(eqmem)
- default:
- and(ir.NewBinaryExpr(base.Pos, ir.OEQ, p, q))
- }
- if eqCanPanic(f.Type) {
- // Also enforce ordering after something that can panic.
- conds = append(conds, []ir.Node{})
- }
- i++
- continue
- }
-
- // Find maximal length run of memory-only fields.
- size, next := memrun(t, i)
-
- // TODO(rsc): All the calls to newname are wrong for
- // cross-package unexported fields.
- if s := fields[i:next]; len(s) <= 2 {
- // Two or fewer fields: use plain field equality.
- for _, f := range s {
- and(eqfield(np, nq, f.Sym))
- }
- } else {
- // More than two fields: use memequal.
- and(eqmem(np, nq, f.Sym, size))
- }
- i = next
- }
-
- // Sort conditions to put runtime calls last.
- // Preserve the rest of the ordering.
- var flatConds []ir.Node
- for _, c := range conds {
- isCall := func(n ir.Node) bool {
- return n.Op() == ir.OCALL || n.Op() == ir.OCALLFUNC
- }
- sort.SliceStable(c, func(i, j int) bool {
- return !isCall(c[i]) && isCall(c[j])
- })
- flatConds = append(flatConds, c...)
- }
-
+ flatConds := compare.EqStruct(t, np, nq)
if len(flatConds) == 0 {
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
} else {
@@ -631,7 +530,7 @@ func geneq(t *types.Type) *obj.LSym {
// return (or goto ret)
fn.Body.Append(ir.NewLabelStmt(base.Pos, neq))
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(false)))
- if eqCanPanic(t) || anyCall(fn) {
+ if compare.EqCanPanic(t) || anyCall(fn) {
// Epilogue is large, so share it with the equal case.
fn.Body.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, ret))
} else {
@@ -680,153 +579,6 @@ func anyCall(fn *ir.Func) bool {
})
}
-// eqfield returns the node
-//
-// p.field == q.field
-func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node {
- nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
- ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
- ne := ir.NewBinaryExpr(base.Pos, ir.OEQ, nx, ny)
- return ne
-}
-
-// EqString returns the nodes
-//
-// len(s) == len(t)
-//
-// and
-//
-// memequal(s.ptr, t.ptr, len(s))
-//
-// which can be used to construct string equality comparison.
-// eqlen must be evaluated before eqmem, and shortcircuiting is required.
-func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
- s = typecheck.Conv(s, types.Types[types.TSTRING])
- t = typecheck.Conv(t, types.Types[types.TSTRING])
- sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
- tptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, t)
- slen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, s), types.Types[types.TUINTPTR])
- tlen := typecheck.Conv(ir.NewUnaryExpr(base.Pos, ir.OLEN, t), types.Types[types.TUINTPTR])
-
- fn := typecheck.LookupRuntime("memequal")
- fn = typecheck.SubstArgTypes(fn, types.Types[types.TUINT8], types.Types[types.TUINT8])
- 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)
- cmp.SetType(types.Types[types.TBOOL])
- return cmp, call
-}
-
-// EqInterface returns the nodes
-//
-// s.tab == t.tab (or s.typ == t.typ, as appropriate)
-//
-// and
-//
-// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
-//
-// which can be used to construct interface equality comparison.
-// eqtab must be evaluated before eqdata, and shortcircuiting is required.
-func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
- if !types.Identical(s.Type(), t.Type()) {
- base.Fatalf("EqInterface %v %v", s.Type(), t.Type())
- }
- // func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
- // func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
- var fn ir.Node
- if s.Type().IsEmptyInterface() {
- fn = typecheck.LookupRuntime("efaceeq")
- } else {
- fn = typecheck.LookupRuntime("ifaceeq")
- }
-
- stab := ir.NewUnaryExpr(base.Pos, ir.OITAB, s)
- ttab := ir.NewUnaryExpr(base.Pos, ir.OITAB, t)
- sdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, s)
- tdata := ir.NewUnaryExpr(base.Pos, ir.OIDATA, t)
- sdata.SetType(types.Types[types.TUNSAFEPTR])
- tdata.SetType(types.Types[types.TUNSAFEPTR])
- sdata.SetTypecheck(1)
- tdata.SetTypecheck(1)
-
- 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)
- cmp.SetType(types.Types[types.TBOOL])
- return cmp, call
-}
-
-// eqmem returns the node
-//
-// memequal(&p.field, &q.field [, size])
-func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
- nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
- ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))
-
- fn, needsize := eqmemfunc(size, nx.Type().Elem())
- call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
- call.Args.Append(nx)
- call.Args.Append(ny)
- if needsize {
- call.Args.Append(ir.NewInt(size))
- }
-
- return call
-}
-
-func eqmemfunc(size int64, t *types.Type) (fn *ir.Name, needsize bool) {
- switch size {
- default:
- fn = typecheck.LookupRuntime("memequal")
- needsize = true
- case 1, 2, 4, 8, 16:
- buf := fmt.Sprintf("memequal%d", int(size)*8)
- fn = typecheck.LookupRuntime(buf)
- }
-
- fn = typecheck.SubstArgTypes(fn, t, t)
- return fn, needsize
-}
-
-// memrun finds runs of struct fields for which memory-only algs are appropriate.
-// t is the parent struct type, and start is the field index at which to start the run.
-// size is the length in bytes of the memory included in the run.
-// next is the index just after the end of the memory run.
-func memrun(t *types.Type, start int) (size int64, next int) {
- next = start
- for {
- next++
- if next == t.NumFields() {
- break
- }
- // Stop run after a padded field.
- if types.IsPaddedField(t, next-1) {
- break
- }
- // Also, stop before a blank or non-memory field.
- if f := t.Field(next); f.Sym.IsBlank() || !isRegularMemory(f.Type) {
- break
- }
- // For issue 46283, don't combine fields if the resulting load would
- // require a larger alignment than the component fields.
- if base.Ctxt.Arch.Alignment > 1 {
- align := t.Alignment()
- if off := t.Field(start).Offset; off&(align-1) != 0 {
- // Offset is less aligned than the containing type.
- // Use offset to determine alignment.
- align = 1 << uint(bits.TrailingZeros64(uint64(off)))
- }
- size := t.Field(next).End() - t.Field(start).Offset
- if size > align {
- break
- }
- }
- }
- return t.Field(next-1).End() - t.Field(start).Offset, next
-}
-
func hashmem(t *types.Type) ir.Node {
sym := ir.Pkgs.Runtime.Lookup("memhash")
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index affc6799ab..9553b0d759 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -14,6 +14,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/bitvec"
+ "cmd/compile/internal/compare"
"cmd/compile/internal/escape"
"cmd/compile/internal/inline"
"cmd/compile/internal/ir"
@@ -728,7 +729,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
if t.Sym() != nil && t.Sym().Name != "" {
tflag |= tflagNamed
}
- if isRegularMemory(t) {
+ if compare.IsRegularMemory(t) {
tflag |= tflagRegularMemory
}
diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go
index f18d526877..a89dcfae52 100644
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -827,7 +827,7 @@ func (p *parser) unaryExpr() Expr {
switch p.tok {
case _Operator, _Star:
switch p.op {
- case Mul, Add, Sub, Not, Xor:
+ case Mul, Add, Sub, Not, Xor, Tilde:
x := new(Operation)
x.pos = p.pos()
x.Op = p.op
@@ -991,7 +991,7 @@ func (p *parser) operand(keep_parens bool) Expr {
case _Func:
pos := p.pos()
p.next()
- _, ftyp := p.funcType("function literal")
+ _, ftyp := p.funcType("function type")
if p.tok == _Lbrace {
p.xnest++
@@ -1499,44 +1499,14 @@ func (p *parser) interfaceType() *InterfaceType {
p.want(_Interface)
p.want(_Lbrace)
p.list("interface type", _Semi, _Rbrace, func() bool {
- switch p.tok {
- case _Name:
- f := p.methodDecl()
- if f.Name == nil {
- f = p.embeddedElem(f)
- }
- typ.MethodList = append(typ.MethodList, f)
- return false
-
- case _Lparen:
- p.syntaxError("cannot parenthesize embedded type")
- f := new(Field)
- f.pos = p.pos()
- p.next()
- f.Type = p.qualifiedName(nil)
- p.want(_Rparen)
- typ.MethodList = append(typ.MethodList, f)
- return false
-
- case _Operator:
- if p.op == Tilde {
- typ.MethodList = append(typ.MethodList, p.embeddedElem(nil))
- return false
- }
-
- default:
- pos := p.pos()
- if t := p.typeOrNil(); t != nil {
- f := new(Field)
- f.pos = pos
- f.Type = t
- typ.MethodList = append(typ.MethodList, p.embeddedElem(f))
- return false
- }
+ var f *Field
+ if p.tok == _Name {
+ f = p.methodDecl()
}
-
- p.syntaxError("expecting method or embedded element")
- p.advance(_Semi, _Rbrace)
+ if f == nil || f.Name == nil {
+ f = p.embeddedElem(f)
+ }
+ typ.MethodList = append(typ.MethodList, f)
return false
})
diff --git a/src/cmd/compile/internal/syntax/testdata/issue48382.go b/src/cmd/compile/internal/syntax/testdata/issue48382.go
index c00fee6f82..7c024a051f 100644
--- a/src/cmd/compile/internal/syntax/testdata/issue48382.go
+++ b/src/cmd/compile/internal/syntax/testdata/issue48382.go
@@ -8,7 +8,8 @@ type _ func /* ERROR function type must have no type parameters */ [ /* ERROR em
type _ func /* ERROR function type must have no type parameters */ [ x /* ERROR missing type constraint */ ]()
type _ func /* ERROR function type must have no type parameters */ [P any]()
-var _ = func /* ERROR function literal must have no type parameters */ [P any]() {}
+var _ = (func /* ERROR function type must have no type parameters */ [P any]())(nil)
+var _ = func /* ERROR function type must have no type parameters */ [P any]() {}
type _ interface{
m /* ERROR interface method must have no type parameters */ [P any]()
diff --git a/src/cmd/compile/internal/syntax/testdata/issue52391.go b/src/cmd/compile/internal/syntax/testdata/issue52391.go
new file mode 100644
index 0000000000..f2098ceadb
--- /dev/null
+++ b/src/cmd/compile/internal/syntax/testdata/issue52391.go
@@ -0,0 +1,17 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type _ interface {
+ int
+ (int)
+ (*int)
+ *([]byte)
+ ~(int)
+ (int) | (string)
+ (int) | ~(string)
+ (/* ERROR unexpected ~ */ ~int)
+ (int /* ERROR unexpected \| */ | /* ERROR unexpected string */ string /* ERROR unexpected \) */ )
+}
diff --git a/src/cmd/compile/internal/syntax/testdata/typeset.go b/src/cmd/compile/internal/syntax/testdata/typeset.go
index 19b74f28ea..fe5c3f45a8 100644
--- a/src/cmd/compile/internal/syntax/testdata/typeset.go
+++ b/src/cmd/compile/internal/syntax/testdata/typeset.go
@@ -65,15 +65,17 @@ 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).
+// The term must be a valid expression (it could be a type incl. a
+// tilde term) but the type-checker will complain.
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
+
+ // These are invalid and the type-checker will complain.
+ _[~t] t
+ _[~t|t] t
+ _[t|~t] t
+ _[~t|~t] t
)
type (
diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go
index 211068e1dc..af66a32085 100644
--- a/src/cmd/compile/internal/test/inl_test.go
+++ b/src/cmd/compile/internal/test/inl_test.go
@@ -128,15 +128,33 @@ func TestIntendedInlining(t *testing.T) {
"ValidRune",
},
"reflect": {
- "Value.CanInt",
- "Value.CanUint",
- "Value.CanFloat",
- "Value.CanComplex",
+ "Value.Bool",
+ "Value.Bytes",
"Value.CanAddr",
- "Value.CanSet",
+ "Value.CanComplex",
+ "Value.CanFloat",
+ "Value.CanInt",
"Value.CanInterface",
+ "Value.CanSet",
+ "Value.CanUint",
+ "Value.Cap",
+ "Value.Complex",
+ "Value.Float",
+ "Value.Int",
+ "Value.Interface",
+ "Value.IsNil",
"Value.IsValid",
+ "Value.Kind",
+ "Value.Len",
"Value.MapRange",
+ "Value.OverflowComplex",
+ "Value.OverflowFloat",
+ "Value.OverflowInt",
+ "Value.OverflowUint",
+ "Value.String",
+ "Value.Type",
+ "Value.Uint",
+ "Value.UnsafeAddr",
"Value.pointer",
"add",
"align",
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
index 95143cbed5..4f28c362c7 100644
--- a/src/cmd/compile/internal/types2/decl.go
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -735,7 +735,7 @@ func (check *Checker) declStmt(list []syntax.Decl) {
top := len(check.delayed)
// iota is the index of the current constDecl within the group
- if first < 0 || list[index-1].(*syntax.ConstDecl).Group != s.Group {
+ if first < 0 || s.Group == nil || list[index-1].(*syntax.ConstDecl).Group != s.Group {
first = index
last = nil
}
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index e0c22f5b03..33d329f82d 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -89,21 +89,11 @@ func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool {
func (check *Checker) overflow(x *operand) {
assert(x.mode == constant_)
- // If the corresponding expression is an operation, use the
- // operator position rather than the start of the expression
- // as error position.
- pos := syntax.StartPos(x.expr)
- what := "" // operator description, if any
- if op, _ := x.expr.(*syntax.Operation); op != nil {
- pos = op.Pos()
- what = opName(op)
- }
-
if x.val.Kind() == constant.Unknown {
// TODO(gri) We should report exactly what went wrong. At the
// moment we don't have the (go/constant) API for that.
// See also TODO in go/constant/value.go.
- check.error(pos, "constant result is not representable")
+ check.error(opPos(x.expr), "constant result is not representable")
return
}
@@ -119,22 +109,37 @@ func (check *Checker) overflow(x *operand) {
// Untyped integer values must not grow arbitrarily.
const prec = 512 // 512 is the constant precision
if x.val.Kind() == constant.Int && constant.BitLen(x.val) > prec {
- check.errorf(pos, "constant %s overflow", what)
+ check.errorf(opPos(x.expr), "constant %s overflow", opName(x.expr))
x.val = constant.MakeUnknown()
}
}
-// opName returns the name of an operation, or the empty string.
-// Only operations that might overflow are handled.
-func opName(e *syntax.Operation) string {
- op := int(e.Op)
- if e.Y == nil {
- if op < len(op2str1) {
- return op2str1[op]
- }
- } else {
- if op < len(op2str2) {
- return op2str2[op]
+// opPos returns the position of the operator if x is an operation;
+// otherwise it returns the start position of x.
+func opPos(x syntax.Expr) syntax.Pos {
+ switch op := x.(type) {
+ case nil:
+ return nopos // don't crash
+ case *syntax.Operation:
+ return op.Pos()
+ default:
+ return syntax.StartPos(x)
+ }
+}
+
+// opName returns the name of the operation if x is an operation
+// that might overflow; otherwise it returns the empty string.
+func opName(x syntax.Expr) string {
+ if e, _ := x.(*syntax.Operation); e != nil {
+ op := int(e.Op)
+ if e.Y == nil {
+ if op < len(op2str1) {
+ return op2str1[op]
+ }
+ } else {
+ if op < len(op2str2) {
+ return op2str2[op]
+ }
}
}
return ""
@@ -203,6 +208,12 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
x.typ = ch.elem
check.hasCallOrRecv = true
return
+
+ case syntax.Tilde:
+ // Provide a better error position and message than what check.op below could do.
+ check.error(e, "cannot use ~ outside of interface or type constraint")
+ x.mode = invalid
+ return
}
if !check.op(unaryOpPredicates, x, e.Op) {
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
index 5c64ecdfc8..5d498b6b2b 100644
--- a/src/cmd/compile/internal/types2/resolver.go
+++ b/src/cmd/compile/internal/types2/resolver.go
@@ -340,7 +340,7 @@ func (check *Checker) collectObjects() {
case *syntax.ConstDecl:
// iota is the index of the current constDecl within the group
- if first < 0 || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
+ if first < 0 || s.Group == nil || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
first = index
last = nil
}
diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go
index f530849a9d..6133e15924 100644
--- a/src/cmd/compile/internal/types2/sizes.go
+++ b/src/cmd/compile/internal/types2/sizes.go
@@ -166,10 +166,11 @@ func (s *StdSizes) Sizeof(T Type) int64 {
// common architecture word sizes and alignments
var gcArchSizes = map[string]*StdSizes{
"386": {4, 4},
- "arm": {4, 4},
- "arm64": {8, 8},
"amd64": {8, 8},
"amd64p32": {4, 8},
+ "arm": {4, 4},
+ "arm64": {8, 8},
+ "loong64": {8, 8},
"mips": {4, 4},
"mipsle": {4, 4},
"mips64": {8, 8},
@@ -188,7 +189,7 @@ var gcArchSizes = map[string]*StdSizes{
// The result is nil if a compiler/architecture pair is not known.
//
// Supported architectures for compiler "gc":
-// "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle",
+// "386", "amd64", "amd64p32", "arm", "arm64", "loong64", "mips", "mipsle",
// "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
func SizesFor(compiler, arch string) Sizes {
var m map[string]*StdSizes
diff --git a/src/cmd/compile/internal/types2/testdata/check/const0.go b/src/cmd/compile/internal/types2/testdata/check/const0.go
index 3cffdf904c..229c248643 100644
--- a/src/cmd/compile/internal/types2/testdata/check/const0.go
+++ b/src/cmd/compile/internal/types2/testdata/check/const0.go
@@ -349,6 +349,25 @@ const _ = unsafe.Sizeof(func() {
assert(iota == 0)
})
+// issue #52438
+const i1 = iota
+const i2 = iota
+const i3 = iota
+
+func _() {
+ assert(i1 == 0)
+ assert(i2 == 0)
+ assert(i3 == 0)
+
+ const i4 = iota
+ const i5 = iota
+ const i6 = iota
+
+ assert(i4 == 0)
+ assert(i5 == 0)
+ assert(i6 == 0)
+}
+
// untyped constants must not get arbitrarily large
const prec = 512 // internal maximum precision for integers
const maxInt = (1<<(prec/2) - 1) * (1<<(prec/2) + 1) // == 1< 0 {
os.Stderr.Write(ee.Stderr)
} else {
- fmt.Fprintf(os.Stderr, err.Error())
+ fmt.Fprintln(os.Stderr, err.Error())
}
}
}
@@ -678,14 +676,24 @@ func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([
// Ping pings to determine scheme to use.
func (v *Cmd) Ping(scheme, repo string) error {
- return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo)
+ // Run the ping command in an arbitrary working directory,
+ // but don't let the current working directory pollute the results.
+ // In module mode, we expect GOMODCACHE to exist and be a safe place for
+ // commands; in GOPATH mode, we expect that to be true of GOPATH/src.
+ dir := cfg.GOMODCACHE
+ if !cfg.ModulesEnabled {
+ dir = filepath.Join(cfg.BuildContext.GOPATH, "src")
+ }
+ os.MkdirAll(dir, 0777) // Ignore errors — if unsuccessful, the command will likely fail.
+
+ return v.runVerboseOnly(dir, v.PingCmd, "scheme", scheme, "repo", repo)
}
// Create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *Cmd) Create(dir, repo string) error {
for _, cmd := range v.CreateCmd {
- if err := v.run(".", cmd, "dir", dir, "repo", repo); err != nil {
+ if err := v.run(filepath.Dir(dir), cmd, "dir", dir, "repo", repo); err != nil {
return err
}
}
diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go
index 76335e9bb1..ac98aa344c 100644
--- a/src/cmd/go/internal/work/buildid.go
+++ b/src/cmd/go/internal/work/buildid.go
@@ -160,7 +160,6 @@ func (b *Builder) toolID(name string) string {
cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
- cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
@@ -219,9 +218,8 @@ func (b *Builder) gccToolID(name, language string) (string, error) {
// compile an empty file on standard input.
cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-")
cmd := exec.Command(cmdline[0], cmdline[1:]...)
- cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
// Force untranslated output so that we see the string "version".
- cmd.Env = append(cmd.Env, "LC_ALL=C")
+ cmd.Env = append(os.Environ(), "LC_ALL=C")
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%s: %v; output: %q", name, err, out)
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 9c9d58b2a1..0b8e5d2330 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -2116,8 +2116,10 @@ func (b *Builder) runOut(a *Action, dir string, env []string, cmdargs ...any) ([
cmd.Stderr = &buf
cleanup := passLongArgsInResponseFiles(cmd)
defer cleanup()
- cmd.Dir = dir
- cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
+ if dir != "." {
+ cmd.Dir = dir
+ }
+ cmd.Env = cmd.Environ() // Pre-allocate with correct PWD.
// Add the TOOLEXEC_IMPORTPATH environment variable for -toolexec tools.
// It doesn't really matter if -toolexec isn't being used.
@@ -2606,8 +2608,7 @@ func (b *Builder) gccSupportsFlag(compiler []string, flag string) bool {
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = b.WorkDir
- cmd.Env = base.AppendPWD(os.Environ(), cmd.Dir)
- cmd.Env = append(cmd.Env, "LC_ALL=C")
+ cmd.Env = append(cmd.Environ(), "LC_ALL=C")
out, _ := cmd.CombinedOutput()
// GCC says "unrecognized command line option".
// clang says "unknown argument".
@@ -3071,7 +3072,7 @@ var (
)
func (b *Builder) swigDoVersionCheck() error {
- out, err := b.runOut(nil, "", nil, "swig", "-version")
+ out, err := b.runOut(nil, ".", nil, "swig", "-version")
if err != nil {
return err
}
diff --git a/src/cmd/go/testdata/script/list_json_fields.txt b/src/cmd/go/testdata/script/list_json_fields.txt
index 58c9efa162..9b8edc6d7f 100644
--- a/src/cmd/go/testdata/script/list_json_fields.txt
+++ b/src/cmd/go/testdata/script/list_json_fields.txt
@@ -21,6 +21,11 @@ cmp stdout want-json-name.txt
go list -json=ImportPath,Name,GoFiles,Imports
cmp stdout want-json-multiple.txt
+# Test -json= with Deps outputs the Deps field.
+go list -json=Deps
+stdout '"Deps": \['
+stdout '"errors",'
+
-- go.mod --
module example.com/a
diff --git a/src/cmd/go/testdata/script/mod_list_direct.txt b/src/cmd/go/testdata/script/mod_list_direct.txt
index 9b7a04c504..3aa1881554 100644
--- a/src/cmd/go/testdata/script/mod_list_direct.txt
+++ b/src/cmd/go/testdata/script/mod_list_direct.txt
@@ -10,7 +10,7 @@ env GOSUMDB=off
# For a while, (*modfetch.codeRepo).Stat was not checking for a go.mod file,
# which would produce a hard error at the subsequent call to GoMod.
-go get
+go get -v
-- go.mod --
module example.com
diff --git a/src/cmd/go/testdata/script/work_module_not_in_go_work.txt b/src/cmd/go/testdata/script/work_module_not_in_go_work.txt
index 23d908c302..9109b2de7f 100644
--- a/src/cmd/go/testdata/script/work_module_not_in_go_work.txt
+++ b/src/cmd/go/testdata/script/work_module_not_in_go_work.txt
@@ -6,8 +6,8 @@
! go list ./...
stderr 'pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies'
-! go list ./a
-stderr 'directory a outside modules listed in go.work'
+! go list ./a/c
+stderr 'directory a[\\/]c is contained in a module that is not one of the workspace modules listed in go.work. You can add the module to the workspace using go work use a'
-- go.work --
go 1.18
@@ -19,6 +19,8 @@ module example.com/a
go 1.18
-- a/a.go --
package a
+-- a/c/c.go --
+package c
-- b/go.mod --
module example.com/b
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 5fa883fb56..9b639bd996 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -76,6 +76,11 @@ func initParserMode() {
if *allErrors {
parserMode |= parser.AllErrors
}
+ // Both -r and -s make use of go/ast's object resolution.
+ // If neither is being used, avoid that unnecessary work.
+ if *rewriteRule == "" && !*simplifyAST {
+ parserMode |= parser.SkipObjectResolution
+ }
}
func isGoFile(f fs.DirEntry) bool {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index c980a7cf2c..2f7ce061d4 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -726,11 +726,13 @@ func genFuncInfoSyms(ctxt *Link) {
}
o.Write(&b)
+ p := b.Bytes()
isym := &LSym{
Type: objabi.SDATA, // for now, I don't think it matters
PkgIdx: goobj.PkgIdxSelf,
SymIdx: symidx,
- P: append([]byte(nil), b.Bytes()...),
+ P: append([]byte(nil), p...),
+ Size: int64(len(p)),
}
isym.Set(AttrIndexed, true)
symidx++
diff --git a/src/cmd/link/internal/ld/stackcheck.go b/src/cmd/link/internal/ld/stackcheck.go
index 520e4d67b5..f0e1367068 100644
--- a/src/cmd/link/internal/ld/stackcheck.go
+++ b/src/cmd/link/internal/ld/stackcheck.go
@@ -101,7 +101,7 @@ func (ctxt *Link) doStackCheck() {
// the same function multiple times at different
// depths, but lets us find all paths.
for _, root := range roots {
- ctxt.Errorf(root, "nosplit stack overflow")
+ ctxt.Errorf(root, "nosplit stack over %d byte limit", limit)
chain := []stackCheckChain{{stackCheckEdge{0, root}, false}}
sc.report(root, limit, &chain)
}
diff --git a/src/cmd/link/internal/ld/stackcheck_test.go b/src/cmd/link/internal/ld/stackcheck_test.go
index 21dbf2b3fd..2089badbe9 100644
--- a/src/cmd/link/internal/ld/stackcheck_test.go
+++ b/src/cmd/link/internal/ld/stackcheck_test.go
@@ -5,13 +5,12 @@
package ld
import (
- "cmd/internal/objabi"
- "cmd/internal/sys"
"fmt"
"internal/testenv"
"os"
"os/exec"
"regexp"
+ "strconv"
"testing"
)
@@ -24,7 +23,7 @@ func TestStackCheckOutput(t *testing.T) {
cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
// The rules for computing frame sizes on all of the
// architectures are complicated, so just do this on amd64.
- cmd.Env = append(os.Environ(), "GOARCH=amd64")
+ cmd.Env = append(os.Environ(), "GOARCH=amd64", "GOOS=linux")
outB, err := cmd.CombinedOutput()
if err == nil {
@@ -34,13 +33,13 @@ func TestStackCheckOutput(t *testing.T) {
t.Logf("linker output:\n%s", out)
- // Construct expected stanzas
- arch := sys.ArchAMD64
- call := 0
- if !arch.HasLR {
- call = arch.RegSize
+ // Get expected limit.
+ limitRe := regexp.MustCompile("nosplit stack over ([0-9]+) byte limit")
+ m := limitRe.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("no overflow errors in output")
}
- limit := objabi.StackLimit - call
+ limit, _ := strconv.Atoi(m[1])
wantMap := map[string]string{
"main.startSelf": fmt.Sprintf(
@@ -67,7 +66,7 @@ func TestStackCheckOutput(t *testing.T) {
}
// Parse stanzas
- stanza := regexp.MustCompile(`^(.*): nosplit stack overflow\n(.*\n(?: .*\n)*)`)
+ stanza := regexp.MustCompile(`^(.*): nosplit stack over [0-9]+ byte limit\n(.*\n(?: .*\n)*)`)
// Strip comments from cmd/go
out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
for len(out) > 0 {
diff --git a/misc/trace/README.md b/src/cmd/trace/static/README.md
similarity index 97%
rename from misc/trace/README.md
rename to src/cmd/trace/static/README.md
index 218d728546..f81c59eae5 100644
--- a/misc/trace/README.md
+++ b/src/cmd/trace/static/README.md
@@ -17,7 +17,7 @@ The file was generated by catapult's `vulcanize_trace_viewer` command.
$ git clone https://chromium.googlesource.com/catapult
$ cd catapult
$ ./tracing/bin/vulcanize_trace_viewer --config=full
-$ cp tracing/bin/trace_viewer_full.html $GOROOT/misc/trace/trace_viewer_full.html
+$ cp tracing/bin/trace_viewer_full.html $GOROOT/src/cmd/trace/static/trace_viewer_full.html
```
We are supposed to use --config=lean (produces smaller html),
@@ -31,7 +31,7 @@ to import the `trace_viewer_full.html`.
This is copied from the catapult repo.
```
-$ cp third_party/polymer/components/webcomponentsjs/webcomponents.min.js $GOROOT/misc/trace/webcomponents.min.js
+$ cp third_party/polymer/components/webcomponentsjs/webcomponents.min.js $GOROOT/src/cmd/trace/static/webcomponents.min.js
```
## Licenses
diff --git a/misc/trace/trace_viewer_full.html b/src/cmd/trace/static/trace_viewer_full.html
similarity index 100%
rename from misc/trace/trace_viewer_full.html
rename to src/cmd/trace/static/trace_viewer_full.html
diff --git a/misc/trace/webcomponents.min.js b/src/cmd/trace/static/webcomponents.min.js
similarity index 100%
rename from misc/trace/webcomponents.min.js
rename to src/cmd/trace/static/webcomponents.min.js
diff --git a/src/cmd/trace/trace.go b/src/cmd/trace/trace.go
index 0139639dae..a0d742ac54 100644
--- a/src/cmd/trace/trace.go
+++ b/src/cmd/trace/trace.go
@@ -6,6 +6,7 @@ package main
import (
"cmd/internal/traceviewer"
+ "embed"
"encoding/json"
"fmt"
"internal/trace"
@@ -13,8 +14,6 @@ import (
"log"
"math"
"net/http"
- "path/filepath"
- "runtime"
"runtime/debug"
"sort"
"strconv"
@@ -22,13 +21,16 @@ import (
"time"
)
+//go:embed static/trace_viewer_full.html static/webcomponents.min.js
+var staticContent embed.FS
+
func init() {
http.HandleFunc("/trace", httpTrace)
http.HandleFunc("/jsontrace", httpJsonTrace)
- http.HandleFunc("/trace_viewer_html", httpTraceViewerHTML)
- http.HandleFunc("/webcomponents.min.js", webcomponentsJS)
+ http.Handle("/static/", http.FileServer(http.FS(staticContent)))
}
+
// httpTrace serves either whole trace (goid==0) or trace for goid goroutine.
func httpTrace(w http.ResponseWriter, r *http.Request) {
_, err := parseTrace()
@@ -50,19 +52,19 @@ func httpTrace(w http.ResponseWriter, r *http.Request) {
var templTrace = `
-
+
-