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

+
crypto/tls
+
+

+ The tls10default GODEBUG option has been + removed. It is still possible to enable TLS 1.0 client-side by setting + Config.MinVersion. +

+
+
+
image/draw

@@ -132,6 +142,21 @@ Do not send CLs removing the interior tags from such phrases.

+
os/exec
+
+

+ An exec.Cmd with a non-empty Dir and a + nil Env now implicitly sets the PWD environment + variable for the subprocess to match Dir. +

+

+ The new method (*exec.Cmd).Environ reports the + environment that would be used to run the command, including the + aforementioned PWD variable. +

+
+
+
runtime

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 = ` - + -